@lark-apaas/nestjs-logger 1.0.1-alpha.1 → 1.0.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,9 @@ import { LoggerModule } from '@lark-apaas/nestjs-logger';
36
36
  export class AppModule {}
37
37
  ```
38
38
 
39
- ### 2. 使用 Logger
39
+ ### 2. 使用 Logger(完全兼容 NestJS 官方 API)
40
+
41
+ 本库完全遵循 NestJS 官方 Logger API,无缝替换官方 Logger。
40
42
 
41
43
  ```typescript
42
44
  import { Injectable, Logger } from '@nestjs/common';
@@ -46,14 +48,87 @@ export class MyService {
46
48
  private readonly logger = new Logger(MyService.name);
47
49
 
48
50
  doSomething() {
51
+ // 简单日志
49
52
  this.logger.log('This is an info log');
50
- this.logger.debug('This is a debug log');
51
- this.logger.warn('This is a warning');
52
- this.logger.error('This is an error', error.stack);
53
+
54
+ // context 的日志(最后一个参数)
55
+ this.logger.log('User logged in', 'AuthModule');
56
+ // ↑ context
57
+
58
+ // 多个参数会用空格连接
59
+ this.logger.log('User', userId, 'logged in from', ip);
60
+ // 输出: User 123 logged in from 192.168.1.1
61
+
62
+ // 使用模板字符串
63
+ this.logger.log(`User ${userId} logged in from ${ip}`);
64
+
65
+ // 多参数 + context
66
+ this.logger.log('Processing order', orderId, 'OrderService');
67
+ // ↑ context
68
+ }
69
+
70
+ handleError() {
71
+ try {
72
+ // 业务逻辑
73
+ } catch (error) {
74
+ // Error 日志 + stack trace(官方推荐方式)
75
+ this.logger.error('Failed to process', error.stack);
76
+ // ↑ 第一个参数自动识别为 stack
77
+
78
+ // Error + stack + context
79
+ this.logger.error('Payment failed', error.stack, 'PaymentService');
80
+ // ↑ stack ↑ context
81
+ }
53
82
  }
54
83
  }
