done
This commit is contained in:
parent
caf1c5fcaf
commit
20b7180a43
5 changed files with 207 additions and 158 deletions
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
import pino from 'pino';
|
||||
import pretty from 'pino-pretty';
|
||||
import type { LogContext, LoggerConfig, LogLevel, LogMetadata } from './types';
|
||||
|
||||
// Simple cache for logger instances
|
||||
|
|
@ -35,68 +36,73 @@ export function setLoggerConfig(config: LoggerConfig): void {
|
|||
console.log('Logger config updated:', globalConfig.logLevel);
|
||||
}
|
||||
/**
|
||||
* Create transport configuration
|
||||
* Create logger destination using multistream approach:
|
||||
* - Console: In-process pretty stream (fast shutdown, disabled in production)
|
||||
* - File/Loki: Worker transports (default timeout, ok to wait)
|
||||
*/
|
||||
function createTransports(serviceName: string, config: LoggerConfig = globalConfig): any {
|
||||
const targets: any[] = [];
|
||||
function createDestination(
|
||||
serviceName: string,
|
||||
config: LoggerConfig = globalConfig
|
||||
): pino.DestinationStream | null {
|
||||
const streams: pino.StreamEntry[] = [];
|
||||
|
||||
// Console transport
|
||||
if (config.logConsole) {
|
||||
targets.push({
|
||||
target: 'pino-pretty',
|
||||
level: config.logLevel || 'info',
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: 'yyyy-mm-dd HH:MM:ss.l',
|
||||
messageFormat: '[{service}{childName}] {msg}',
|
||||
singleLine: true,
|
||||
hideObject: false,
|
||||
ignore: 'pid,hostname,service,environment,version,childName',
|
||||
errorLikeObjectKeys: ['err', 'error'],
|
||||
errorProps: 'message,stack,name,code',
|
||||
// Ensure the transport respects the configured level
|
||||
minimumLevel: config.logLevel || 'info',
|
||||
},
|
||||
// Console: In-process pretty stream for dev (fast shutdown)
|
||||
if (config.logConsole && config.environment !== 'production') {
|
||||
const prettyStream = pretty({
|
||||
sync: true,
|
||||
colorize: true,
|
||||
translateTime: 'yyyy-mm-dd HH:MM:ss.l',
|
||||
messageFormat: '[{service}{childName}] {msg}',
|
||||
singleLine: true,
|
||||
hideObject: false,
|
||||
ignore: 'pid,hostname,service,environment,version,childName',
|
||||
errorLikeObjectKeys: ['err', 'error'],
|
||||
errorProps: 'message,stack,name,code',
|
||||
});
|
||||
streams.push({ stream: prettyStream });
|
||||
}
|
||||
|
||||
// File transport
|
||||
// File: Worker transport (has timeout but acceptable)
|
||||
if (config.logFile) {
|
||||
targets.push({
|
||||
target: 'pino/file',
|
||||
level: config.logLevel || 'info',
|
||||
options: {
|
||||
destination: `${config.logFilePath}/${serviceName}.log`,
|
||||
mkdir: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Loki transport
|
||||
if (config.logLoki && config.lokiHost) {
|
||||
targets.push({
|
||||
target: 'pino-loki',
|
||||
level: config.logLevel || 'info',
|
||||
options: {
|
||||
host: config.lokiHost,
|
||||
labels: {
|
||||
service: serviceName,
|
||||
environment: config.environment || 'development',
|
||||
streams.push(
|
||||
pino.transport({
|
||||
target: 'pino/file',
|
||||
level: config.logLevel || 'info',
|
||||
options: {
|
||||
destination: `${config.logFilePath}/${serviceName}.log`,
|
||||
mkdir: true,
|
||||
},
|
||||
ignore: 'childName',
|
||||
...(config.lokiUser && config.lokiPassword
|
||||
? {
|
||||
basicAuth: {
|
||||
username: config.lokiUser,
|
||||
password: config.lokiPassword,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return targets.length > 0 ? { targets } : null;
|
||||
// Loki: Worker transport (has timeout but acceptable)
|
||||
if (config.logLoki && config.lokiHost) {
|
||||
streams.push(
|
||||
pino.transport({
|
||||
target: 'pino-loki',
|
||||
level: config.logLevel || 'info',
|
||||
options: {
|
||||
host: config.lokiHost,
|
||||
labels: {
|
||||
service: serviceName,
|
||||
environment: config.environment || 'development',
|
||||
},
|
||||
ignore: 'childName',
|
||||
...(config.lokiUser && config.lokiPassword
|
||||
? {
|
||||
basicAuth: {
|
||||
username: config.lokiUser,
|
||||
password: config.lokiPassword,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return streams.length > 0 ? pino.multistream(streams) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,7 +111,7 @@ function createTransports(serviceName: string, config: LoggerConfig = globalConf
|
|||
function getPinoLogger(serviceName: string, config: LoggerConfig = globalConfig): pino.Logger {
|
||||
const cacheKey = `${serviceName}-${JSON.stringify(config)}`;
|
||||
if (!loggerCache.has(cacheKey)) {
|
||||
const transport = createTransports(serviceName, config);
|
||||
const destination = createDestination(serviceName, config);
|
||||
|
||||
const loggerOptions: pino.LoggerOptions = {
|
||||
level: config.logLevel || 'info',
|
||||
|
|
@ -116,11 +122,8 @@ function getPinoLogger(serviceName: string, config: LoggerConfig = globalConfig)
|
|||
},
|
||||
};
|
||||
|
||||
if (transport) {
|
||||
loggerOptions.transport = transport;
|
||||
}
|
||||
const logger = destination ? pino(loggerOptions, destination) : pino(loggerOptions);
|
||||
|
||||
const logger = pino(loggerOptions);
|
||||
loggerCache.set(cacheKey, logger);
|
||||
}
|
||||
|
||||
|
|
@ -356,5 +359,35 @@ export async function shutdownLoggers(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Graceful shutdown - flush all logger transports quickly
|
||||
* Use this in your application shutdown handlers
|
||||
*/
|
||||
export async function gracefulShutdown(): Promise<void> {
|
||||
const flushPromises: Promise<void>[] = [];
|
||||
|
||||
for (const logger of loggerCache.values()) {
|
||||
// Use pino v9's flush() method - this is much faster than the complex shutdown
|
||||
flushPromises.push(
|
||||
new Promise<void>((resolve, reject) => {
|
||||
logger.flush((err?: Error) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(flushPromises);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Logger graceful shutdown failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Export types for convenience
|
||||
export type { LogContext, LogLevel, LogMetadata } from './types';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue