@adatechnology/logger 0.0.9 → 0.0.10
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 +2 -17
- package/agents/skills/SKILL.md +15 -5
- package/dist/index.d.ts +22 -7
- package/dist/index.js +220 -162
- package/package.json +1 -1
- package/src/context/async-context.service.ts +1 -1
- package/src/implementations/winston/winston.logger.module.ts +231 -176
- package/src/implementations/winston/winston.logger.provider.ts +47 -48
- package/src/index.ts +12 -1
- package/src/interceptors/http-logging.interceptor.ts +11 -1
- package/src/logger.interface.ts +24 -8
- package/src/logger.provider.ts +19 -30
package/README.md
CHANGED
|
@@ -107,9 +107,7 @@ Ao realizar um log, você pode passar as seguintes propriedades para enriquecer
|
|
|
107
107
|
- `libMethod`: O método interno da biblioteca sendo executado. Ex: `get`.
|
|
108
108
|
- `meta`: Objeto com metadados adicionais (será exibido em uma única linha compacta).
|
|
109
109
|
|
|
110
|
-
> ✅ **Padrão
|
|
111
|
-
>
|
|
112
|
-
> ⚠️ **Compatibilidade legada:** payload em `params` ainda é aceito e promovido para `meta` internamente para não quebrar serviços antigos.
|
|
110
|
+
> ✅ **Padrão obrigatório:** enviar payload estruturado em `meta`.
|
|
113
111
|
|
|
114
112
|
### `meta` vs `params` (recomendação)
|
|
115
113
|
|
|
@@ -130,20 +128,7 @@ this.logger.error({
|
|
|
130
128
|
});
|
|
131
129
|
```
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
this.logger.error({
|
|
137
|
-
message: "Exception caught in filter",
|
|
138
|
-
context: "HttpExceptionFilter.logResponse",
|
|
139
|
-
requestId,
|
|
140
|
-
params: {
|
|
141
|
-
request: { path, method, headers, params, query, body },
|
|
142
|
-
response: { status, headers, messages },
|
|
143
|
-
error: { type, message, status, body, details },
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
```
|
|
131
|
+
Campos fora do contrato (`message`, `context`, `meta`) não são recomendados na API pública.
|
|
147
132
|
|
|
148
133
|
### Exemplo de Log de Biblioteca
|
|
149
134
|
|
package/agents/skills/SKILL.md
CHANGED
|
@@ -6,42 +6,52 @@ description: Patterns for @adatechnology/logger. Use when configuring Winston lo
|
|
|
6
6
|
# 📝 Logger Standards
|
|
7
7
|
|
|
8
8
|
## 🚀 Setup Example
|
|
9
|
+
|
|
9
10
|
```typescript
|
|
10
11
|
@Module({
|
|
11
12
|
imports: [
|
|
12
13
|
LoggerModule.forRoot({
|
|
13
|
-
level:
|
|
14
|
-
sensitiveKeys: [
|
|
14
|
+
level: "info",
|
|
15
|
+
sensitiveKeys: ["password", "token", "clientSecret"],
|
|
15
16
|
}),
|
|
16
17
|
],
|
|
17
18
|
})
|
|
18
19
|
export class AppModule implements NestModule {
|
|
19
20
|
configure(consumer: MiddlewareConsumer) {
|
|
20
21
|
// Required to enable Request-ID tracking across async calls
|
|
21
|
-
consumer.apply(RequestContextMiddleware).forRoutes(
|
|
22
|
+
consumer.apply(RequestContextMiddleware).forRoutes("*");
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
## 🏗️ Usage Pattern
|
|
28
|
+
|
|
27
29
|
```typescript
|
|
28
30
|
@Injectable()
|
|
29
31
|
export class MyService {
|
|
30
32
|
constructor(
|
|
31
|
-
@Inject(LOGGER_PROVIDER) private readonly logger: LoggerProviderInterface
|
|
33
|
+
@Inject(LOGGER_PROVIDER) private readonly logger: LoggerProviderInterface,
|
|
32
34
|
) {}
|
|
33
35
|
|
|
34
36
|
doWork() {
|
|
35
|
-
this.logger.info(
|
|
37
|
+
this.logger.info({
|
|
38
|
+
message: "Action performed",
|
|
39
|
+
context: MyService.name,
|
|
40
|
+
meta: { userId: "123" },
|
|
41
|
+
});
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
```
|
|
39
45
|
|
|
40
46
|
## 🔐 Obfuscation
|
|
47
|
+
|
|
41
48
|
The logger automatically hides values for keys defined in `sensitiveKeys`.
|
|
49
|
+
|
|
42
50
|
- **Default Keys**: `password`, `token`, `authorization`, `cookie`, `secret`.
|
|
43
51
|
- **Custom Keys**: Add via `LoggerModule.forRoot()`.
|
|
44
52
|
|
|
45
53
|
## 🛠️ Internal Mechanics
|
|
54
|
+
|
|
46
55
|
- **Async Context**: Uses `AsyncLocalStorage` to store the Request-ID.
|
|
47
56
|
- **Context Access**: Use `getContext()` to retrieve the current request state anywhere in the call stack.
|
|
57
|
+
- **Type naming**: aliases de tipagem com sufixo `Params`/`Result` devem usar **PascalCase** (ex.: `InfoParams`, `InfoResult`).
|
package/dist/index.d.ts
CHANGED
|
@@ -21,18 +21,32 @@ declare enum LoggerLevel {
|
|
|
21
21
|
WARN = "warn",
|
|
22
22
|
ERROR = "error"
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
type LogParams = {
|
|
25
25
|
message: string;
|
|
26
26
|
context?: string;
|
|
27
27
|
meta?: Record<string, unknown>;
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
|
+
type LogResult = void;
|
|
30
|
+
type DebugParams = LogParams;
|
|
31
|
+
type DebugResult = LogResult;
|
|
32
|
+
type InfoParams = LogParams;
|
|
33
|
+
type InfoResult = LogResult;
|
|
34
|
+
type WarnParams = LogParams;
|
|
35
|
+
type WarnResult = LogResult;
|
|
36
|
+
type ErrorParams = LogParams;
|
|
37
|
+
type ErrorResult = LogResult;
|
|
29
38
|
interface LoggerProviderInterface {
|
|
30
|
-
debug(
|
|
31
|
-
info(
|
|
32
|
-
warn(
|
|
33
|
-
error(
|
|
39
|
+
debug(params: DebugParams): DebugResult;
|
|
40
|
+
info(params: InfoParams): InfoResult;
|
|
41
|
+
warn(params: WarnParams): WarnResult;
|
|
42
|
+
error(params: ErrorParams): ErrorResult;
|
|
34
43
|
setContext?(context: string): void;
|
|
35
44
|
}
|
|
45
|
+
type WriteLogParams = {
|
|
46
|
+
level: LoggerLevel;
|
|
47
|
+
payload: LogParams;
|
|
48
|
+
};
|
|
49
|
+
type WriteLogResult = LogResult;
|
|
36
50
|
|
|
37
51
|
interface LoggerConfig extends WinstonModuleConfig {
|
|
38
52
|
/**
|
|
@@ -109,6 +123,7 @@ declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
109
123
|
private readonly logger;
|
|
110
124
|
private readonly reflector;
|
|
111
125
|
private readonly excludedPaths;
|
|
126
|
+
private toErrorMessage;
|
|
112
127
|
constructor(logger: LoggerProviderInterface, reflector: Reflector, config?: LoggerConfig);
|
|
113
128
|
private isExcluded;
|
|
114
129
|
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
@@ -127,4 +142,4 @@ type RequestContext = Record<string, unknown> | undefined;
|
|
|
127
142
|
declare function getContext(): RequestContext;
|
|
128
143
|
declare function runWithContext<T>(ctx: Record<string, unknown>, fn: () => T): T;
|
|
129
144
|
|
|
130
|
-
export { DEFAULT_LOGGER_CONFIG, ExcludeHttpLogging, HTTP_LOGGING_INTERCEPTOR, HTTP_LOGGING_INTERCEPTOR_CONTEXT, HttpLoggingInterceptor, LOGGER_CONFIG, LOGGER_PROVIDER, type
|
|
145
|
+
export { DEFAULT_LOGGER_CONFIG, type DebugParams, type DebugResult, type ErrorParams, type ErrorResult, ExcludeHttpLogging, HTTP_LOGGING_INTERCEPTOR, HTTP_LOGGING_INTERCEPTOR_CONTEXT, HttpLoggingInterceptor, type InfoParams, type InfoResult, LOGGER_CONFIG, LOGGER_PROVIDER, type LogParams, type LogResult, type LoggerConfig, LoggerLevel, LoggerModule, type LoggerProviderInterface, RequestContextMiddleware, type WarnParams, type WarnResult, type WriteLogParams, type WriteLogResult, getContext, runWithContext };
|
package/dist/index.js
CHANGED
|
@@ -9049,23 +9049,22 @@ var LoggerProvider = class {
|
|
|
9049
9049
|
constructor(implementation) {
|
|
9050
9050
|
this.implementation = implementation;
|
|
9051
9051
|
}
|
|
9052
|
-
debug(
|
|
9053
|
-
return this.implementation.debug(
|
|
9052
|
+
debug(params) {
|
|
9053
|
+
return this.implementation.debug(params);
|
|
9054
9054
|
}
|
|
9055
|
-
info(
|
|
9056
|
-
return this.implementation.info(
|
|
9055
|
+
info(params) {
|
|
9056
|
+
return this.implementation.info(params);
|
|
9057
9057
|
}
|
|
9058
|
-
warn(
|
|
9059
|
-
return this.implementation.warn(
|
|
9058
|
+
warn(params) {
|
|
9059
|
+
return this.implementation.warn(params);
|
|
9060
9060
|
}
|
|
9061
|
-
error(
|
|
9062
|
-
return this.implementation.error(
|
|
9061
|
+
error(params) {
|
|
9062
|
+
return this.implementation.error(params);
|
|
9063
9063
|
}
|
|
9064
9064
|
setContext(context) {
|
|
9065
9065
|
if (typeof this.implementation.setContext === "function") {
|
|
9066
9066
|
return this.implementation.setContext(context);
|
|
9067
9067
|
}
|
|
9068
|
-
return;
|
|
9069
9068
|
}
|
|
9070
9069
|
};
|
|
9071
9070
|
LoggerProvider = _ts_decorate([
|
|
@@ -9085,7 +9084,7 @@ var HTTP_LOGGING_INTERCEPTOR = "HTTP_LOGGING_INTERCEPTOR";
|
|
|
9085
9084
|
// src/implementations/winston/winston.logger.module.ts
|
|
9086
9085
|
var import_common3 = require("@nestjs/common");
|
|
9087
9086
|
var import_winston2 = require("winston");
|
|
9088
|
-
var
|
|
9087
|
+
var import_node_util = require("util");
|
|
9089
9088
|
|
|
9090
9089
|
// src/implementations/winston/winston.logger.provider.ts
|
|
9091
9090
|
var import_common2 = require("@nestjs/common");
|
|
@@ -9101,8 +9100,8 @@ var LoggerLevel = /* @__PURE__ */ (function(LoggerLevel2) {
|
|
|
9101
9100
|
})({});
|
|
9102
9101
|
|
|
9103
9102
|
// src/context/async-context.service.ts
|
|
9104
|
-
var
|
|
9105
|
-
var asyncLocalStorage = new
|
|
9103
|
+
var import_node_async_hooks = require("async_hooks");
|
|
9104
|
+
var asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
9106
9105
|
function getContext() {
|
|
9107
9106
|
return asyncLocalStorage.getStore();
|
|
9108
9107
|
}
|
|
@@ -9166,44 +9165,45 @@ var WinstonLoggerProvider = class {
|
|
|
9166
9165
|
this.logger = logger;
|
|
9167
9166
|
this.obfuscator = obfuscator;
|
|
9168
9167
|
}
|
|
9169
|
-
debug(
|
|
9170
|
-
this.
|
|
9168
|
+
debug(payload) {
|
|
9169
|
+
this.log({
|
|
9170
|
+
level: LoggerLevel.DEBUG,
|
|
9171
|
+
payload: {
|
|
9172
|
+
...payload,
|
|
9173
|
+
context: payload.context || this.context
|
|
9174
|
+
}
|
|
9175
|
+
});
|
|
9171
9176
|
}
|
|
9172
|
-
info(
|
|
9173
|
-
this.
|
|
9177
|
+
info(payload) {
|
|
9178
|
+
this.log({
|
|
9179
|
+
level: LoggerLevel.INFO,
|
|
9180
|
+
payload: {
|
|
9181
|
+
...payload,
|
|
9182
|
+
context: payload.context || this.context
|
|
9183
|
+
}
|
|
9184
|
+
});
|
|
9174
9185
|
}
|
|
9175
|
-
warn(
|
|
9176
|
-
this.
|
|
9186
|
+
warn(payload) {
|
|
9187
|
+
this.log({
|
|
9188
|
+
level: LoggerLevel.WARN,
|
|
9189
|
+
payload: {
|
|
9190
|
+
...payload,
|
|
9191
|
+
context: payload.context || this.context
|
|
9192
|
+
}
|
|
9193
|
+
});
|
|
9177
9194
|
}
|
|
9178
|
-
error(
|
|
9179
|
-
this.
|
|
9195
|
+
error(payload) {
|
|
9196
|
+
this.log({
|
|
9197
|
+
level: LoggerLevel.ERROR,
|
|
9198
|
+
payload: {
|
|
9199
|
+
...payload,
|
|
9200
|
+
context: payload.context || this.context
|
|
9201
|
+
}
|
|
9202
|
+
});
|
|
9180
9203
|
}
|
|
9181
9204
|
setContext(context) {
|
|
9182
9205
|
this.context = context;
|
|
9183
9206
|
}
|
|
9184
|
-
handleLog(level, messageOrPayload, meta, context) {
|
|
9185
|
-
let payload;
|
|
9186
|
-
if (typeof messageOrPayload === "string") {
|
|
9187
|
-
payload = {
|
|
9188
|
-
message: messageOrPayload,
|
|
9189
|
-
meta,
|
|
9190
|
-
context: context || this.context
|
|
9191
|
-
};
|
|
9192
|
-
} else {
|
|
9193
|
-
payload = {
|
|
9194
|
-
...messageOrPayload,
|
|
9195
|
-
context: messageOrPayload.context || context || this.context,
|
|
9196
|
-
meta: {
|
|
9197
|
-
...messageOrPayload.meta,
|
|
9198
|
-
...meta
|
|
9199
|
-
}
|
|
9200
|
-
};
|
|
9201
|
-
}
|
|
9202
|
-
this.log({
|
|
9203
|
-
level,
|
|
9204
|
-
payload
|
|
9205
|
-
});
|
|
9206
|
-
}
|
|
9207
9207
|
log(params) {
|
|
9208
9208
|
const { level, payload } = params;
|
|
9209
9209
|
const { message, context, meta, ...rest } = payload;
|
|
@@ -9215,7 +9215,7 @@ var WinstonLoggerProvider = class {
|
|
|
9215
9215
|
const logInfo = {
|
|
9216
9216
|
...rest,
|
|
9217
9217
|
context: messageContext,
|
|
9218
|
-
requestId: requestIdFromContext ||
|
|
9218
|
+
requestId: requestIdFromContext || rest.requestId,
|
|
9219
9219
|
meta: obfuscatedMeta
|
|
9220
9220
|
};
|
|
9221
9221
|
this.logger.log(level, messageText, logInfo);
|
|
@@ -9332,6 +9332,163 @@ function _ts_decorate3(decorators, target, key, desc) {
|
|
|
9332
9332
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
9333
9333
|
}
|
|
9334
9334
|
__name(_ts_decorate3, "_ts_decorate");
|
|
9335
|
+
function asString(value) {
|
|
9336
|
+
if (typeof value === "string") return value;
|
|
9337
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
9338
|
+
return String(value);
|
|
9339
|
+
}
|
|
9340
|
+
return void 0;
|
|
9341
|
+
}
|
|
9342
|
+
__name(asString, "asString");
|
|
9343
|
+
function colorizeText(useColors, color, reset, text) {
|
|
9344
|
+
return useColors ? `${color}${text}${reset}` : text;
|
|
9345
|
+
}
|
|
9346
|
+
__name(colorizeText, "colorizeText");
|
|
9347
|
+
function fillInfoFromMeta(info) {
|
|
9348
|
+
if (!info.meta || typeof info.meta !== "object") return;
|
|
9349
|
+
const meta = info.meta;
|
|
9350
|
+
const metadataKeys = [
|
|
9351
|
+
"requestId",
|
|
9352
|
+
"context",
|
|
9353
|
+
"source",
|
|
9354
|
+
"lib",
|
|
9355
|
+
"libVersion",
|
|
9356
|
+
"libMethod",
|
|
9357
|
+
"appName",
|
|
9358
|
+
"appVersion"
|
|
9359
|
+
];
|
|
9360
|
+
for (const key of metadataKeys) {
|
|
9361
|
+
if (meta[key] && !info[key]) {
|
|
9362
|
+
info[key] = meta[key];
|
|
9363
|
+
delete meta[key];
|
|
9364
|
+
}
|
|
9365
|
+
}
|
|
9366
|
+
const logContext = meta.logContext;
|
|
9367
|
+
if (!logContext || info.source) return;
|
|
9368
|
+
const className = asString(logContext.className);
|
|
9369
|
+
const methodName = asString(logContext.methodName);
|
|
9370
|
+
if (className && methodName) {
|
|
9371
|
+
info.source = `${className}.${methodName}`;
|
|
9372
|
+
} else if (className) {
|
|
9373
|
+
info.source = className;
|
|
9374
|
+
} else if (methodName) {
|
|
9375
|
+
info.source = methodName;
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
__name(fillInfoFromMeta, "fillInfoFromMeta");
|
|
9379
|
+
function applyDefaultInfoValues(info, config) {
|
|
9380
|
+
info.requestId = info.requestId || "no-request-id";
|
|
9381
|
+
info.context = info.context || config?.context || "App";
|
|
9382
|
+
info.appName = info.appName || config?.appName;
|
|
9383
|
+
info.appVersion = info.appVersion || config?.appVersion;
|
|
9384
|
+
info.lib = info.lib || config?.lib;
|
|
9385
|
+
info.libVersion = info.libVersion || config?.libVersion;
|
|
9386
|
+
}
|
|
9387
|
+
__name(applyDefaultInfoValues, "applyDefaultInfoValues");
|
|
9388
|
+
function buildMethodDisplays(params) {
|
|
9389
|
+
const { context, source, lib, libMethod, colorize } = params;
|
|
9390
|
+
let sourceDisplay = "";
|
|
9391
|
+
let libMethodDisplay = "";
|
|
9392
|
+
if (source) {
|
|
9393
|
+
sourceDisplay = `[${colorize(source)}]`;
|
|
9394
|
+
}
|
|
9395
|
+
if (lib) {
|
|
9396
|
+
const methodPath = libMethod ? `${context}.${libMethod}` : context;
|
|
9397
|
+
libMethodDisplay = `[${colorize(methodPath)}]`;
|
|
9398
|
+
if (source === context || source === methodPath) {
|
|
9399
|
+
sourceDisplay = "";
|
|
9400
|
+
}
|
|
9401
|
+
return {
|
|
9402
|
+
sourceDisplay,
|
|
9403
|
+
libMethodDisplay
|
|
9404
|
+
};
|
|
9405
|
+
}
|
|
9406
|
+
if (!source) {
|
|
9407
|
+
libMethodDisplay = `[${colorize(context)}]`;
|
|
9408
|
+
return {
|
|
9409
|
+
sourceDisplay,
|
|
9410
|
+
libMethodDisplay
|
|
9411
|
+
};
|
|
9412
|
+
}
|
|
9413
|
+
if (source.startsWith(`${context}.`) || source === context) {
|
|
9414
|
+
libMethodDisplay = `[${colorize(source)}]`;
|
|
9415
|
+
sourceDisplay = "";
|
|
9416
|
+
return {
|
|
9417
|
+
sourceDisplay,
|
|
9418
|
+
libMethodDisplay
|
|
9419
|
+
};
|
|
9420
|
+
}
|
|
9421
|
+
libMethodDisplay = `[${colorize(context)}]`;
|
|
9422
|
+
sourceDisplay = `[${colorize(source)}]`;
|
|
9423
|
+
return {
|
|
9424
|
+
sourceDisplay,
|
|
9425
|
+
libMethodDisplay
|
|
9426
|
+
};
|
|
9427
|
+
}
|
|
9428
|
+
__name(buildMethodDisplays, "buildMethodDisplays");
|
|
9429
|
+
function formatDevelopmentLog(infoInput, useColors, levelColorizer) {
|
|
9430
|
+
const info = infoInput;
|
|
9431
|
+
const level = asString(info.level) ?? "info";
|
|
9432
|
+
const message = asString(info.message) ?? "";
|
|
9433
|
+
const timestamp = asString(info.timestamp) ?? "";
|
|
9434
|
+
const requestId = asString(info.requestId) ?? "no-request-id";
|
|
9435
|
+
const context = asString(info.context) ?? "App";
|
|
9436
|
+
const source = asString(info.source);
|
|
9437
|
+
const appName = asString(info.appName);
|
|
9438
|
+
const appVersion = asString(info.appVersion);
|
|
9439
|
+
const lib = asString(info.lib);
|
|
9440
|
+
const libMethod = asString(info.libMethod);
|
|
9441
|
+
const libVersion = asString(info.libVersion);
|
|
9442
|
+
const stack = asString(info.stack);
|
|
9443
|
+
const meta = info.meta && typeof info.meta === "object" ? info.meta : void 0;
|
|
9444
|
+
const colors = {
|
|
9445
|
+
reset: "\x1B[0m",
|
|
9446
|
+
gray: "\x1B[90m",
|
|
9447
|
+
cyan: "\x1B[36m",
|
|
9448
|
+
magenta: "\x1B[35m",
|
|
9449
|
+
yellow: "\x1B[33m",
|
|
9450
|
+
red: "\x1B[31m",
|
|
9451
|
+
green: "\x1B[32m"
|
|
9452
|
+
};
|
|
9453
|
+
const coloredLevel = useColors ? levelColorizer.colorize(level, level.toUpperCase()) : level.toUpperCase();
|
|
9454
|
+
const coloredTime = colorizeText(useColors, colors.gray, colors.reset, timestamp);
|
|
9455
|
+
const coloredRequestId = colorizeText(useColors, colors.cyan, colors.reset, requestId);
|
|
9456
|
+
let appDisplay = "";
|
|
9457
|
+
if (appName) {
|
|
9458
|
+
const appText = appVersion ? `${appName}@${appVersion}` : appName;
|
|
9459
|
+
const appLabel = `App-${appText}`;
|
|
9460
|
+
appDisplay = `[${colorizeText(useColors, colors.green, colors.reset, appLabel)}]`;
|
|
9461
|
+
}
|
|
9462
|
+
let libDisplay = "";
|
|
9463
|
+
if (lib) {
|
|
9464
|
+
const libText = libVersion ? `${lib}:${libVersion}` : lib;
|
|
9465
|
+
libDisplay = `[${colorizeText(useColors, colors.yellow, colors.reset, libText)}]`;
|
|
9466
|
+
}
|
|
9467
|
+
const { sourceDisplay, libMethodDisplay } = buildMethodDisplays({
|
|
9468
|
+
context,
|
|
9469
|
+
source,
|
|
9470
|
+
lib,
|
|
9471
|
+
libMethod,
|
|
9472
|
+
colorize: /* @__PURE__ */ __name((text) => colorizeText(useColors, colors.magenta, colors.reset, text), "colorize")
|
|
9473
|
+
});
|
|
9474
|
+
let output = `${appDisplay}${libDisplay}[${coloredRequestId}][${coloredTime}]${sourceDisplay}${libMethodDisplay}[${coloredLevel}] - ${message}`;
|
|
9475
|
+
if (meta && typeof meta === "object" && Object.keys(meta).length > 0) {
|
|
9476
|
+
const inspectedMeta = (0, import_node_util.inspect)(meta, {
|
|
9477
|
+
colors: useColors,
|
|
9478
|
+
depth: null,
|
|
9479
|
+
compact: true,
|
|
9480
|
+
sorted: true,
|
|
9481
|
+
breakLength: Infinity
|
|
9482
|
+
});
|
|
9483
|
+
output += ` - ${inspectedMeta}`;
|
|
9484
|
+
}
|
|
9485
|
+
if (stack) {
|
|
9486
|
+
output += `
|
|
9487
|
+
${colorizeText(useColors, colors.red, colors.reset, stack)}`;
|
|
9488
|
+
}
|
|
9489
|
+
return output;
|
|
9490
|
+
}
|
|
9491
|
+
__name(formatDevelopmentLog, "formatDevelopmentLog");
|
|
9335
9492
|
var WinstonImplementationModule = class _WinstonImplementationModule {
|
|
9336
9493
|
static {
|
|
9337
9494
|
__name(this, "WinstonImplementationModule");
|
|
@@ -9406,124 +9563,13 @@ var WinstonImplementationModule = class _WinstonImplementationModule {
|
|
|
9406
9563
|
const isProduction = config?.isProduction ?? process.env.NODE_ENV === "production";
|
|
9407
9564
|
const useColors = config?.colorize !== false;
|
|
9408
9565
|
const standardFields = (0, import_winston2.format)((info) => {
|
|
9409
|
-
const
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
delete meta.requestId;
|
|
9414
|
-
}
|
|
9415
|
-
if (meta.context && !info.context) {
|
|
9416
|
-
info.context = meta.context;
|
|
9417
|
-
delete meta.context;
|
|
9418
|
-
}
|
|
9419
|
-
if (meta.source && !info.source) {
|
|
9420
|
-
info.source = meta.source;
|
|
9421
|
-
delete meta.source;
|
|
9422
|
-
}
|
|
9423
|
-
if (meta.lib && !info.lib) {
|
|
9424
|
-
info.lib = meta.lib;
|
|
9425
|
-
delete meta.lib;
|
|
9426
|
-
}
|
|
9427
|
-
if (meta.libVersion && !info.libVersion) {
|
|
9428
|
-
info.libVersion = meta.libVersion;
|
|
9429
|
-
delete meta.libVersion;
|
|
9430
|
-
}
|
|
9431
|
-
if (meta.libMethod && !info.libMethod) {
|
|
9432
|
-
info.libMethod = meta.libMethod;
|
|
9433
|
-
delete meta.libMethod;
|
|
9434
|
-
}
|
|
9435
|
-
if (meta.appName && !info.appName) {
|
|
9436
|
-
info.appName = meta.appName;
|
|
9437
|
-
delete meta.appName;
|
|
9438
|
-
}
|
|
9439
|
-
if (meta.appVersion && !info.appVersion) {
|
|
9440
|
-
info.appVersion = meta.appVersion;
|
|
9441
|
-
delete meta.appVersion;
|
|
9442
|
-
}
|
|
9443
|
-
if (meta.logContext && !info.source) {
|
|
9444
|
-
const lc = meta.logContext;
|
|
9445
|
-
if (lc.className && lc.methodName) {
|
|
9446
|
-
info.source = `${lc.className}.${lc.methodName}`;
|
|
9447
|
-
} else if (lc.className) {
|
|
9448
|
-
info.source = lc.className;
|
|
9449
|
-
} else if (lc.methodName) {
|
|
9450
|
-
info.source = lc.methodName;
|
|
9451
|
-
}
|
|
9452
|
-
}
|
|
9453
|
-
}
|
|
9454
|
-
info.requestId = info.requestId || "no-request-id";
|
|
9455
|
-
info.context = info.context || config?.context || "App";
|
|
9456
|
-
info.appName = info.appName || config?.appName;
|
|
9457
|
-
info.appVersion = info.appVersion || config?.appVersion;
|
|
9458
|
-
info.lib = info.lib || config?.lib;
|
|
9459
|
-
info.libVersion = info.libVersion || config?.libVersion;
|
|
9460
|
-
return info;
|
|
9566
|
+
const writableInfo = info;
|
|
9567
|
+
fillInfoFromMeta(writableInfo);
|
|
9568
|
+
applyDefaultInfoValues(writableInfo, config);
|
|
9569
|
+
return writableInfo;
|
|
9461
9570
|
});
|
|
9462
9571
|
const levelColorizer = import_winston2.format.colorize();
|
|
9463
|
-
const developmentFormat = import_winston2.format.printf((info) =>
|
|
9464
|
-
const { level, message, timestamp, requestId, context, source, meta, stack, appName, appVersion, lib, libMethod, libVersion } = info;
|
|
9465
|
-
const colors = {
|
|
9466
|
-
reset: "\x1B[0m",
|
|
9467
|
-
gray: "\x1B[90m",
|
|
9468
|
-
cyan: "\x1B[36m",
|
|
9469
|
-
magenta: "\x1B[35m",
|
|
9470
|
-
yellow: "\x1B[33m",
|
|
9471
|
-
red: "\x1B[31m",
|
|
9472
|
-
green: "\x1B[32m",
|
|
9473
|
-
bold: "\x1B[1m"
|
|
9474
|
-
};
|
|
9475
|
-
const colorize = /* @__PURE__ */ __name((color, text) => useColors ? `${color}${text}${colors.reset}` : text, "colorize");
|
|
9476
|
-
const coloredLevel = useColors ? levelColorizer.colorize(level, level.toUpperCase()) : level.toUpperCase();
|
|
9477
|
-
const coloredTime = colorize(colors.gray, timestamp);
|
|
9478
|
-
const coloredRequestId = colorize(colors.cyan, requestId);
|
|
9479
|
-
let appDisplay = "";
|
|
9480
|
-
if (appName) {
|
|
9481
|
-
const appText = appVersion ? `${appName}@${appVersion}` : appName;
|
|
9482
|
-
appDisplay = `[${colorize(colors.green, `App-${appText}`)}]`;
|
|
9483
|
-
}
|
|
9484
|
-
let libDisplay = "";
|
|
9485
|
-
if (lib) {
|
|
9486
|
-
const libText = libVersion ? `${lib}:${libVersion}` : lib;
|
|
9487
|
-
libDisplay = `[${colorize(colors.yellow, libText)}]`;
|
|
9488
|
-
}
|
|
9489
|
-
const mag = colors.magenta;
|
|
9490
|
-
let sourceDisplay = "";
|
|
9491
|
-
let libMethodDisplay = "";
|
|
9492
|
-
if (source) {
|
|
9493
|
-
sourceDisplay = `[${colorize(mag, source)}]`;
|
|
9494
|
-
}
|
|
9495
|
-
if (lib) {
|
|
9496
|
-
const methodPath = libMethod ? `${context}.${libMethod}` : context;
|
|
9497
|
-
libMethodDisplay = `[${colorize(mag, methodPath)}]`;
|
|
9498
|
-
if (source === context || source === methodPath) {
|
|
9499
|
-
sourceDisplay = "";
|
|
9500
|
-
}
|
|
9501
|
-
} else if (!source) {
|
|
9502
|
-
libMethodDisplay = `[${colorize(mag, context)}]`;
|
|
9503
|
-
} else if (source.startsWith(`${context}.`) || source === context) {
|
|
9504
|
-
libMethodDisplay = `[${colorize(mag, source)}]`;
|
|
9505
|
-
sourceDisplay = "";
|
|
9506
|
-
} else {
|
|
9507
|
-
libMethodDisplay = `[${colorize(mag, context)}]`;
|
|
9508
|
-
sourceDisplay = `[${colorize(mag, source)}]`;
|
|
9509
|
-
}
|
|
9510
|
-
let output = `${appDisplay}${libDisplay}[${coloredRequestId}][${coloredTime}]${sourceDisplay}${libMethodDisplay}[${coloredLevel}] - ${message}`;
|
|
9511
|
-
if (meta && typeof meta === "object" && Object.keys(meta).length > 0) {
|
|
9512
|
-
const inspectedMeta = (0, import_util.inspect)(meta, {
|
|
9513
|
-
colors: useColors,
|
|
9514
|
-
depth: null,
|
|
9515
|
-
compact: true,
|
|
9516
|
-
sorted: true,
|
|
9517
|
-
breakLength: Infinity
|
|
9518
|
-
});
|
|
9519
|
-
output += ` - ${inspectedMeta}`;
|
|
9520
|
-
}
|
|
9521
|
-
if (stack) {
|
|
9522
|
-
output += `
|
|
9523
|
-
${colorize(colors.red, stack)}`;
|
|
9524
|
-
}
|
|
9525
|
-
return output;
|
|
9526
|
-
});
|
|
9572
|
+
const developmentFormat = import_winston2.format.printf((info) => formatDevelopmentLog(info, useColors, levelColorizer));
|
|
9527
9573
|
const formats = [
|
|
9528
9574
|
import_winston2.format.timestamp(),
|
|
9529
9575
|
import_winston2.format.errors({
|
|
@@ -9548,7 +9594,10 @@ ${colorize(colors.red, stack)}`;
|
|
|
9548
9594
|
consoleTransport
|
|
9549
9595
|
]
|
|
9550
9596
|
};
|
|
9551
|
-
const mergedOptions =
|
|
9597
|
+
const mergedOptions = config?.loggerOptions ? {
|
|
9598
|
+
...defaultOptions,
|
|
9599
|
+
...config.loggerOptions
|
|
9600
|
+
} : defaultOptions;
|
|
9552
9601
|
return (0, import_winston2.createLogger)(mergedOptions);
|
|
9553
9602
|
}
|
|
9554
9603
|
};
|
|
@@ -9598,6 +9647,15 @@ var HttpLoggingInterceptor = class {
|
|
|
9598
9647
|
logger;
|
|
9599
9648
|
reflector;
|
|
9600
9649
|
excludedPaths;
|
|
9650
|
+
toErrorMessage(error) {
|
|
9651
|
+
if (error instanceof Error) return error.message;
|
|
9652
|
+
if (typeof error === "string") return error;
|
|
9653
|
+
try {
|
|
9654
|
+
return JSON.stringify(error);
|
|
9655
|
+
} catch {
|
|
9656
|
+
return "unknown error";
|
|
9657
|
+
}
|
|
9658
|
+
}
|
|
9601
9659
|
constructor(logger, reflector, config) {
|
|
9602
9660
|
this.logger = logger;
|
|
9603
9661
|
this.reflector = reflector;
|
|
@@ -9666,7 +9724,7 @@ var HttpLoggingInterceptor = class {
|
|
|
9666
9724
|
},
|
|
9667
9725
|
response: {
|
|
9668
9726
|
durationMs,
|
|
9669
|
-
error:
|
|
9727
|
+
error: this.toErrorMessage(error)
|
|
9670
9728
|
}
|
|
9671
9729
|
}
|
|
9672
9730
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DynamicModule, Module, Provider } from "@nestjs/common";
|
|
2
2
|
import { createLogger, format, transports, LoggerOptions } from "winston";
|
|
3
|
-
import { inspect } from "util";
|
|
3
|
+
import { inspect } from "node:util";
|
|
4
4
|
import { WinstonLoggerProvider } from "./winston.logger.provider";
|
|
5
5
|
import {
|
|
6
6
|
WINSTON_LOGGER,
|
|
@@ -11,6 +11,227 @@ import type { LoggerConfig } from "../../logger.config";
|
|
|
11
11
|
import { DEFAULT_LOG_LEVEL } from "./winston.logger.constants";
|
|
12
12
|
import { buildDefaultObfuscator } from "../../obfuscator";
|
|
13
13
|
|
|
14
|
+
type WritableLogInfo = Record<string, unknown> & {
|
|
15
|
+
level?: unknown;
|
|
16
|
+
message?: unknown;
|
|
17
|
+
timestamp?: unknown;
|
|
18
|
+
requestId?: unknown;
|
|
19
|
+
context?: unknown;
|
|
20
|
+
source?: unknown;
|
|
21
|
+
appName?: unknown;
|
|
22
|
+
appVersion?: unknown;
|
|
23
|
+
lib?: unknown;
|
|
24
|
+
libVersion?: unknown;
|
|
25
|
+
libMethod?: unknown;
|
|
26
|
+
stack?: unknown;
|
|
27
|
+
meta?: unknown;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type MetaLogContext = {
|
|
31
|
+
className?: unknown;
|
|
32
|
+
methodName?: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function asString(value: unknown): string | undefined {
|
|
36
|
+
if (typeof value === "string") return value;
|
|
37
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
38
|
+
return String(value);
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function colorizeText(
|
|
44
|
+
useColors: boolean,
|
|
45
|
+
color: string,
|
|
46
|
+
reset: string,
|
|
47
|
+
text: string,
|
|
48
|
+
): string {
|
|
49
|
+
return useColors ? `${color}${text}${reset}` : text;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fillInfoFromMeta(info: WritableLogInfo): void {
|
|
53
|
+
if (!info.meta || typeof info.meta !== "object") return;
|
|
54
|
+
|
|
55
|
+
const meta = info.meta as Record<string, unknown>;
|
|
56
|
+
const metadataKeys = [
|
|
57
|
+
"requestId",
|
|
58
|
+
"context",
|
|
59
|
+
"source",
|
|
60
|
+
"lib",
|
|
61
|
+
"libVersion",
|
|
62
|
+
"libMethod",
|
|
63
|
+
"appName",
|
|
64
|
+
"appVersion",
|
|
65
|
+
] as const;
|
|
66
|
+
|
|
67
|
+
for (const key of metadataKeys) {
|
|
68
|
+
if (meta[key] && !info[key]) {
|
|
69
|
+
info[key] = meta[key];
|
|
70
|
+
delete meta[key];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const logContext = meta.logContext as MetaLogContext | undefined;
|
|
75
|
+
if (!logContext || info.source) return;
|
|
76
|
+
|
|
77
|
+
const className = asString(logContext.className);
|
|
78
|
+
const methodName = asString(logContext.methodName);
|
|
79
|
+
|
|
80
|
+
if (className && methodName) {
|
|
81
|
+
info.source = `${className}.${methodName}`;
|
|
82
|
+
} else if (className) {
|
|
83
|
+
info.source = className;
|
|
84
|
+
} else if (methodName) {
|
|
85
|
+
info.source = methodName;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function applyDefaultInfoValues(
|
|
90
|
+
info: WritableLogInfo,
|
|
91
|
+
config?: LoggerConfig,
|
|
92
|
+
): void {
|
|
93
|
+
info.requestId = info.requestId || "no-request-id";
|
|
94
|
+
info.context = info.context || config?.context || "App";
|
|
95
|
+
info.appName = info.appName || config?.appName;
|
|
96
|
+
info.appVersion = info.appVersion || config?.appVersion;
|
|
97
|
+
info.lib = info.lib || config?.lib;
|
|
98
|
+
info.libVersion = info.libVersion || config?.libVersion;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildMethodDisplays(params: {
|
|
102
|
+
context: string;
|
|
103
|
+
source?: string;
|
|
104
|
+
lib?: string;
|
|
105
|
+
libMethod?: string;
|
|
106
|
+
colorize: (text: string) => string;
|
|
107
|
+
}): { sourceDisplay: string; libMethodDisplay: string } {
|
|
108
|
+
const { context, source, lib, libMethod, colorize } = params;
|
|
109
|
+
|
|
110
|
+
let sourceDisplay = "";
|
|
111
|
+
let libMethodDisplay = "";
|
|
112
|
+
|
|
113
|
+
if (source) {
|
|
114
|
+
sourceDisplay = `[${colorize(source)}]`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (lib) {
|
|
118
|
+
const methodPath = libMethod ? `${context}.${libMethod}` : context;
|
|
119
|
+
libMethodDisplay = `[${colorize(methodPath)}]`;
|
|
120
|
+
if (source === context || source === methodPath) {
|
|
121
|
+
sourceDisplay = "";
|
|
122
|
+
}
|
|
123
|
+
return { sourceDisplay, libMethodDisplay };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!source) {
|
|
127
|
+
libMethodDisplay = `[${colorize(context)}]`;
|
|
128
|
+
return { sourceDisplay, libMethodDisplay };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (source.startsWith(`${context}.`) || source === context) {
|
|
132
|
+
libMethodDisplay = `[${colorize(source)}]`;
|
|
133
|
+
sourceDisplay = "";
|
|
134
|
+
return { sourceDisplay, libMethodDisplay };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
libMethodDisplay = `[${colorize(context)}]`;
|
|
138
|
+
sourceDisplay = `[${colorize(source)}]`;
|
|
139
|
+
return { sourceDisplay, libMethodDisplay };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function formatDevelopmentLog(
|
|
143
|
+
infoInput: WritableLogInfo,
|
|
144
|
+
useColors: boolean,
|
|
145
|
+
levelColorizer: ReturnType<typeof format.colorize>,
|
|
146
|
+
): string {
|
|
147
|
+
const info = infoInput;
|
|
148
|
+
const level = asString(info.level) ?? "info";
|
|
149
|
+
const message = asString(info.message) ?? "";
|
|
150
|
+
const timestamp = asString(info.timestamp) ?? "";
|
|
151
|
+
const requestId = asString(info.requestId) ?? "no-request-id";
|
|
152
|
+
const context = asString(info.context) ?? "App";
|
|
153
|
+
const source = asString(info.source);
|
|
154
|
+
const appName = asString(info.appName);
|
|
155
|
+
const appVersion = asString(info.appVersion);
|
|
156
|
+
const lib = asString(info.lib);
|
|
157
|
+
const libMethod = asString(info.libMethod);
|
|
158
|
+
const libVersion = asString(info.libVersion);
|
|
159
|
+
const stack = asString(info.stack);
|
|
160
|
+
|
|
161
|
+
const meta =
|
|
162
|
+
info.meta && typeof info.meta === "object"
|
|
163
|
+
? (info.meta as Record<string, unknown>)
|
|
164
|
+
: undefined;
|
|
165
|
+
|
|
166
|
+
const colors = {
|
|
167
|
+
reset: "\x1b[0m",
|
|
168
|
+
gray: "\x1b[90m",
|
|
169
|
+
cyan: "\x1b[36m",
|
|
170
|
+
magenta: "\x1b[35m",
|
|
171
|
+
yellow: "\x1b[33m",
|
|
172
|
+
red: "\x1b[31m",
|
|
173
|
+
green: "\x1b[32m",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const coloredLevel = useColors
|
|
177
|
+
? levelColorizer.colorize(level, level.toUpperCase())
|
|
178
|
+
: level.toUpperCase();
|
|
179
|
+
|
|
180
|
+
const coloredTime = colorizeText(
|
|
181
|
+
useColors,
|
|
182
|
+
colors.gray,
|
|
183
|
+
colors.reset,
|
|
184
|
+
timestamp,
|
|
185
|
+
);
|
|
186
|
+
const coloredRequestId = colorizeText(
|
|
187
|
+
useColors,
|
|
188
|
+
colors.cyan,
|
|
189
|
+
colors.reset,
|
|
190
|
+
requestId,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
let appDisplay = "";
|
|
194
|
+
if (appName) {
|
|
195
|
+
const appText = appVersion ? `${appName}@${appVersion}` : appName;
|
|
196
|
+
const appLabel = `App-${appText}`;
|
|
197
|
+
appDisplay = `[${colorizeText(useColors, colors.green, colors.reset, appLabel)}]`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let libDisplay = "";
|
|
201
|
+
if (lib) {
|
|
202
|
+
const libText = libVersion ? `${lib}:${libVersion}` : lib;
|
|
203
|
+
libDisplay = `[${colorizeText(useColors, colors.yellow, colors.reset, libText)}]`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { sourceDisplay, libMethodDisplay } = buildMethodDisplays({
|
|
207
|
+
context,
|
|
208
|
+
source,
|
|
209
|
+
lib,
|
|
210
|
+
libMethod,
|
|
211
|
+
colorize: (text) =>
|
|
212
|
+
colorizeText(useColors, colors.magenta, colors.reset, text),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
let output = `${appDisplay}${libDisplay}[${coloredRequestId}][${coloredTime}]${sourceDisplay}${libMethodDisplay}[${coloredLevel}] - ${message}`;
|
|
216
|
+
|
|
217
|
+
if (meta && typeof meta === "object" && Object.keys(meta).length > 0) {
|
|
218
|
+
const inspectedMeta = inspect(meta, {
|
|
219
|
+
colors: useColors,
|
|
220
|
+
depth: null,
|
|
221
|
+
compact: true,
|
|
222
|
+
sorted: true,
|
|
223
|
+
breakLength: Infinity,
|
|
224
|
+
});
|
|
225
|
+
output += ` - ${inspectedMeta}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (stack) {
|
|
229
|
+
output += `\n${colorizeText(useColors, colors.red, colors.reset, stack)}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return output;
|
|
233
|
+
}
|
|
234
|
+
|
|
14
235
|
@Module({})
|
|
15
236
|
export class WinstonImplementationModule {
|
|
16
237
|
static forRoot(config?: LoggerConfig): DynamicModule {
|
|
@@ -91,181 +312,17 @@ export class WinstonImplementationModule {
|
|
|
91
312
|
|
|
92
313
|
// Standard fields we want to ensure are in the log object
|
|
93
314
|
const standardFields = format((info) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
info.requestId = meta.requestId;
|
|
99
|
-
delete meta.requestId;
|
|
100
|
-
}
|
|
101
|
-
if (meta.context && !info.context) {
|
|
102
|
-
info.context = meta.context;
|
|
103
|
-
delete meta.context;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Extract source (used for className.methodName)
|
|
107
|
-
if (meta.source && !info.source) {
|
|
108
|
-
info.source = meta.source;
|
|
109
|
-
delete meta.source;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Support lib identification from metadata
|
|
113
|
-
if (meta.lib && !info.lib) {
|
|
114
|
-
info.lib = meta.lib;
|
|
115
|
-
delete meta.lib;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Support lib version identification from metadata
|
|
119
|
-
if (meta.libVersion && !info.libVersion) {
|
|
120
|
-
info.libVersion = meta.libVersion;
|
|
121
|
-
delete meta.libVersion;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Support lib method identification
|
|
125
|
-
if (meta.libMethod && !info.libMethod) {
|
|
126
|
-
info.libMethod = meta.libMethod;
|
|
127
|
-
delete meta.libMethod;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Support app identification from metadata
|
|
131
|
-
if (meta.appName && !info.appName) {
|
|
132
|
-
info.appName = meta.appName;
|
|
133
|
-
delete meta.appName;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Support app version identification from metadata
|
|
137
|
-
if (meta.appVersion && !info.appVersion) {
|
|
138
|
-
info.appVersion = meta.appVersion;
|
|
139
|
-
delete meta.appVersion;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Support logContext object from some providers
|
|
143
|
-
if (meta.logContext && !info.source) {
|
|
144
|
-
const lc = meta.logContext;
|
|
145
|
-
if (lc.className && lc.methodName) {
|
|
146
|
-
info.source = `${lc.className}.${lc.methodName}`;
|
|
147
|
-
} else if (lc.className) {
|
|
148
|
-
info.source = lc.className;
|
|
149
|
-
} else if (lc.methodName) {
|
|
150
|
-
info.source = lc.methodName;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Default values
|
|
156
|
-
info.requestId = info.requestId || "no-request-id";
|
|
157
|
-
info.context = info.context || config?.context || "App";
|
|
158
|
-
info.appName = info.appName || config?.appName;
|
|
159
|
-
info.appVersion = info.appVersion || config?.appVersion;
|
|
160
|
-
info.lib = info.lib || config?.lib;
|
|
161
|
-
info.libVersion = info.libVersion || config?.libVersion;
|
|
162
|
-
|
|
163
|
-
return info;
|
|
315
|
+
const writableInfo = info as WritableLogInfo;
|
|
316
|
+
fillInfoFromMeta(writableInfo);
|
|
317
|
+
applyDefaultInfoValues(writableInfo, config);
|
|
318
|
+
return writableInfo;
|
|
164
319
|
});
|
|
165
320
|
|
|
166
321
|
const levelColorizer = format.colorize();
|
|
167
322
|
|
|
168
323
|
// Custom format for development (colorful and intuitive)
|
|
169
|
-
const developmentFormat = format.printf(
|
|
170
|
-
(info
|
|
171
|
-
const {
|
|
172
|
-
level, message, timestamp, requestId, context, source,
|
|
173
|
-
meta, stack, appName, appVersion, lib, libMethod, libVersion
|
|
174
|
-
} = info;
|
|
175
|
-
|
|
176
|
-
// Colors (using ANSI codes for precision)
|
|
177
|
-
const colors = {
|
|
178
|
-
reset: "\x1b[0m",
|
|
179
|
-
gray: "\x1b[90m",
|
|
180
|
-
cyan: "\x1b[36m",
|
|
181
|
-
magenta: "\x1b[35m",
|
|
182
|
-
yellow: "\x1b[33m",
|
|
183
|
-
red: "\x1b[31m",
|
|
184
|
-
green: "\x1b[32m",
|
|
185
|
-
bold: "\x1b[1m",
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const colorize = (color: string, text: string) =>
|
|
189
|
-
useColors ? `${color}${text}${colors.reset}` : text;
|
|
190
|
-
|
|
191
|
-
// No trailing space for the level itself, but keep it uppercase
|
|
192
|
-
const coloredLevel = useColors
|
|
193
|
-
? levelColorizer.colorize(level, level.toUpperCase())
|
|
194
|
-
: level.toUpperCase();
|
|
195
|
-
|
|
196
|
-
const coloredTime = colorize(colors.gray, timestamp);
|
|
197
|
-
const coloredRequestId = colorize(colors.cyan, requestId);
|
|
198
|
-
|
|
199
|
-
// App identification: [App-example@0.0.3]
|
|
200
|
-
let appDisplay = "";
|
|
201
|
-
if (appName) {
|
|
202
|
-
const appText = appVersion ? `${appName}@${appVersion}` : appName;
|
|
203
|
-
appDisplay = `[${colorize(colors.green, `App-${appText}`)}]`;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Lib identification: [@adatechnology/http-client:0.0.2]
|
|
207
|
-
let libDisplay = "";
|
|
208
|
-
if (lib) {
|
|
209
|
-
const libText = libVersion ? `${lib}:${libVersion}` : lib;
|
|
210
|
-
libDisplay = `[${colorize(colors.yellow, libText)}]`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const mag = colors.magenta;
|
|
214
|
-
|
|
215
|
-
// Context formatting logic:
|
|
216
|
-
// [Source] (Magenta) - From the caller (e.g., HttpClientController.listPokemon)
|
|
217
|
-
// [Context.Method] (Magenta) - From the lib itself (e.g., HttpRedisClient.get)
|
|
218
|
-
let sourceDisplay = "";
|
|
219
|
-
let libMethodDisplay = "";
|
|
220
|
-
|
|
221
|
-
if (source) {
|
|
222
|
-
sourceDisplay = `[${colorize(mag, source)}]`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (lib) {
|
|
226
|
-
// Inside a library log
|
|
227
|
-
const methodPath = libMethod ? `${context}.${libMethod}` : context;
|
|
228
|
-
libMethodDisplay = `[${colorize(mag, methodPath)}]`;
|
|
229
|
-
|
|
230
|
-
// If source is the same as context or methodPath, we can omit it to avoid duplication
|
|
231
|
-
if (source === context || source === methodPath) {
|
|
232
|
-
sourceDisplay = "";
|
|
233
|
-
}
|
|
234
|
-
} else if (!source) {
|
|
235
|
-
// App-only log without source: use context
|
|
236
|
-
libMethodDisplay = `[${colorize(mag, context)}]`;
|
|
237
|
-
} else if (source.startsWith(`${context}.`) || source === context) {
|
|
238
|
-
// App-only log where source is more specific than context: use source only
|
|
239
|
-
libMethodDisplay = `[${colorize(mag, source)}]`;
|
|
240
|
-
sourceDisplay = "";
|
|
241
|
-
} else {
|
|
242
|
-
// App-only log with different context and source
|
|
243
|
-
libMethodDisplay = `[${colorize(mag, context)}]`;
|
|
244
|
-
sourceDisplay = `[${colorize(mag, source)}]`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Header line: [App][Lib][requestId][timestamp][Source][LibMethod][LEVEL]
|
|
248
|
-
let output = `${appDisplay}${libDisplay}[${coloredRequestId}][${coloredTime}]${sourceDisplay}${libMethodDisplay}[${coloredLevel}] - ${message}`;
|
|
249
|
-
|
|
250
|
-
// Meta data (Pretty printed if not empty)
|
|
251
|
-
if (meta && typeof meta === "object" && Object.keys(meta).length > 0) {
|
|
252
|
-
const inspectedMeta = inspect(meta, {
|
|
253
|
-
colors: useColors,
|
|
254
|
-
depth: null,
|
|
255
|
-
compact: true,
|
|
256
|
-
sorted: true,
|
|
257
|
-
breakLength: Infinity,
|
|
258
|
-
});
|
|
259
|
-
output += ` - ${inspectedMeta}`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Error stack trace
|
|
263
|
-
if (stack) {
|
|
264
|
-
output += `\n${colorize(colors.red, stack)}`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return output;
|
|
268
|
-
}
|
|
324
|
+
const developmentFormat = format.printf((info) =>
|
|
325
|
+
formatDevelopmentLog(info as WritableLogInfo, useColors, levelColorizer),
|
|
269
326
|
);
|
|
270
327
|
|
|
271
328
|
const formats = [
|
|
@@ -294,11 +351,9 @@ export class WinstonImplementationModule {
|
|
|
294
351
|
transports: [consoleTransport],
|
|
295
352
|
};
|
|
296
353
|
|
|
297
|
-
const mergedOptions: LoggerOptions =
|
|
298
|
-
{}
|
|
299
|
-
defaultOptions
|
|
300
|
-
config?.loggerOptions || {},
|
|
301
|
-
);
|
|
354
|
+
const mergedOptions: LoggerOptions = config?.loggerOptions
|
|
355
|
+
? { ...defaultOptions, ...config.loggerOptions }
|
|
356
|
+
: defaultOptions;
|
|
302
357
|
|
|
303
358
|
return createLogger(mergedOptions);
|
|
304
359
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Injectable, Inject } from "@nestjs/common";
|
|
2
2
|
import { Logger as WinstonLoggerType } from "winston";
|
|
3
3
|
import {
|
|
4
|
-
type
|
|
4
|
+
type DebugParams,
|
|
5
|
+
type DebugResult,
|
|
6
|
+
type ErrorParams,
|
|
7
|
+
type ErrorResult,
|
|
8
|
+
type InfoParams,
|
|
9
|
+
type InfoResult,
|
|
5
10
|
type LoggerProviderInterface,
|
|
6
11
|
LoggerLevel,
|
|
7
|
-
type
|
|
12
|
+
type WarnParams,
|
|
13
|
+
type WarnResult,
|
|
14
|
+
type WriteLogParams,
|
|
15
|
+
type WriteLogResult,
|
|
8
16
|
} from "../../logger.interface";
|
|
9
17
|
import type { Obfuscator } from "./winston.logger.types";
|
|
10
18
|
import { getContext } from "../../context/async-context.service";
|
|
@@ -20,76 +28,67 @@ export class WinstonLoggerProvider implements LoggerProviderInterface {
|
|
|
20
28
|
@Inject(WINSTON_OBFUSCATOR) private readonly obfuscator?: Obfuscator,
|
|
21
29
|
) {}
|
|
22
30
|
|
|
23
|
-
debug(payload:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
debug(payload: DebugParams): DebugResult {
|
|
32
|
+
this.log({
|
|
33
|
+
level: LoggerLevel.DEBUG,
|
|
34
|
+
payload: {
|
|
35
|
+
...payload,
|
|
36
|
+
context: payload.context || this.context,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
27
39
|
}
|
|
28
40
|
|
|
29
|
-
info(payload:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
info(payload: InfoParams): InfoResult {
|
|
42
|
+
this.log({
|
|
43
|
+
level: LoggerLevel.INFO,
|
|
44
|
+
payload: {
|
|
45
|
+
...payload,
|
|
46
|
+
context: payload.context || this.context,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
warn(payload:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
warn(payload: WarnParams): WarnResult {
|
|
52
|
+
this.log({
|
|
53
|
+
level: LoggerLevel.WARN,
|
|
54
|
+
payload: {
|
|
55
|
+
...payload,
|
|
56
|
+
context: payload.context || this.context,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
error(payload:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
error(payload: ErrorParams): ErrorResult {
|
|
62
|
+
this.log({
|
|
63
|
+
level: LoggerLevel.ERROR,
|
|
64
|
+
payload: {
|
|
65
|
+
...payload,
|
|
66
|
+
context: payload.context || this.context,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
45
69
|
}
|
|
46
70
|
|
|
47
71
|
setContext(context: string): void {
|
|
48
72
|
this.context = context;
|
|
49
73
|
}
|
|
50
74
|
|
|
51
|
-
private
|
|
52
|
-
level: LoggerLevel,
|
|
53
|
-
messageOrPayload: string | LogPayload,
|
|
54
|
-
meta?: Record<string, unknown>,
|
|
55
|
-
context?: string,
|
|
56
|
-
): void {
|
|
57
|
-
let payload: LogPayload;
|
|
58
|
-
|
|
59
|
-
if (typeof messageOrPayload === "string") {
|
|
60
|
-
payload = {
|
|
61
|
-
message: messageOrPayload,
|
|
62
|
-
meta,
|
|
63
|
-
context: context || this.context,
|
|
64
|
-
};
|
|
65
|
-
} else {
|
|
66
|
-
payload = {
|
|
67
|
-
...messageOrPayload,
|
|
68
|
-
context: messageOrPayload.context || context || this.context,
|
|
69
|
-
meta: { ...messageOrPayload.meta, ...meta },
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.log({ level, payload });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private log(params: LogParams) {
|
|
75
|
+
private log(params: WriteLogParams): WriteLogResult {
|
|
77
76
|
const { level, payload } = params;
|
|
78
77
|
// Extract standard fields but keep the rest to pass to Winston
|
|
79
78
|
const { message, context, meta, ...rest } = payload as any;
|
|
80
|
-
|
|
79
|
+
|
|
81
80
|
const messageText = message ?? EMPTY_STRING;
|
|
82
81
|
const messageContext = context ?? this.context;
|
|
83
82
|
const obfuscatedMeta = this.obfuscator ? this.obfuscator(meta) : meta;
|
|
84
83
|
|
|
85
84
|
const requestContext = getContext();
|
|
86
|
-
const requestIdFromContext =
|
|
87
|
-
|
|
85
|
+
const requestIdFromContext = requestContext?.requestId;
|
|
86
|
+
|
|
88
87
|
// Merge everything into a flat info object for Winston
|
|
89
88
|
const logInfo: Record<string, unknown> = {
|
|
90
89
|
...rest,
|
|
91
90
|
context: messageContext,
|
|
92
|
-
requestId: requestIdFromContext ||
|
|
91
|
+
requestId: requestIdFromContext || rest.requestId,
|
|
93
92
|
meta: obfuscatedMeta,
|
|
94
93
|
};
|
|
95
94
|
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
export { LoggerModule } from "./logger.module";
|
|
2
2
|
export { LOGGER_PROVIDER, LOGGER_CONFIG, HTTP_LOGGING_INTERCEPTOR } from "./logger.token";
|
|
3
3
|
export type {
|
|
4
|
+
DebugParams,
|
|
5
|
+
DebugResult,
|
|
6
|
+
ErrorParams,
|
|
7
|
+
ErrorResult,
|
|
8
|
+
InfoParams,
|
|
9
|
+
InfoResult,
|
|
10
|
+
LogParams,
|
|
11
|
+
LogResult,
|
|
4
12
|
LoggerProviderInterface,
|
|
5
|
-
LogPayload,
|
|
6
13
|
LoggerLevel,
|
|
14
|
+
WarnParams,
|
|
15
|
+
WarnResult,
|
|
16
|
+
WriteLogParams,
|
|
17
|
+
WriteLogResult,
|
|
7
18
|
} from "./logger.interface";
|
|
8
19
|
export { RequestContextMiddleware } from "./middleware/request-context.middleware";
|
|
9
20
|
export { HttpLoggingInterceptor } from "./interceptors/http-logging.interceptor";
|
|
@@ -12,6 +12,16 @@ import { EXCLUDE_HTTP_LOGGING_KEY } from "./exclude-http-logging.decorator";
|
|
|
12
12
|
export class HttpLoggingInterceptor implements NestInterceptor {
|
|
13
13
|
private readonly excludedPaths: string[];
|
|
14
14
|
|
|
15
|
+
private toErrorMessage(error: unknown): string {
|
|
16
|
+
if (error instanceof Error) return error.message;
|
|
17
|
+
if (typeof error === "string") return error;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.stringify(error);
|
|
20
|
+
} catch {
|
|
21
|
+
return "unknown error";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
constructor(
|
|
16
26
|
@Inject(LOGGER_PROVIDER)
|
|
17
27
|
private readonly logger: LoggerProviderInterface,
|
|
@@ -99,7 +109,7 @@ export class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
99
109
|
},
|
|
100
110
|
response: {
|
|
101
111
|
durationMs,
|
|
102
|
-
error:
|
|
112
|
+
error: this.toErrorMessage(error),
|
|
103
113
|
},
|
|
104
114
|
},
|
|
105
115
|
});
|
package/src/logger.interface.ts
CHANGED
|
@@ -5,21 +5,37 @@ export enum LoggerLevel {
|
|
|
5
5
|
ERROR = "error",
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export type LogParams = {
|
|
9
9
|
message: string;
|
|
10
10
|
context?: string;
|
|
11
11
|
meta?: Record<string, unknown>;
|
|
12
|
-
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type LogResult = void;
|
|
15
|
+
|
|
16
|
+
export type DebugParams = LogParams;
|
|
17
|
+
export type DebugResult = LogResult;
|
|
18
|
+
|
|
19
|
+
export type InfoParams = LogParams;
|
|
20
|
+
export type InfoResult = LogResult;
|
|
21
|
+
|
|
22
|
+
export type WarnParams = LogParams;
|
|
23
|
+
export type WarnResult = LogResult;
|
|
24
|
+
|
|
25
|
+
export type ErrorParams = LogParams;
|
|
26
|
+
export type ErrorResult = LogResult;
|
|
13
27
|
|
|
14
28
|
export interface LoggerProviderInterface {
|
|
15
|
-
debug(
|
|
16
|
-
info(
|
|
17
|
-
warn(
|
|
18
|
-
error(
|
|
29
|
+
debug(params: DebugParams): DebugResult;
|
|
30
|
+
info(params: InfoParams): InfoResult;
|
|
31
|
+
warn(params: WarnParams): WarnResult;
|
|
32
|
+
error(params: ErrorParams): ErrorResult;
|
|
19
33
|
setContext?(context: string): void;
|
|
20
34
|
}
|
|
21
35
|
|
|
22
|
-
export type
|
|
36
|
+
export type WriteLogParams = {
|
|
23
37
|
level: LoggerLevel;
|
|
24
|
-
payload:
|
|
38
|
+
payload: LogParams;
|
|
25
39
|
};
|
|
40
|
+
|
|
41
|
+
export type WriteLogResult = LogResult;
|
package/src/logger.provider.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Injectable, Inject } from "@nestjs/common";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
DebugParams,
|
|
4
|
+
DebugResult,
|
|
5
|
+
ErrorParams,
|
|
6
|
+
ErrorResult,
|
|
7
|
+
InfoParams,
|
|
8
|
+
InfoResult,
|
|
9
|
+
LoggerProviderInterface,
|
|
10
|
+
WarnParams,
|
|
11
|
+
WarnResult,
|
|
12
|
+
} from "./logger.interface";
|
|
3
13
|
import { WINSTON_LOGGER } from "./implementations/winston/winston.logger.token";
|
|
4
14
|
|
|
5
15
|
@Injectable()
|
|
@@ -9,45 +19,24 @@ export class LoggerProvider implements LoggerProviderInterface {
|
|
|
9
19
|
private readonly implementation: LoggerProviderInterface,
|
|
10
20
|
) {}
|
|
11
21
|
|
|
12
|
-
debug(
|
|
13
|
-
|
|
14
|
-
message: string,
|
|
15
|
-
meta?: Record<string, unknown>,
|
|
16
|
-
context?: string,
|
|
17
|
-
): void;
|
|
18
|
-
debug(...args: unknown[]): void {
|
|
19
|
-
// @ts-ignore - delegate to implementation which supports overloads
|
|
20
|
-
return this.implementation.debug(...(args as any));
|
|
22
|
+
debug(params: DebugParams): DebugResult {
|
|
23
|
+
return this.implementation.debug(params);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
info(
|
|
24
|
-
|
|
25
|
-
info(...args: unknown[]): void {
|
|
26
|
-
// @ts-ignore
|
|
27
|
-
return this.implementation.info(...(args as any));
|
|
26
|
+
info(params: InfoParams): InfoResult {
|
|
27
|
+
return this.implementation.info(params);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
warn(
|
|
31
|
-
|
|
32
|
-
warn(...args: unknown[]): void {
|
|
33
|
-
// @ts-ignore
|
|
34
|
-
return this.implementation.warn(...(args as any));
|
|
30
|
+
warn(params: WarnParams): WarnResult {
|
|
31
|
+
return this.implementation.warn(params);
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
error(
|
|
38
|
-
|
|
39
|
-
message: string,
|
|
40
|
-
meta?: Record<string, unknown>,
|
|
41
|
-
context?: string,
|
|
42
|
-
): void;
|
|
43
|
-
error(...args: unknown[]): void {
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
return this.implementation.error(...(args as any));
|
|
34
|
+
error(params: ErrorParams): ErrorResult {
|
|
35
|
+
return this.implementation.error(params);
|
|
46
36
|
}
|
|
47
37
|
setContext?(context: string): void {
|
|
48
38
|
if (typeof this.implementation.setContext === "function") {
|
|
49
39
|
return this.implementation.setContext(context);
|
|
50
40
|
}
|
|
51
|
-
return;
|
|
52
41
|
}
|
|
53
42
|
}
|