55
84
  ```
56
85
 
86
+ **参数规则(与 NestJS 官方一致)**:
87
+ - **最后一个字符串参数**:自动识别为 `context`
88
+ - **Error/Fatal 方法的第一个参数**:如果是 stack trace 格式,自动识别为 `stack`
89
+ - **多个参数**:会用空格连接成一个字符串
90
+ - **不支持格式化占位符**:不支持 `%s`, `%d`, `%o` 等占位符,请使用模板字符串
91
+
92
+ ### 3. 使用 logStructured 记录结构化日志(扩展功能)
93
+
94
+ 除了标准的 Logger API,本库还提供了 `logStructured()` 方法用于记录结构化日志:
95
+
96
+ ```typescript
97
+ import { Inject, Injectable } from '@nestjs/common';
98
+ import { AppLogger } from '@lark-apaas/nestjs-logger';
99
+
100
+ @Injectable()
101
+ export class MyService {
102
+ constructor(
103
+ @Inject(AppLogger) private readonly logger: AppLogger,
104
+ ) {}
105
+
106
+ doSomething() {
107
+ // 使用 logStructured 记录结构化日志
108
+ this.logger.logStructured(
109
+ 'log', // 日志级别
110
+ 'User action completed', // 消息
111
+ { // 元数据对象(会合并到日志字段中)
112
+ action: 'create_order',
113
+ order_id: '123',
114
+ amount: 100,
115
+ },
116
+ 'MyService', // context(可选)
117
+ );
118
+ }
119
+ }
120
+ ```
121
+
122
+ **两种方式对比**:
123
+
124
+ | 特性 | 标准 Logger API | logStructured API |
125
+ |------|----------------|-------------------|
126
+ | **用途** | 常规日志,兼容 NestJS 官方 | 结构化日志,将 meta 合并到字段 |
127
+ | **Context** | 最后一个参数自动识别 | 显式传递第4个参数 |
128
+ | **消息格式** | 使用模板字符串拼接 | 纯文本消息 |
129
+ | **元数据** | 需要手动拼接到消息中 | 作为独立字段存储,便于查询 |
130
+ | **使用场景** | 日常日志、错误日志 | 需要结构化查询的业务日志 |
131
+
57
132
  ## 环境变量配置
58
133
 
59
134
  ### 基础配置
@@ -240,7 +315,7 @@ NestJS LoggerService 级别到 Pino 级别的映射:
240
315
 
241
316
  日志会写入以下文件(默认在 `logs/` 目录):
242
317
 
243
- - `app.log` - 应用日志
318
+ - `server.log` - 应用日志
244
319
  - `trace.log` - HTTP 请求追踪日志
245
320
 
246
321
  ## 注意事项
@@ -266,6 +341,14 @@ NestJS LoggerService 级别到 Pino 级别的映射:
266
341
  - **延迟记录**:不在 Interceptor 的 `tap()` 或 `catchError()` 中记录日志,而是在 `finish` 事件中统一记录
267
342
  - **完整信息**:此时可以获取 Exception Filter 处理后的最终状态码和响应体,确保日志准确性
268
343
 
344
+ 4. **Logger API 兼容性**:
345
+ - **完全兼容 NestJS 官方**:实现与 `@nestjs/common` 的 Logger 完全一致的 API
346
+ - **参数解析规则**:
347
+ - 最后一个字符串参数自动识别为 `context`
348
+ - Error/Fatal 方法的第一个参数如果是 stack trace 格式,自动识别为 `stack`
349
+ - **无缝替换**:可以直接替换项目中的官方 Logger,无需修改业务代码
350
+ - **扩展功能**:提供 `logStructured()` 方法用于结构化日志场景
351
+
269
352
  ### 安全性
270
353
 
271
354
  1. **生产环境默认不打印 HTTP 追踪日志**:由于使用 verbose 级别,生产环境(info 级别)默认不会输出详细日志
package/dist/index.cjs CHANGED
@@ -14678,8 +14678,12 @@ var BasePinoLogger = class _BasePinoLogger {
14678
14678
  messageText: message
14679
14679
  };
14680
14680
  }
14681
+ const allMessages = [
14682
+ message,
14683
+ ...extras.map((e) => stringifyParam(e))
14684
+ ].join(" ");
14681
14685
  return {
14682
- messageText: (0, import_util.format)(message, ...extras)
14686
+ messageText: allMessages
14683
14687
  };
14684
14688
  }
14685
14689
  if (typeof message === "object" && message !== null) {
@@ -14739,6 +14743,18 @@ function mapLogLevelToPino(level) {
14739
14743
  }
14740
14744
  }
14741
14745
  __name(mapLogLevelToPino, "mapLogLevelToPino");
14746
+ function stringifyParam(param) {
14747
+ if (typeof param === "string") {
14748
+ return param;
14749
+ }
14750
+ const inspectOptions = {
14751
+ depth: 4,
14752
+ colors: false,
14753
+ compact: true
14754
+ };
14755
+ return (0, import_util.inspect)(param, inspectOptions);
14756
+ }
14757
+ __name(stringifyParam, "stringifyParam");
14742
14758
  function sanitizeValue(value) {
14743
14759
  if (value === null || value === void 0) {
14744
14760
  return value;
@@ -14784,7 +14800,7 @@ function normalizeLevel(level) {
14784
14800
  }
14785
14801
  __name(normalizeLevel, "normalizeLevel");
14786
14802
  var logger_config_default = (0, import_config.registerAs)("logger", () => {
14787
- const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "trace"));
14803
+ const level = normalizeLevel(process.env.LOGGER_LEVEL || (process.env.NODE_ENV === "production" ? "trace" : "trace"));
14788
14804
  const maxBodyLengthEnv = process.env.LOG_MAX_BODY_LENGTH;
14789
14805
  const maxBodyLength = maxBodyLengthEnv ? Number(maxBodyLengthEnv) : null;
14790
14806
  return {
@@ -14874,7 +14890,8 @@ var LoggingInterceptor = class {
14874
14890
  if (this.config.logRequestBody && Object.keys(req.query || {}).length > 0) {
14875
14891
  requestMeta["query_params"] = this.sanitizeAndTruncate(req.query);
14876
14892
  }
14877
- this.appLogger.log("HTTP request started \n url=%s \n request_query=%o \n request_body=%o", req.url, requestMeta["query_params"] ?? {}, requestMeta["request_body"] ?? {});
14893
+ this.appLogger.log(`HTTP request started
14894
+ url=${req.url}`, "HTTPTraceInterceptor");
14878
14895
  this.traceLogger.logStructured("verbose", "HTTP request started", requestMeta, "HTTPTraceInterceptor");
14879
14896
  let logged = false;
14880
14897
  res.on("finish", () => {
@@ -14901,15 +14918,29 @@ var LoggingInterceptor = class {
14901
14918
  const shouldLogBody = this.config.logResponseBody && isJsonResponse && finalResponseBody !== void 0;
14902
14919
  if (isError) {
14903
14920
  if (shouldLogBody) {
14904
- appLog("HTTP request failed\n url=%s \n status_code=%d \n duration_ms=%d \n response_body=%o", req.url, statusCode, durationMs, finalResponseBody);
14921
+ appLog(`HTTP request failed
14922
+ url=${req.url}
14923
+ status_code=${statusCode}
14924
+ duration_ms=${durationMs}
14925
+ response_body=${JSON.stringify(finalResponseBody ?? {})}`, "HTTPTraceInterceptor");
14905
14926
  } else {
14906
- appLog("HTTP request failed\n url=%s \n status_code=%d \n duration_ms=%d", req.url, statusCode, durationMs);
14927
+ appLog(`HTTP request failed
14928
+ url=${req.url}
14929
+ status_code=${statusCode}
14930
+ duration_ms=${durationMs}`, "HTTPTraceInterceptor");
14907
14931
  }
14908
14932
  } else {
14909
14933
  if (shouldLogBody) {
14910
- appLog("HTTP request completed\n url=%s \n status_code=%d \n duration_ms=%d \n response_body=%o", req.url, statusCode, durationMs, finalResponseBody);
14934
+ appLog(`HTTP request completed
14935
+ url=${req.url}
14936
+ status_code=${statusCode}
14937
+ duration_ms=${durationMs}
14938
+ response_body=${JSON.stringify(finalResponseBody ?? {})}`, "HTTPTraceInterceptor");
14911
14939
  } else {
14912
- appLog("HTTP request completed\n url=%s \n status_code=%d \n duration_ms=%d", req.url, statusCode, durationMs);
14940
+ appLog(`HTTP request completed
14941
+ url=${req.url}
14942
+ status_code=${statusCode}
14943
+ duration_ms=${durationMs}`, "HTTPTraceInterceptor");
14913
14944
  }
14914
14945
  }
14915
14946
  this.traceLogger.logStructured(traceLevel, isError ? "HTTP request failed" : "HTTP request completed", responseMeta, "HTTPTraceInterceptor");