61f5f76ef0
- Add circuit breaker configuration options to SocketConfig type - Initialize circuit breakers for query, connection, and pre-key operations - Wrap query() with circuit breaker protection for all WhatsApp queries - Wrap sendRawMessage() with connection circuit breaker - Integrate pre-key circuit breaker with uploadPreKeys() - Add circuit breaker utilities to socket return object (stats, reset) - Clean up circuit breakers on connection close - Fix TypeScript errors in utility files (prometheus-metrics, logger-adapter, etc.) Circuit breakers provide: - Sliding window failure tracking - Automatic state transitions (closed -> open -> half-open) - Configurable thresholds and timeouts - Prometheus metrics integration - Event callbacks for monitoring
378 lines
9.5 KiB
TypeScript
378 lines
9.5 KiB
TypeScript
/**
|
|
* @fileoverview Adaptador entre diferentes sistemas de logging
|
|
* @module Utils/logger-adapter
|
|
*
|
|
* Fornece:
|
|
* - Adapter pattern para integrar diferentes loggers
|
|
* - Mapeamento de níveis de log entre sistemas
|
|
* - Transformação de formatos de log
|
|
* - Compatibilidade com Pino, Console e StructuredLogger
|
|
*/
|
|
|
|
import type { ILogger } from './logger.js'
|
|
import type P from 'pino'
|
|
import { StructuredLogger, createStructuredLogger, type LogLevel, LOG_LEVEL_VALUES } from './structured-logger.js'
|
|
|
|
/**
|
|
* Tipo de logger suportado
|
|
*/
|
|
export type LoggerType = 'pino' | 'console' | 'structured' | 'custom'
|
|
|
|
/**
|
|
* Configuração do adapter
|
|
*/
|
|
export interface LoggerAdapterConfig {
|
|
/** Tipo de logger de origem */
|
|
sourceType: LoggerType
|
|
/** Tipo de logger de destino */
|
|
targetType: LoggerType
|
|
/** Mapeamento customizado de níveis */
|
|
levelMapping?: Record<string, LogLevel>
|
|
/** Transformador de contexto */
|
|
contextTransformer?: (context: Record<string, unknown>) => Record<string, unknown>
|
|
/** Filtro de logs */
|
|
logFilter?: (level: LogLevel, message: string, data?: unknown) => boolean
|
|
}
|
|
|
|
/**
|
|
* Mapeamento padrão de níveis Pino para StructuredLogger
|
|
*/
|
|
const PINO_LEVEL_MAPPING: Record<number, LogLevel> = {
|
|
10: 'trace',
|
|
20: 'debug',
|
|
30: 'info',
|
|
40: 'warn',
|
|
50: 'error',
|
|
60: 'fatal',
|
|
}
|
|
|
|
/**
|
|
* Mapeamento reverso para Pino
|
|
*/
|
|
const STRUCTURED_TO_PINO_LEVEL: Record<LogLevel, number> = {
|
|
trace: 10,
|
|
debug: 20,
|
|
info: 30,
|
|
warn: 40,
|
|
error: 50,
|
|
fatal: 60,
|
|
silent: 100,
|
|
}
|
|
|
|
/**
|
|
* Classe adaptadora principal
|
|
*/
|
|
export class LoggerAdapter implements ILogger {
|
|
private sourceLogger: ILogger
|
|
private targetLogger: ILogger | null = null
|
|
private config: LoggerAdapterConfig
|
|
|
|
constructor(sourceLogger: ILogger, config: Partial<LoggerAdapterConfig> = {}) {
|
|
this.sourceLogger = sourceLogger
|
|
this.config = {
|
|
sourceType: config.sourceType || 'pino',
|
|
targetType: config.targetType || 'structured',
|
|
levelMapping: config.levelMapping,
|
|
contextTransformer: config.contextTransformer,
|
|
logFilter: config.logFilter,
|
|
}
|
|
}
|
|
|
|
get level(): string {
|
|
return this.sourceLogger.level
|
|
}
|
|
|
|
set level(newLevel: string) {
|
|
this.sourceLogger.level = newLevel
|
|
if (this.targetLogger) {
|
|
this.targetLogger.level = newLevel
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Define o logger de destino
|
|
*/
|
|
setTargetLogger(logger: ILogger): void {
|
|
this.targetLogger = logger
|
|
}
|
|
|
|
/**
|
|
* Cria um logger filho
|
|
*/
|
|
child(obj: Record<string, unknown>): LoggerAdapter {
|
|
const transformedContext = this.config.contextTransformer ? this.config.contextTransformer(obj) : obj
|
|
|
|
const childAdapter = new LoggerAdapter(this.sourceLogger.child(transformedContext), this.config)
|
|
|
|
if (this.targetLogger) {
|
|
childAdapter.setTargetLogger(this.targetLogger.child(transformedContext))
|
|
}
|
|
|
|
return childAdapter
|
|
}
|
|
|
|
/**
|
|
* Mapeia nível de log
|
|
*/
|
|
private mapLevel(level: string | number): LogLevel {
|
|
if (typeof level === 'number') {
|
|
return PINO_LEVEL_MAPPING[level] || 'info'
|
|
}
|
|
|
|
if (this.config.levelMapping && level in this.config.levelMapping) {
|
|
return this.config.levelMapping[level] ?? 'info'
|
|
}
|
|
|
|
return (level as LogLevel) || 'info'
|
|
}
|
|
|
|
/**
|
|
* Verifica se o log deve ser processado
|
|
*/
|
|
private shouldLog(level: LogLevel, msg: string, obj?: unknown): boolean {
|
|
if (this.config.logFilter) {
|
|
return this.config.logFilter(level, msg, obj)
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Processa log em ambos loggers
|
|
*/
|
|
private processLog(level: LogLevel, obj: unknown, msg?: string): void {
|
|
if (!this.shouldLog(level, msg || '', obj)) {
|
|
return
|
|
}
|
|
|
|
// Log no source logger
|
|
const sourceMethod = this.sourceLogger[level as keyof ILogger]
|
|
if (typeof sourceMethod === 'function') {
|
|
;(sourceMethod as (obj: unknown, msg?: string) => void).call(this.sourceLogger, obj, msg)
|
|
}
|
|
|
|
// Log no target logger se configurado
|
|
if (this.targetLogger) {
|
|
const targetMethod = this.targetLogger[level as keyof ILogger]
|
|
if (typeof targetMethod === 'function') {
|
|
;(targetMethod as (obj: unknown, msg?: string) => void).call(this.targetLogger, obj, msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
trace(obj: unknown, msg?: string): void {
|
|
this.processLog('trace', obj, msg)
|
|
}
|
|
|
|
debug(obj: unknown, msg?: string): void {
|
|
this.processLog('debug', obj, msg)
|
|
}
|
|
|
|
info(obj: unknown, msg?: string): void {
|
|
this.processLog('info', obj, msg)
|
|
}
|
|
|
|
warn(obj: unknown, msg?: string): void {
|
|
this.processLog('warn', obj, msg)
|
|
}
|
|
|
|
error(obj: unknown, msg?: string): void {
|
|
this.processLog('error', obj, msg)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper para converter Pino logger em StructuredLogger
|
|
*/
|
|
export class PinoToStructuredAdapter implements ILogger {
|
|
private pinoLogger: P.Logger
|
|
private structuredLogger: StructuredLogger
|
|
|
|
constructor(pinoLogger: P.Logger, structuredLoggerConfig?: Parameters<typeof createStructuredLogger>[0]) {
|
|
this.pinoLogger = pinoLogger
|
|
this.structuredLogger = createStructuredLogger({
|
|
level: this.mapPinoLevel(pinoLogger.level),
|
|
...structuredLoggerConfig,
|
|
})
|
|
}
|
|
|
|
get level(): string {
|
|
return this.pinoLogger.level
|
|
}
|
|
|
|
set level(newLevel: string) {
|
|
this.pinoLogger.level = newLevel
|
|
this.structuredLogger.level = newLevel
|
|
}
|
|
|
|
private mapPinoLevel(pinoLevel: string): LogLevel {
|
|
const levelMap: Record<string, LogLevel> = {
|
|
trace: 'trace',
|
|
debug: 'debug',
|
|
info: 'info',
|
|
warn: 'warn',
|
|
error: 'error',
|
|
fatal: 'fatal',
|
|
silent: 'silent',
|
|
}
|
|
return levelMap[pinoLevel] || 'info'
|
|
}
|
|
|
|
child(obj: Record<string, unknown>): PinoToStructuredAdapter {
|
|
const adapter = new PinoToStructuredAdapter(this.pinoLogger.child(obj))
|
|
return adapter
|
|
}
|
|
|
|
trace(obj: unknown, msg?: string): void {
|
|
this.pinoLogger.trace(obj as object, msg)
|
|
this.structuredLogger.trace(obj, msg)
|
|
}
|
|
|
|
debug(obj: unknown, msg?: string): void {
|
|
this.pinoLogger.debug(obj as object, msg)
|
|
this.structuredLogger.debug(obj, msg)
|
|
}
|
|
|
|
info(obj: unknown, msg?: string): void {
|
|
this.pinoLogger.info(obj as object, msg)
|
|
this.structuredLogger.info(obj, msg)
|
|
}
|
|
|
|
warn(obj: unknown, msg?: string): void {
|
|
this.pinoLogger.warn(obj as object, msg)
|
|
this.structuredLogger.warn(obj, msg)
|
|
}
|
|
|
|
error(obj: unknown, msg?: string): void {
|
|
this.pinoLogger.error(obj as object, msg)
|
|
this.structuredLogger.error(obj, msg)
|
|
}
|
|
|
|
/**
|
|
* Obtém métricas do structured logger
|
|
*/
|
|
getMetrics() {
|
|
return this.structuredLogger.getMetrics()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory para criar adapter baseado no tipo de logger
|
|
*/
|
|
export function createLoggerAdapter(
|
|
logger: ILogger,
|
|
config?: Partial<LoggerAdapterConfig>
|
|
): LoggerAdapter {
|
|
return new LoggerAdapter(logger, config)
|
|
}
|
|
|
|
/**
|
|
* Converte qualquer logger para a interface ILogger
|
|
*/
|
|
export function normalizeLogger(logger: unknown): ILogger {
|
|
if (isILogger(logger)) {
|
|
return logger
|
|
}
|
|
|
|
// Se for um objeto com métodos de log
|
|
if (typeof logger === 'object' && logger !== null) {
|
|
const logObj = logger as Record<string, unknown>
|
|
|
|
return {
|
|
level: (logObj.level as string) || 'info',
|
|
child: (obj: Record<string, unknown>) => {
|
|
if (typeof logObj.child === 'function') {
|
|
return normalizeLogger((logObj.child as (obj: Record<string, unknown>) => unknown)(obj))
|
|
}
|
|
return normalizeLogger(logger)
|
|
},
|
|
trace: createLogMethod(logObj, 'trace'),
|
|
debug: createLogMethod(logObj, 'debug'),
|
|
info: createLogMethod(logObj, 'info'),
|
|
warn: createLogMethod(logObj, 'warn'),
|
|
error: createLogMethod(logObj, 'error'),
|
|
}
|
|
}
|
|
|
|
// Fallback: console logger
|
|
return createConsoleLogger()
|
|
}
|
|
|
|
/**
|
|
* Verifica se objeto implementa ILogger
|
|
*/
|
|
export function isILogger(obj: unknown): obj is ILogger {
|
|
if (typeof obj !== 'object' || obj === null) {
|
|
return false
|
|
}
|
|
|
|
const logger = obj as Record<string, unknown>
|
|
|
|
return (
|
|
typeof logger.child === 'function' &&
|
|
typeof logger.trace === 'function' &&
|
|
typeof logger.debug === 'function' &&
|
|
typeof logger.info === 'function' &&
|
|
typeof logger.warn === 'function' &&
|
|
typeof logger.error === 'function'
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Cria método de log genérico
|
|
*/
|
|
function createLogMethod(
|
|
logger: Record<string, unknown>,
|
|
level: string
|
|
): (obj: unknown, msg?: string) => void {
|
|
return (obj: unknown, msg?: string) => {
|
|
if (typeof logger[level] === 'function') {
|
|
;(logger[level] as (obj: unknown, msg?: string) => void)(obj, msg)
|
|
} else {
|
|
// Fallback to console methods
|
|
const consoleMethod = (console as unknown as Record<string, ((...args: unknown[]) => void) | undefined>)[level]
|
|
if (typeof consoleMethod === 'function') {
|
|
consoleMethod(obj, msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cria um logger baseado em console
|
|
*/
|
|
export function createConsoleLogger(prefix?: string): ILogger {
|
|
const formatMessage = (level: string, obj: unknown, msg?: string): string => {
|
|
const timestamp = new Date().toISOString()
|
|
const prefixStr = prefix ? `[${prefix}]` : ''
|
|
const message = msg || (typeof obj === 'string' ? obj : '')
|
|
const data = typeof obj === 'object' ? JSON.stringify(obj) : ''
|
|
|
|
return `${timestamp} ${prefixStr}[${level.toUpperCase()}] ${message} ${data}`.trim()
|
|
}
|
|
|
|
return {
|
|
level: 'info',
|
|
child(obj: Record<string, unknown>): ILogger {
|
|
const childPrefix = prefix ? `${prefix}:${Object.values(obj)[0]}` : String(Object.values(obj)[0])
|
|
return createConsoleLogger(childPrefix)
|
|
},
|
|
trace(obj: unknown, msg?: string): void {
|
|
console.debug(formatMessage('trace', obj, msg))
|
|
},
|
|
debug(obj: unknown, msg?: string): void {
|
|
console.debug(formatMessage('debug', obj, msg))
|
|
},
|
|
info(obj: unknown, msg?: string): void {
|
|
console.info(formatMessage('info', obj, msg))
|
|
},
|
|
warn(obj: unknown, msg?: string): void {
|
|
console.warn(formatMessage('warn', obj, msg))
|
|
},
|
|
error(obj: unknown, msg?: string): void {
|
|
console.error(formatMessage('error', obj, msg))
|
|
},
|
|
}
|
|
}
|
|
|
|
export default LoggerAdapter
|