import path from 'path'; import pino from 'pino'; import { genTraceId, getTraceId, runInTrace } from '../trace'; declare var __isNodeSingleFileBundle: any; declare var __isVite: any; // https://github.com/pinojs/pino/blob/master/docs/api.md function getPinoConfig() { const targets: any = []; const rotate = `./rotate${path.extname(__filename)}`; targets.push({ level: 'debug', target: rotate, options: { path: './logs/' }, }); if ( typeof __isNodeSingleFileBundle !== 'undefined' || typeof __isVite !== 'undefined' || // remix (global as any)?.__remix_devServerHooks || // remix process.env.NODE_ENV !== 'development' ) { // 打包单文件的时候自动拷贝worker (globalThis as any).__bundlerPathsOverrides = { 'pino-pretty': path.join(__dirname, './worker/pino-pretty.cjs'), 'pino-worker': path.join(__dirname, './worker/pino-worker.cjs'), [rotate]: path.join(__dirname, './worker/pino-rotate.cjs'), 'pino/file': path.join(__dirname, './worker/pino-file.cjs'), 'thread-stream-worker': path.join(__dirname, './worker/thread-stream.cjs'), ...(globalThis as any).__bundlerPathsOverrides, }; } if (process.env.NODE_ENV === 'production') { } else { targets.push({ level: 'debug', target: 'pino-pretty', // must be installed separately, options: { // https://github.com/pinojs/pino-pretty?tab=readme-ov-file#options ignore: 'pid', messageKey: 'message', }, }); } const config: pino.LoggerOptions = { transport: { targets }, mixin(data: any) { // {"level":30,"time":1726065716643,"pid":21968,"hostname":"Win-LH"} // 基础字段不能重复 删除掉 delete data.level; delete data.time; delete data.pid; delete data.name; delete data.hostname; if (!data?.traceId) { return { traceId: getTraceId() }; } return {}; }, }; return config; } export function createLogger() { const logger = pino({ // https://github.com/pinojs/pino/blob/main/docs/api.md#opt-serializers base: { pid: process.pid }, messageKey: 'message', errorKey: 'error', timestamp: pino.stdTimeFunctions.epochTime, // timestamp: process.env.NODE_ENV === 'production' ? pino.stdTimeFunctions.epochTime : pino.stdTimeFunctions.isoTime, ...getPinoConfig(), }); return { logger }; } import type { SerializedRequest, SerializedResponse } from 'pino-std-serializers'; export type ExpressFormat = (info: ExpressInfo) => any; export interface ExpressInfo { duration: number; request: SerializedRequest; response: SerializedResponse; } export function useExpress(logger: any, format?: ExpressFormat) { const serializers = require('pino-std-serializers') as typeof import('pino-std-serializers'); const mapHttpRequest = serializers.mapHttpRequest; const mapHttpResponse = serializers.mapHttpResponse || (serializers as any).default.mapHttpResponse; const startTime = Symbol(); return (req: any, res: any, next: any) => { const response: any = res; response[startTime] = response[startTime] || Date.now(); const traceId = req.headers['trace-id'] || genTraceId(); const onComplete = () => { res.removeListener('close', onComplete); res.removeListener('finish', onComplete); res.removeListener('error', onComplete); const duration = Date.now() - response[startTime]; const serializedRequest = mapHttpRequest(req).req; const serializedResponse = mapHttpResponse(res).res; const msg = `${serializedRequest.method} ${serializedRequest.url}`; const info: ExpressInfo = { duration, request: serializedRequest, response: serializedResponse }; logger.info(format?.(info) || info, msg); }; res.on('close', onComplete); res.on('finish', onComplete); res.on('error', onComplete); runInTrace({ traceId }, next); }; }