131 lines
3.1 KiB
TypeScript
131 lines
3.1 KiB
TypeScript
import type { ILoggingConfig } from '../index.js';
|
|
|
|
/**
|
|
* Log levels in order of severity
|
|
*/
|
|
const LOG_LEVELS = {
|
|
error: 0,
|
|
warn: 1,
|
|
info: 2,
|
|
debug: 3,
|
|
} as const;
|
|
|
|
type LogLevel = keyof typeof LOG_LEVELS;
|
|
|
|
/**
|
|
* Structured logger with configurable levels and formats
|
|
*/
|
|
export class Logger {
|
|
private config: Required<ILoggingConfig>;
|
|
private minLevel: number;
|
|
|
|
constructor(config: ILoggingConfig) {
|
|
// Apply defaults for any missing config
|
|
this.config = {
|
|
level: config.level ?? 'info',
|
|
format: config.format ?? 'text',
|
|
enabled: config.enabled ?? true,
|
|
};
|
|
this.minLevel = LOG_LEVELS[this.config.level];
|
|
}
|
|
|
|
/**
|
|
* Check if a log level should be output
|
|
*/
|
|
private shouldLog(level: LogLevel): boolean {
|
|
if (!this.config.enabled) {
|
|
return false;
|
|
}
|
|
return LOG_LEVELS[level] <= this.minLevel;
|
|
}
|
|
|
|
/**
|
|
* Format a log message
|
|
*/
|
|
private format(level: LogLevel, message: string, meta?: Record<string, any>): string {
|
|
const timestamp = new Date().toISOString();
|
|
|
|
if (this.config.format === 'json') {
|
|
return JSON.stringify({
|
|
timestamp,
|
|
level,
|
|
message,
|
|
...(meta || {}),
|
|
});
|
|
}
|
|
|
|
// Text format
|
|
const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';
|
|
return `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`;
|
|
}
|
|
|
|
/**
|
|
* Log at error level
|
|
*/
|
|
public error(message: string, meta?: Record<string, any>): void {
|
|
if (this.shouldLog('error')) {
|
|
console.error(this.format('error', message, meta));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log at warn level
|
|
*/
|
|
public warn(message: string, meta?: Record<string, any>): void {
|
|
if (this.shouldLog('warn')) {
|
|
console.warn(this.format('warn', message, meta));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log at info level
|
|
*/
|
|
public info(message: string, meta?: Record<string, any>): void {
|
|
if (this.shouldLog('info')) {
|
|
console.log(this.format('info', message, meta));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log at debug level
|
|
*/
|
|
public debug(message: string, meta?: Record<string, any>): void {
|
|
if (this.shouldLog('debug')) {
|
|
console.log(this.format('debug', message, meta));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log HTTP request
|
|
*/
|
|
public request(method: string, url: string, meta?: Record<string, any>): void {
|
|
this.info(`→ ${method} ${url}`, meta);
|
|
}
|
|
|
|
/**
|
|
* Log HTTP response
|
|
*/
|
|
public response(method: string, url: string, statusCode: number, duration: number): void {
|
|
const level: LogLevel = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'info';
|
|
|
|
if (this.shouldLog(level)) {
|
|
const message = `← ${method} ${url} - ${statusCode} (${duration}ms)`;
|
|
|
|
if (level === 'error') {
|
|
this.error(message, { statusCode, duration });
|
|
} else if (level === 'warn') {
|
|
this.warn(message, { statusCode, duration });
|
|
} else {
|
|
this.info(message, { statusCode, duration });
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log S3 error
|
|
*/
|
|
public s3Error(code: string, message: string, status: number): void {
|
|
this.error(`[S3Error] ${code}: ${message}`, { code, status });
|
|
}
|
|
}
|