Eve-Relay/src/utils/logs.ts
2025-06-04 12:43:23 +02:00

140 lines
3.5 KiB
TypeScript

import type { Database } from '@db/sqlite';
import * as colors from 'jsr:@std/fmt@^1.0.4/colors';
import * as log from 'jsr:@std/log';
import { DatabaseHandler } from './databaseLogger.ts';
import { getEveFilePath } from './files.ts';
export * as log from 'jsr:@std/log';
/**
* Sanitizes data before logging to prevent accidental exposure of sensitive information
* @param data The data to sanitize
* @returns Sanitized data safe for logging
*/
function sanitizeForLogging(data: unknown): unknown {
if (data === null || data === undefined || typeof data !== 'object') {
return data;
}
if (data instanceof Uint8Array) {
// Never log raw binary data that could contain keys
return `[Uint8Array length=${data.length}]`;
}
if (Array.isArray(data)) {
return data.map(sanitizeForLogging);
}
const sanitized: Record<string, unknown> = {};
const sensitiveKeys = [
'privatekey',
'private_key',
'privkey',
'priv_key',
'secretkey',
'secret_key',
'seckey',
'sec_key',
'password',
'pass',
'pwd',
'token',
'auth',
'ccnprivatekey',
'ccn_private_key',
'ccnprivkey',
'seed',
'seedphrase',
'seed_phrase',
'mnemonic',
'mnemonic_phrase',
'mnemonic_phrase_words',
];
for (const [key, value] of Object.entries(data as Record<string, unknown>)) {
const lowerKey = key.toLowerCase();
if (sensitiveKeys.some((sensitiveKey) => lowerKey.includes(sensitiveKey))) {
sanitized[key] = '[REDACTED]';
} else {
sanitized[key] = sanitizeForLogging(value);
}
}
return sanitized;
}
export async function setupLogger(db: Database | null) {
const formatLevel = (level: number): string => {
return (
{
10: colors.gray('[DEBUG]'),
20: colors.green('[INFO] '),
30: colors.yellow('[WARN] '),
40: colors.red('[ERROR]'),
50: colors.bgRed('[FATAL]'),
}[level] || `[LVL${level}]`
);
};
const levelName = (level: number): string => {
return (
{
10: 'DEBUG',
20: 'INFO',
30: 'WARN',
40: 'ERROR',
50: 'FATAL',
}[level] || `LVL${level}`
);
};
const formatArg = (arg: unknown): string => {
const sanitized = sanitizeForLogging(arg);
if (typeof sanitized === 'object') return JSON.stringify(sanitized);
return String(sanitized);
};
const handlers: Record<string, log.BaseHandler> = {
console: new log.ConsoleHandler('DEBUG', {
useColors: true,
formatter: (record) => {
const timestamp = new Date().toISOString();
let msg = `${colors.dim(`[${timestamp}]`)} ${formatLevel(record.level)} ${record.msg}`;
if (record.args.length > 0) {
const args = record.args
.map((arg, i) => `${colors.dim(`arg${i}:`)} ${formatArg(arg)}`)
.join(' ');
msg += ` ${colors.dim('|')} ${args}`;
}
return msg;
},
}),
file: new log.FileHandler('DEBUG', {
filename:
Deno.env.get('LOG_FILE') || (await getEveFilePath('eve-logs.jsonl')),
formatter: (record) => {
const timestamp = new Date().toISOString();
return JSON.stringify({
timestamp,
level: levelName(record.level),
msg: record.msg,
args: record.args.map(sanitizeForLogging),
});
},
}),
};
if (db) {
handlers.database = new DatabaseHandler('DEBUG', { db });
}
log.setup({
handlers,
loggers: {
default: {
level: 'DEBUG',
handlers: ['console', 'file', 'database'],
},
},
});
}