@lark-apaas/nestjs-logger 0.1.0-alpha.6 → 0.1.0-alpha.7
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 +59 -87
- package/dist/index.cjs +5 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# NestJS Logger
|
|
2
2
|
|
|
3
|
-
基于 Pino 的高性能 NestJS
|
|
3
|
+
基于 Pino 的高性能 NestJS 日志库,支持请求追踪和结构化日志。
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
7
|
- 基于 Pino 的高性能日志记录
|
|
8
|
-
- 自动 HTTP
|
|
8
|
+
- 自动 HTTP 请求追踪(双层日志:info 链路日志 + verbose 详细日志)
|
|
9
9
|
- 请求和响应体日志记录(可配置)
|
|
10
|
-
- 敏感字段自动脱敏
|
|
11
10
|
- 支持多日志级别
|
|
12
11
|
- 独立的 trace 日志文件
|
|
13
12
|
- 请求上下文传递
|
|
13
|
+
- 日志体长度截断
|
|
14
14
|
|
|
15
15
|
## 安装
|
|
16
16
|
|
|
@@ -74,7 +74,6 @@ NODE_ENV=production
|
|
|
74
74
|
LOG_REQUEST_BODY=true
|
|
75
75
|
|
|
76
76
|
# 启用响应体日志(默认:false)
|
|
77
|
-
# 注意:仅记录 Content-Type 为 JSON 的响应
|
|
78
77
|
LOG_RESPONSE_BODY=true
|
|
79
78
|
|
|
80
79
|
# 注意:即使启用了请求/响应体日志,也需要将日志级别设置为 verbose 才能实际输出
|
|
@@ -84,9 +83,6 @@ LOGGER_LEVEL=verbose
|
|
|
84
83
|
# 日志体最大长度,超过会截断(默认:不限制)
|
|
85
84
|
# 不设置此环境变量时,不会进行截断
|
|
86
85
|
LOG_MAX_BODY_LENGTH=10000
|
|
87
|
-
|
|
88
|
-
# 敏感字段配置(逗号分隔,默认包含常见敏感字段)
|
|
89
|
-
LOG_SENSITIVE_FIELDS=password,token,secret,authorization,cookie,apiKey,accessToken,refreshToken
|
|
90
86
|
```
|
|
91
87
|
|
|
92
88
|
## 请求/响应体日志
|
|
@@ -115,108 +111,85 @@ LOG_RESPONSE_BODY=true
|
|
|
115
111
|
|
|
116
112
|
### 重要说明
|
|
117
113
|
|
|
118
|
-
1.
|
|
114
|
+
1. **双层日志机制**:
|
|
115
|
+
- **info 级别**:始终记录基础链路日志(不含 body),用于生产环境请求追踪
|
|
116
|
+
- **verbose 级别**:记录详细追踪日志(包含 body),用于开发调试
|
|
119
117
|
|
|
120
118
|
2. **默认不限制长度**:如果不设置 `LOG_MAX_BODY_LENGTH` 环境变量,日志体不会被截断。建议在生产环境设置合理的限制值。
|
|
121
119
|
|
|
122
|
-
3.
|
|
120
|
+
3. **响应数据处理**:日志库会尝试序列化所有响应数据,对于无法序列化的数据(如 Buffer、Stream)会返回类型信息。
|
|
123
121
|
|
|
124
122
|
### 日志输出示例
|
|
125
123
|
|
|
126
|
-
####
|
|
124
|
+
#### Info 级别(链路日志)
|
|
127
125
|
|
|
128
126
|
```json
|
|
129
127
|
{
|
|
130
|
-
"level": "
|
|
128
|
+
"level": "INFO",
|
|
131
129
|
"time": 1234567890,
|
|
132
130
|
"msg": "HTTP request started",
|
|
133
|
-
"method": "
|
|
131
|
+
"method": "GET",
|
|
134
132
|
"path": "/api/users",
|
|
135
133
|
"trace_id": "req-123-456",
|
|
136
134
|
"user_id": "user-001",
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"password": "***MASKED***"
|
|
141
|
-
},
|
|
142
|
-
"queryParams": {
|
|
143
|
-
"filter": "active"
|
|
144
|
-
}
|
|
135
|
+
"tenant_id": 24020896,
|
|
136
|
+
"app_id": "",
|
|
137
|
+
"context": "HTTPTraceInterceptor"
|
|
145
138
|
}
|
|
146
139
|
```
|
|
147
140
|
|
|
148
|
-
#### 响应日志(包含响应体)
|
|
149
|
-
|
|
150
141
|
```json
|
|
151
142
|
{
|
|
152
|
-
"level": "
|
|
143
|
+
"level": "INFO",
|
|
153
144
|
"time": 1234567890,
|
|
154
145
|
"msg": "HTTP request completed",
|
|
155
|
-
"method": "
|
|
146
|
+
"method": "GET",
|
|
156
147
|
"path": "/api/users",
|
|
157
148
|
"trace_id": "req-123-456",
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"id": "user-123",
|
|
162
|
-
"username": "john_doe",
|
|
163
|
-
"email": "john@example.com",
|
|
164
|
-
"accessToken": "***MASKED***"
|
|
165
|
-
}
|
|
149
|
+
"status_code": 200,
|
|
150
|
+
"duration_ms": 125,
|
|
151
|
+
"context": "HTTPTraceInterceptor"
|
|
166
152
|
}
|
|
167
153
|
```
|
|
168
154
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
### 默认脱敏字段
|
|
172
|
-
|
|
173
|
-
以下字段会自动脱敏(不区分大小写,支持部分匹配):
|
|
174
|
-
|
|
175
|
-
- password
|
|
176
|
-
- token
|
|
177
|
-
- secret
|
|
178
|
-
- authorization
|
|
179
|
-
- cookie
|
|
180
|
-
- apiKey
|
|
181
|
-
- accessToken
|
|
182
|
-
- refreshToken
|
|
183
|
-
|
|
184
|
-
### 自定义敏感字段
|
|
185
|
-
|
|
186
|
-
通过环境变量配置:
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
LOG_SENSITIVE_FIELDS=password,token,secret,myCustomSecret,privateKey
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 脱敏规则
|
|
193
|
-
|
|
194
|
-
- 字段匹配不区分大小写
|
|
195
|
-
- 支持部分匹配(例如:`accessToken` 会匹配 `token`)
|
|
196
|
-
- 敏感字段值会被替换为 `***MASKED***`
|
|
197
|
-
- 嵌套对象中的敏感字段也会被脱敏
|
|
198
|
-
|
|
199
|
-
### 示例
|
|
155
|
+
#### Verbose 级别(详细追踪日志)
|
|
200
156
|
|
|
201
|
-
```
|
|
202
|
-
// 原始数据
|
|
157
|
+
```json
|
|
203
158
|
{
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
159
|
+
"level": "TRACE",
|
|
160
|
+
"time": 1234567890,
|
|
161
|
+
"msg": "HTTP request started",
|
|
162
|
+
"method": "POST",
|
|
163
|
+
"path": "/api/users",
|
|
164
|
+
"trace_id": "req-123-456",
|
|
165
|
+
"request_body": {
|
|
166
|
+
"username": "john_doe",
|
|
167
|
+
"email": "john@example.com",
|
|
168
|
+
"password": "secret123"
|
|
169
|
+
},
|
|
170
|
+
"query_params": {
|
|
171
|
+
"filter": "active"
|
|
172
|
+
},
|
|
173
|
+
"context": "HTTPTraceInterceptor"
|
|
210
174
|
}
|
|
175
|
+
```
|
|
211
176
|
|
|
212
|
-
|
|
177
|
+
```json
|
|
213
178
|
{
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
179
|
+
"level": "TRACE",
|
|
180
|
+
"time": 1234567890,
|
|
181
|
+
"msg": "HTTP request completed",
|
|
182
|
+
"method": "POST",
|
|
183
|
+
"path": "/api/users",
|
|
184
|
+
"trace_id": "req-123-456",
|
|
185
|
+
"status_code": 201,
|
|
186
|
+
"duration_ms": 125,
|
|
187
|
+
"response_body": {
|
|
188
|
+
"id": "user-123",
|
|
189
|
+
"username": "john_doe",
|
|
190
|
+
"email": "john@example.com"
|
|
191
|
+
},
|
|
192
|
+
"context": "HTTPTraceInterceptor"
|
|
220
193
|
}
|
|
221
194
|
```
|
|
222
195
|
|
|
@@ -226,7 +199,7 @@ LOG_SENSITIVE_FIELDS=password,token,secret,myCustomSecret,privateKey
|
|
|
226
199
|
|
|
227
200
|
```json
|
|
228
201
|
{
|
|
229
|
-
"
|
|
202
|
+
"response_body": {
|
|
230
203
|
"_truncated": true,
|
|
231
204
|
"_originalLength": 50000,
|
|
232
205
|
"_data": "{ 前 10000 个字符... }..."
|
|
@@ -258,22 +231,21 @@ NestJS LoggerService 级别到 Pino 级别的映射:
|
|
|
258
231
|
|
|
259
232
|
### 安全性
|
|
260
233
|
|
|
261
|
-
1. **生产环境默认不打印 HTTP 追踪日志**:由于使用 verbose 级别,生产环境(info
|
|
262
|
-
2.
|
|
263
|
-
3.
|
|
264
|
-
4.
|
|
265
|
-
5. **仅 JSON 响应会被记录**,文件下载、HTML 等其他类型不会被记录
|
|
234
|
+
1. **生产环境默认不打印 HTTP 追踪日志**:由于使用 verbose 级别,生产环境(info 级别)默认不会输出详细日志
|
|
235
|
+
2. **双层日志设计**:info 级别始终记录链路日志,verbose 级别记录详细信息,按需启用
|
|
236
|
+
3. **按需启用**:需要查看详细追踪时,将 LOGGER_LEVEL 设置为 verbose
|
|
237
|
+
4. **敏感数据处理**:日志库不提供脱敏功能,请在业务层或通过中间件处理敏感数据
|
|
266
238
|
|
|
267
239
|
### 性能
|
|
268
240
|
|
|
269
|
-
1. verbose 级别会输出所有 HTTP
|
|
241
|
+
1. verbose 级别会输出所有 HTTP 请求详细日志,可能影响性能
|
|
270
242
|
2. 开启请求/响应体日志会增加日志体积和 I/O 开销
|
|
271
|
-
3.
|
|
243
|
+
3. 大对象的序列化会影响性能
|
|
272
244
|
4. 建议在开发/测试环境使用,生产环境按需临时启用
|
|
273
245
|
|
|
274
246
|
### 存储
|
|
275
247
|
|
|
276
|
-
1.
|
|
248
|
+
1. 日志直接写入文件,不支持自动轮转,建议通过外部工具(如 logrotate)管理日志文件
|
|
277
249
|
2. verbose 级别 + 请求/响应体日志会显著增加日志量
|
|
278
250
|
3. 建议设置 `LOG_MAX_BODY_LENGTH` 限制单条日志大小(默认不限制)
|
|
279
251
|
|
package/dist/index.cjs
CHANGED
|
@@ -14792,8 +14792,7 @@ var logger_config_default = (0, import_config.registerAs)("logger", () => {
|
|
|
14792
14792
|
logDir: process.env.LOG_DIR || "logs",
|
|
14793
14793
|
logRequestBody: process.env.LOG_REQUEST_BODY === "true",
|
|
14794
14794
|
logResponseBody: process.env.LOG_RESPONSE_BODY === "true",
|
|
14795
|
-
maxBodyLength
|
|
14796
|
-
sensitiveFields: (process.env.LOG_SENSITIVE_FIELDS || "").split(",").map((f) => f.trim())
|
|
14795
|
+
maxBodyLength
|
|
14797
14796
|
};
|
|
14798
14797
|
});
|
|
14799
14798
|
|
|
@@ -14914,15 +14913,14 @@ var LoggingInterceptor = class {
|
|
|
14914
14913
|
}));
|
|
14915
14914
|
}
|
|
14916
14915
|
/**
|
|
14917
|
-
*
|
|
14916
|
+
* 对数据进行截断处理
|
|
14918
14917
|
*/
|
|
14919
14918
|
sanitizeAndTruncate(data) {
|
|
14920
14919
|
try {
|
|
14921
|
-
const sanitized = this.maskSensitiveFields(data);
|
|
14922
14920
|
if (this.config.maxBodyLength === null) {
|
|
14923
|
-
return
|
|
14921
|
+
return data;
|
|
14924
14922
|
}
|
|
14925
|
-
const jsonStr = JSON.stringify(
|
|
14923
|
+
const jsonStr = JSON.stringify(data);
|
|
14926
14924
|
if (jsonStr.length > this.config.maxBodyLength) {
|
|
14927
14925
|
return {
|
|
14928
14926
|
_truncated: true,
|
|
@@ -14930,7 +14928,7 @@ var LoggingInterceptor = class {
|
|
|
14930
14928
|
_data: jsonStr.substring(0, this.config.maxBodyLength) + "..."
|
|
14931
14929
|
};
|
|
14932
14930
|
}
|
|
14933
|
-
return
|
|
14931
|
+
return data;
|
|
14934
14932
|
} catch (error) {
|
|
14935
14933
|
return {
|
|
14936
14934
|
_error: "Failed to serialize data",
|
|
@@ -14939,33 +14937,6 @@ var LoggingInterceptor = class {
|
|
|
14939
14937
|
};
|
|
14940
14938
|
}
|
|
14941
14939
|
}
|
|
14942
|
-
/**
|
|
14943
|
-
* 脱敏敏感字段
|
|
14944
|
-
*/
|
|
14945
|
-
maskSensitiveFields(data) {
|
|
14946
|
-
if (data === null || data === void 0) {
|
|
14947
|
-
return data;
|
|
14948
|
-
}
|
|
14949
|
-
if (Array.isArray(data)) {
|
|
14950
|
-
return data.map((item) => this.maskSensitiveFields(item));
|
|
14951
|
-
}
|
|
14952
|
-
if (typeof data === "object") {
|
|
14953
|
-
const result = {};
|
|
14954
|
-
for (const [key, value] of Object.entries(data)) {
|
|
14955
|
-
const lowerKey = key.toLowerCase();
|
|
14956
|
-
const isSensitive = this.config.sensitiveFields.some((field) => lowerKey.includes(field.toLowerCase()));
|
|
14957
|
-
if (isSensitive) {
|
|
14958
|
-
result[key] = "***MASKED***";
|
|
14959
|
-
} else if (typeof value === "object" && value !== null) {
|
|
14960
|
-
result[key] = this.maskSensitiveFields(value);
|
|
14961
|
-
} else {
|
|
14962
|
-
result[key] = value;
|
|
14963
|
-
}
|
|
14964
|
-
}
|
|
14965
|
-
return result;
|
|
14966
|
-
}
|
|
14967
|
-
return data;
|
|
14968
|
-
}
|
|
14969
14940
|
};
|
|
14970
14941
|
LoggingInterceptor = _ts_decorate3([
|
|
14971
14942
|
(0, import_common3.Injectable)(),
|