Files
gemini-nexus/src/server/logging/api-logger.ts
T
Gemini Nexus Bot 27da62afc9 feat(logging): add comprehensive API request/response logging with structured winston logs
- Add dedicated API logger (api-logger.ts) with winston daily rotation
- Create Fastify plugin (api-logging.plugin.ts) to intercept all API requests/responses
- Log request start, body, response, errors, and summary with precise timestamps
- Auto-mask sensitive data using existing sanitizeObject utility
- Register API logging plugin in NestJS bootstrap (server/main.ts)
- Add npm run dev script with cross-env for automatic DEBUG on Windows
- Install cross-env as devDependency for cross-platform env vars
2026-04-22 00:07:30 -03:00

95 lines
2.5 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import winston from 'winston';
import DailyRotateFile from 'winston-daily-rotate-file';
import { getAgentDir } from '../../utils/paths';
import { sanitizeObject } from '../../utils/sensitiveDataMasking';
const LOG_RETENTION = '30d';
const LOG_MAX_SIZE = '50m';
const agentDir = getAgentDir();
if (!fs.existsSync(agentDir)) {
try {
fs.mkdirSync(agentDir, { recursive: true });
} catch (e) {
console.error('Failed to create agent directory for API logs', e);
}
}
const fileFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
const logEntry = {
timestamp,
level,
message,
...meta,
};
return JSON.stringify(logEntry);
}),
);
const consoleFormat = winston.format.combine(
winston.format.colorize({ all: true }),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
const logEntry = {
timestamp,
level,
message,
...meta,
};
return JSON.stringify(logEntry);
}),
);
const rotateTransport = new DailyRotateFile({
filename: path.join(agentDir, 'api-%DATE%.log'),
datePattern: 'YYYY-MM-DD-HH',
maxSize: LOG_MAX_SIZE,
maxFiles: LOG_RETENTION,
zippedArchive: false,
auditFile: path.join(agentDir, '.api-log-audit.json'),
level: 'debug',
format: fileFormat,
});
rotateTransport.on('error', (error) => {
console.error('DailyRotateFile API transport error', error);
});
const consoleTransport = new winston.transports.Console({
level: 'debug',
format: consoleFormat,
});
consoleTransport.on('error', (error) => {
console.error('Console API transport error', error);
});
export const apiLogger = winston.createLogger({
level: 'debug',
transports: [consoleTransport, rotateTransport],
exitOnError: false,
});
/**
* Safely serialize a value for API logging.
* Truncates very large bodies to avoid excessive log sizes.
*/
export function safeApiLogValue(value: unknown, maxLength = 50_000): unknown {
const sanitized = sanitizeObject(value);
if (typeof sanitized === 'string' && sanitized.length > maxLength) {
return sanitized.substring(0, maxLength) + `... [truncated ${sanitized.length - maxLength} chars]`;
}
const stringified = JSON.stringify(sanitized);
if (stringified.length > maxLength) {
return stringified.substring(0, maxLength) + `... [truncated ${stringified.length - maxLength} chars]`;
}
return sanitized;
}