logger.ts 3.4 KB
import debug from 'debug';
import fs from 'fs';
import os from 'os';
import util from 'util';

// 类型定义
type LogLevel = 'error' | 'info' | 'warn' | 'debug' | 'trace';

type LoggerMethods = {
  [key in LogLevel]: debug.Debugger;
} & {
  close: () => void;
};

interface LoggerOptions {
  namespace: string;
  logFilePath?: string;
  enabledLevels?: string;
  debugEnabled?: boolean;
}

// 配置解析
const parseEnvBool = (envVar: string): boolean => {
  const value = process.env[envVar] || '';
  return /^(yes|on|true|enable|enabled|1)$/i.test(value);
};

const getEnvValue = (envVar: string, defaultValue: string): string => {
  return process.env[envVar] !== undefined ? process.env[envVar] : defaultValue;
};

// 默认配置
const DEFAULT_LOG_LEVELS = 'info:*,warn:*,error:*,debug:*,trace:*';
const WRITE_STREAM_OPTIONS: fs.WriteStreamOptions = {
  flags: 'a',
  encoding: 'utf8',
  autoClose: true,
  emitClose: true
};

// 空日志函数
const noop = (..._args: any[]): void => {};
const noopLogger: LoggerMethods = {
  error: noop,
  info: noop,
  warn: noop,
  debug: noop,
  trace: noop,
  close: noop
};

// 创建基础日志函数
const createBaseLoggers = (namespace: string): Omit<LoggerMethods, 'close'> => {
  return {
    error: debug('error').extend(namespace),
    info: debug('info').extend(namespace),
    warn: debug('warn').extend(namespace),
    debug: debug('debug').extend(namespace),
    trace: debug('trace').extend(namespace)
  };
};

// 配置日志级别
const configureLogLevels = (
  logger: Omit<LoggerMethods, 'close'>,
  enabledLevels: string
): void => {
  Object.entries(logger).forEach(([level]) => {
    logger[level as LogLevel].enabled = enabledLevels.includes(level);
  });
};

// 创建文件日志写入器
const createFileLogger = (
  logFilePath: string,
  logger: Omit<LoggerMethods, 'close'>,
  enabledLevels: string
): LoggerMethods => {
  const writeStream = fs.createWriteStream(logFilePath, WRITE_STREAM_OPTIONS);
  
  const logToFile = (...args: any[]): void => {
    writeStream.write(`${util.format(...args)}${os.EOL}`);
  };
  
  const logToConsoleAndFile = (...args: any[]): void => {
    const message = util.format(...args);
    console.error(message);
    writeStream.write(`${message}${os.EOL}`);
  };
  
  // 配置日志输出方式
  Object.entries(logger).forEach(([level, logFn]) => {
    logFn.log = enabledLevels.includes(level) ? logToConsoleAndFile : logToFile;
  });
  
  // 返回完整logger对象
  return {
    ...logger,
    close: (): void => {
      writeStream.end();
      // 切换回仅控制台日志
      Object.values(logger).forEach((logFn) => {
        logFn.log = console.error;
      });
    }
  };
};

// 主导出函数
export const createLogger = (
  options: LoggerOptions | string
): LoggerMethods => {
  // 处理参数重载
  const normalizedOptions = typeof options === 'string' 
    ? { namespace: options } 
    : options;
  
  const {
    namespace,
    logFilePath,
    enabledLevels = getEnvValue('DEBUG', DEFAULT_LOG_LEVELS),
    debugEnabled = parseEnvBool('XXL_JOB_DEBUG_LOG')
  } = normalizedOptions;

  if (!debugEnabled && !logFilePath) {
    return noopLogger;
  }
  
  const logger = createBaseLoggers(namespace);
  
  // 配置日志级别
  configureLogLevels(logger, enabledLevels);
  
  if (!logFilePath) {
    return {
      ...logger,
      close: noop
    };
  }
  
  // 文件日志配置
  return createFileLogger(logFilePath, logger, enabledLevels);
};

// 默认导出
export default createLogger;