Skip to content

日志

sora 框架的日志系统采用 Logger 与 Output 分离的设计。Logger 负责产生结构化的日志数据,Output 负责将这些数据输出到具体的目的地。一个 Logger 可以没有 Output(静默),也可以挂载多个 Output(同时输出到控制台和文件)。

架构

Logger (产生日志数据)

  │  ILoggerData (固定结构)
  │  ┌──────────────────────────────────┐
  │  │ time, timeString, identify,     │
  │  │ category, level, error,         │
  │  │ content, position, stack,       │
  │  │ raw, pid                         │
  │  └──────────────────────────────────┘

  ├── pipe(ConsoleOutput)  ──▶ console.log (彩色)
  ├── pipe(FileOutput)     ──▶ 文件 (按日期轮转)
  └── pipe(YourOutput)     ──▶ 数据库、消息队列...

Logger 产出的数据格式(ILoggerData)是固定的,Output 可以自由决定如何组织和展示这些内容。

Logger

框架内置三个 Logger 实例:

Loggeridentify用途
Runtime.frameLoggerframework框架内部日志(启动、关闭、状态变更等)
Runtime.rpcLoggerrpcRPC 通信日志(请求、响应、连接等)
AppLoggerapp应用业务日志(模板项目中自定义)

日志方法

typescript
Runtime.frameLogger.info('runtime', { event: 'load-config', config: options });
Runtime.frameLogger.success('framework', { event: 'start-runtime-success' });
Runtime.frameLogger.warn('connector', { event: 'connector-response-not-enabled' });
Runtime.frameLogger.error('listener', err, { event: 'listener-connector-off-error' });
Runtime.frameLogger.debug('runtime', { event: 'service-state-change', state });
Runtime.frameLogger.fatal('runtime', err, { event: 'connect-discovery' });
方法说明
debug(category, content)调试信息,需开启 debug 模式
info(category, content)一般信息
success(category, content)成功信息
warn(category, content)警告
error(category, error, content)错误,自动根据 ExError.level 路由到合适的日志级别
fatal(category, error, content)致命错误

category 自动推断

通过 logger.category 访问的 CategoryLogger 会自动从当前的 Context 中推断 category,无需手动传递:

typescript
// 在 Worker/Service 内部使用
this.logger_.category.info({ event: 'sync-completed', count: 100 });
// category 自动推断为当前作用域的名称

Output

Output 通过 pipe() 挂载到 Logger,支持链式调用:

typescript
Runtime.frameLogger.pipe(consoleOutput).pipe(fileOutput);
Runtime.rpcLogger.pipe(consoleOutput).pipe(fileOutput);
appLogger.pipe(consoleOutput).pipe(fileOutput);

每个 Output 可以通过 levels 选项过滤只接收特定级别的日志:

typescript
const consoleOutput = new ConsoleOutput({
  levels: [LogLevel.Info, LogLevel.Success, LogLevel.Warn, LogLevel.Error, LogLevel.Fatal],
});

ConsoleOutput

框架内置,输出到控制台并使用 chalk 着色:

typescript
import { ConsoleOutput } from '@sora-soft/framework';

const consoleOutput = new ConsoleOutput({
  levels: [LogLevel.Info, LogLevel.Success, LogLevel.Warn, LogLevel.Error, LogLevel.Fatal],
});

输出格式:

2024-01-15T10:30:00+08:00,2,framework,runtime,Runtime.ts:38,{"event":"load-config"}

FileOutput

模板项目内置,按日期自动轮转日志文件:

typescript
import { FileOutput } from './lib/FileLogger.js';

const fileOutput = new FileOutput({
  levels: logLevels,
  fileFormat: 'logs/app-YYYY-MM-DD.log',
});

自定义 Output

继承 LoggerOutput 实现 output()end() 方法即可:

typescript
import { LoggerOutput, type ILoggerData } from '@sora-soft/framework';

class DatabaseOutput extends LoggerOutput {
  protected async output(log: ILoggerData): Promise<void> {
    await db.insert('logs', {
      time: log.time,
      level: log.level,
      identify: log.identify,
      category: log.category,
      content: log.content,
    });
  }

  async end(): Promise<void> {
    // 关闭数据库连接等清理操作
  }
}

提示: 每个 Output 内部使用队列执行器(QueueExecutor)序列化写入,确保即使在高并发场景下日志也不会交错。

基于 WTFPL 许可发布