refactor: migrate to winston and enable daily rotated app logs (#71)

This commit is contained in:
vin authored and GitHub committed 2026-02-06 21:10:54 +08:00
1 parent ed94abf0d6
commit 2ae2216f1e
17 files changed
+414 -92

No files matched your search

+2 -1
View File
@@ -2,6 +2,7 @@ import { v4 as uuidv4 } from 'uuid';
import { mapClaudeModelToGemini } from './ModelMapping';
import { cleanJsonSchema } from './JsonSchemaUtils';
import { SignatureStore } from './SignatureStore';
import { logger } from '../../utils/logger';
import {
ClaudeRequest,
Message,
@@ -378,7 +379,7 @@ function injectGoogleSearchTool(body: { tools?: GeminiToolDeclaration[] }) {
const hasFunctions = toolsArr.some((t) => t.functionDeclarations);
if (hasFunctions) {
console.info('Skipping googleSearch injection due to existing functionDeclarations');
logger.info('Skipping googleSearch injection due to existing functionDeclarations');
return;
}
+3 -2
View File
@@ -1,6 +1,7 @@
import { GeminiPart, Usage, UsageMetadata } from './types';
import { SignatureStore } from './SignatureStore';
import { decodeSignature } from './signature-utils';
import { logger } from '../../utils/logger';
type BlockType = 'None' | 'Text' | 'Thinking' | 'Function';
@@ -249,7 +250,7 @@ export class StreamingState {
const chunks: string[] = [];
this.parseErrorCount++;
console.warn(
logger.warn(
`[SSE-Parser] Parse error #${this.parseErrorCount}. Raw data length: ${rawData.length}`,
);
@@ -260,7 +261,7 @@ export class StreamingState {
// Emit error event if too many errors
if (this.parseErrorCount > 3) {
console.error(
logger.error(
`[SSE-Parser] High error rate (${this.parseErrorCount} errors). Stream may be corrupted.`,
);
chunks.push(
+6 -9
View File
@@ -1,7 +1,4 @@
const logger = {
log: (msg: string) => console.log(msg),
warn: (msg: string) => console.warn(msg),
};
import { logger } from '../../utils/logger';
const CLAUDE_TO_GEMINI: Record<string, string> = {
// Directly supported models
@@ -78,7 +75,7 @@ export function resolveModelRoute(
): string {
// 1. Check custom exact mapping (Highest priority)
if (customMapping[originalModel]) {
logger.log(
logger.info(
`[Router] Using custom exact mapping: ${originalModel} -> ${customMapping[originalModel]}`,
);
return customMapping[originalModel];
@@ -98,7 +95,7 @@ export function resolveModelRoute(
lowerModel === 'gpt-4'
) {
if (openaiMapping['gpt-4-series']) {
logger.log(
logger.info(
`[Router] Using GPT-4 series mapping: ${originalModel} -> ${openaiMapping['gpt-4-series']}`,
);
return openaiMapping['gpt-4-series'];
@@ -113,7 +110,7 @@ export function resolveModelRoute(
lowerModel.includes('turbo')
) {
if (openaiMapping['gpt-4o-series']) {
logger.log(
logger.info(
`[Router] Using GPT-4o/3.5 series mapping: ${originalModel} -> ${openaiMapping['gpt-4o-series']}`,
);
return openaiMapping['gpt-4o-series'];
@@ -124,13 +121,13 @@ export function resolveModelRoute(
if (lowerModel.startsWith('gpt-5')) {
// Prefer gpt-5-series mapping, fallback to gpt-4-series if missing
if (openaiMapping['gpt-5-series']) {
logger.log(
logger.info(
`[Router] Using GPT-5 series mapping: ${originalModel} -> ${openaiMapping['gpt-5-series']}`,
);
return openaiMapping['gpt-5-series'];
}
if (openaiMapping['gpt-4-series']) {
logger.log(
logger.info(
`[Router] Using GPT-4 series mapping (GPT-5 fallback): ${originalModel} -> ${openaiMapping['gpt-4-series']}`,
);
return openaiMapping['gpt-4-series'];
+10 -9
View File
@@ -1,5 +1,6 @@
import axios, { AxiosError, AxiosInstance } from 'axios';
import { QuotaData, Tier, LoadProjectResponse, QuotaApiResponse } from './types';
import { logger } from '../../utils/logger';
// Constants
const QUOTA_API_URL = 'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels';
@@ -48,15 +49,15 @@ export class QuotaService {
const subscriptionTier = data.paidTier?.id || data.currentTier?.id;
if (subscriptionTier) {
console.log(`📊 [${email}] Subscription Identified: ${subscriptionTier}`);
logger.info(`📊 [${email}] Subscription Identified: ${subscriptionTier}`);
}
return [projectId, subscriptionTier];
} else {
console.warn(`⚠️ [${email}] loadCodeAssist failed: Status: ${res.status}`);
logger.warn(`⚠️ [${email}] loadCodeAssist failed: Status: ${res.status}`);
}
} catch (error: any) {
console.error(`❌ [${email}] loadCodeAssist Network Error: ${error.message}`);
logger.error(`❌ [${email}] loadCodeAssist Network Error: ${error.message}`);
}
return [undefined, undefined];
@@ -86,7 +87,7 @@ export class QuotaService {
const url = QUOTA_API_URL;
const maxRetries = 3;
console.log(`Sending quota request to ${url}`);
logger.info(`Sending quota request to ${url}`);
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
@@ -104,11 +105,11 @@ export class QuotaService {
subscriptionTier: subscriptionTier,
};
console.log(`Quota API returned ${Object.keys(quotaResponse.models || {}).length} models:`);
logger.info(`Quota API returned ${Object.keys(quotaResponse.models || {}).length} models:`);
if (quotaResponse.models) {
for (const [name, info] of Object.entries(quotaResponse.models)) {
console.log(` - ${name}`);
logger.info(` - ${name}`);
if (info.quotaInfo) {
const fraction = info.quotaInfo.remainingFraction ?? 0;
const percentage = Math.floor(fraction * 100);
@@ -135,7 +136,7 @@ export class QuotaService {
// ✅ Handle 403 Forbidden specifically - return immediately, do not retry
if (status === 403) {
console.warn(`Account no permission (403 Forbidden), marked as forbidden`);
logger.warn(`Account no permission (403 Forbidden), marked as forbidden`);
return {
quotaData: {
models: {},
@@ -146,9 +147,9 @@ export class QuotaService {
};
}
console.warn(`API Error: ${status} - ${text} (Attempt ${attempt}/${maxRetries})`);
logger.warn(`API Error: ${status} - ${text} (Attempt ${attempt}/${maxRetries})`);
} else {
console.warn(`Request Failed: ${error.message} (Attempt ${attempt}/${maxRetries})`);
logger.warn(`Request Failed: ${error.message} (Attempt ${attempt}/${maxRetries})`);
}
if (attempt < maxRetries) {
+3 -2
View File
@@ -2,6 +2,7 @@
* Global thought_signature storage shared by all endpoints
* Used to capture and replay signatures for Gemini 3+ function calls when clients don't pass them back.
*/
import { logger } from '../../utils/logger';
class SignatureStoreImpl {
private static instance: SignatureStoreImpl;
@@ -28,12 +29,12 @@ class SignatureStoreImpl {
const newLen = sig.length;
if (newLen > existingLen) {
console.log(
logger.info(
`[ThoughtSig] Storing new signature (length: ${newLen}, replacing old: ${existingLen})`,
);
this.signature = sig;
} else {
console.debug(
logger.debug(
`[ThoughtSig] Skipping shorter signature (new length: ${newLen}, existing: ${existingLen})`,
);
}
+3 -2
View File
@@ -5,6 +5,7 @@
* Problem: Invalid signatures cause thinking blocks to be dropped, losing content
* Solution: Validate and convert invalid blocks to text instead of dropping
*/
import { logger } from '../../utils/logger';
/** Minimum length for a valid thought signature */
const MIN_SIGNATURE_LENGTH = 10;
@@ -82,7 +83,7 @@ export function gracefulDegradeThinking(block: ContentBlock): ContentBlock | nul
// Invalid signature - check if we have content to preserve
if (thinkingBlock.thinking && thinkingBlock.thinking.length > 0) {
// Convert to text block to preserve content
console.info(
logger.info(
`[Thinking-Utils] Converting invalid thinking block to text. Content length: ${thinkingBlock.thinking.length}`,
);
return {
@@ -92,7 +93,7 @@ export function gracefulDegradeThinking(block: ContentBlock): ContentBlock | nul
}
// Empty content + invalid signature = drop
console.debug('[Thinking-Utils] Dropping empty thinking block with invalid signature');
logger.debug('[Thinking-Utils] Dropping empty thinking block with invalid signature');
return null;
}