@globalart/nestjs-logger 1.0.4 โ 1.0.6
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/dist/constants/index.d.ts +5 -4
- package/dist/constants/index.js +6 -5
- package/dist/core/http-logger.interceptor.d.ts +2 -1
- package/dist/core/http-logger.interceptor.js +31 -7
- package/dist/core/logger.di-tokens.d.ts +1 -0
- package/dist/core/logger.di-tokens.js +7 -0
- package/dist/core/logger.module.d.ts +2 -1
- package/dist/core/logger.module.js +4 -4
- package/dist/decorators/index.d.ts +4 -3
- package/dist/formatters/base-formatter.d.ts +2 -0
- package/dist/formatters/base-formatter.js +14 -15
- package/dist/formatters/json-formatter.d.ts +2 -0
- package/dist/formatters/json-formatter.js +23 -11
- package/dist/formatters/pino-formatter.js +3 -5
- package/dist/formatters/text-formatter.d.ts +8 -0
- package/dist/formatters/text-formatter.js +31 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/types/index.d.ts +6 -1
- package/package.json +2 -2
- package/README.md +0 -364
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const LOGGER_CONFIG_TOKEN
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
1
|
+
export declare const LOGGER_CONFIG_TOKEN: unique symbol;
|
|
2
|
+
export declare const LOGGER_SERVICE_TOKEN: unique symbol;
|
|
3
|
+
export declare const LOGGER_CONTEXT_METADATA: unique symbol;
|
|
4
|
+
export declare const LOGGER_METADATA_METADATA: unique symbol;
|
|
5
|
+
export declare const LOGGER_EXCLUDE_METADATA: unique symbol;
|
|
5
6
|
export declare const DEFAULT_SENSITIVE_FIELDS: readonly ["password", "pass", "token", "accessToken", "refreshToken", "secret", "key", "apiKey", "authorization", "auth", "credential", "credentials"];
|
|
6
7
|
export declare const COLORS: {
|
|
7
8
|
readonly reset: "\u001B[0m";
|
package/dist/constants/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_LOGGER_CONFIG = exports.COLORS = exports.DEFAULT_SENSITIVE_FIELDS = exports.LOGGER_EXCLUDE_METADATA = exports.LOGGER_METADATA_METADATA = exports.LOGGER_CONTEXT_METADATA = exports.LOGGER_CONFIG_TOKEN = void 0;
|
|
4
|
-
exports.LOGGER_CONFIG_TOKEN = "LOGGER_CONFIG";
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
3
|
+
exports.DEFAULT_LOGGER_CONFIG = exports.COLORS = exports.DEFAULT_SENSITIVE_FIELDS = exports.LOGGER_EXCLUDE_METADATA = exports.LOGGER_METADATA_METADATA = exports.LOGGER_CONTEXT_METADATA = exports.LOGGER_SERVICE_TOKEN = exports.LOGGER_CONFIG_TOKEN = void 0;
|
|
4
|
+
exports.LOGGER_CONFIG_TOKEN = Symbol("LOGGER_CONFIG");
|
|
5
|
+
exports.LOGGER_SERVICE_TOKEN = Symbol("LOGGER_SERVICE");
|
|
6
|
+
exports.LOGGER_CONTEXT_METADATA = Symbol("LOGGER_CONTEXT");
|
|
7
|
+
exports.LOGGER_METADATA_METADATA = Symbol("LOGGER_METADATA");
|
|
8
|
+
exports.LOGGER_EXCLUDE_METADATA = Symbol("LOGGER_EXCLUDE");
|
|
8
9
|
exports.DEFAULT_SENSITIVE_FIELDS = [
|
|
9
10
|
"password",
|
|
10
11
|
"pass",
|
|
@@ -32,7 +32,8 @@ let HttpLoggerInterceptor = class HttpLoggerInterceptor {
|
|
|
32
32
|
intercept(context, next) {
|
|
33
33
|
const request = context.switchToHttp().getRequest();
|
|
34
34
|
const response = context.switchToHttp().getResponse();
|
|
35
|
-
|
|
35
|
+
const requestMethod = this.getRequestMethod(request.method);
|
|
36
|
+
if (this.shouldExcludeRequest(requestMethod, request.url)) {
|
|
36
37
|
return next.handle();
|
|
37
38
|
}
|
|
38
39
|
const isExcluded = this.reflector.getAllAndOverride(constants_1.LOGGER_EXCLUDE_METADATA, [context.getHandler(), context.getClass()]);
|
|
@@ -130,14 +131,37 @@ let HttpLoggerInterceptor = class HttpLoggerInterceptor {
|
|
|
130
131
|
}
|
|
131
132
|
return this.dataSanitizer.sanitize(sanitized);
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
getRequestMethod(method) {
|
|
135
|
+
switch (method.toUpperCase()) {
|
|
136
|
+
case "GET":
|
|
137
|
+
return common_1.RequestMethod.GET;
|
|
138
|
+
case "POST":
|
|
139
|
+
return common_1.RequestMethod.POST;
|
|
140
|
+
case "PUT":
|
|
141
|
+
return common_1.RequestMethod.PUT;
|
|
142
|
+
case "DELETE":
|
|
143
|
+
return common_1.RequestMethod.DELETE;
|
|
144
|
+
case "PATCH":
|
|
145
|
+
return common_1.RequestMethod.PATCH;
|
|
146
|
+
case "OPTIONS":
|
|
147
|
+
return common_1.RequestMethod.OPTIONS;
|
|
148
|
+
case "HEAD":
|
|
149
|
+
return common_1.RequestMethod.HEAD;
|
|
150
|
+
default:
|
|
151
|
+
return common_1.RequestMethod.ALL;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
shouldExcludeRequest(method, path) {
|
|
155
|
+
return this.config.exclude.some((excludeOption) => {
|
|
156
|
+
if (excludeOption.method !== method) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (excludeOption.path.includes("*")) {
|
|
160
|
+
const pattern = excludeOption.path.replace(/\*/g, ".*");
|
|
137
161
|
const regex = new RegExp(`^${pattern}$`);
|
|
138
|
-
return regex.test(
|
|
162
|
+
return regex.test(path);
|
|
139
163
|
}
|
|
140
|
-
return
|
|
164
|
+
return path === excludeOption.path;
|
|
141
165
|
});
|
|
142
166
|
}
|
|
143
167
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const InjectLogger: () => PropertyDecorator & ParameterDecorator;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InjectLogger = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const constants_1 = require("../constants");
|
|
6
|
+
const InjectLogger = () => (0, common_1.Inject)(constants_1.LOGGER_SERVICE_TOKEN);
|
|
7
|
+
exports.InjectLogger = InjectLogger;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DynamicModule } from "@nestjs/common";
|
|
2
|
+
import { ExcludeOption } from "../types";
|
|
2
3
|
export interface LoggerModuleOptions {
|
|
3
4
|
level?: "error" | "warn" | "info" | "debug" | "verbose";
|
|
4
5
|
timestamp?: boolean;
|
|
@@ -6,7 +7,7 @@ export interface LoggerModuleOptions {
|
|
|
6
7
|
context?: string;
|
|
7
8
|
format?: "json" | "text" | "pino";
|
|
8
9
|
sensitiveFields?: string[];
|
|
9
|
-
exclude?:
|
|
10
|
+
exclude?: ExcludeOption[];
|
|
10
11
|
}
|
|
11
12
|
export interface LoggerModuleAsyncOptions {
|
|
12
13
|
useFactory: (...args: any[]) => LoggerModuleOptions | Promise<LoggerModuleOptions>;
|
|
@@ -25,7 +25,7 @@ let LoggerModule = LoggerModule_1 = class LoggerModule {
|
|
|
25
25
|
return {
|
|
26
26
|
module: LoggerModule_1,
|
|
27
27
|
providers,
|
|
28
|
-
exports: [
|
|
28
|
+
exports: [constants_1.LOGGER_SERVICE_TOKEN, http_logger_interceptor_1.HttpLoggerInterceptor],
|
|
29
29
|
global: true,
|
|
30
30
|
};
|
|
31
31
|
}
|
|
@@ -42,7 +42,7 @@ let LoggerModule = LoggerModule_1 = class LoggerModule {
|
|
|
42
42
|
return {
|
|
43
43
|
module: LoggerModule_1,
|
|
44
44
|
providers,
|
|
45
|
-
exports: [
|
|
45
|
+
exports: [constants_1.LOGGER_SERVICE_TOKEN, http_logger_interceptor_1.HttpLoggerInterceptor],
|
|
46
46
|
global: true,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
@@ -75,7 +75,7 @@ let LoggerModule = LoggerModule_1 = class LoggerModule {
|
|
|
75
75
|
inject: [constants_1.LOGGER_CONFIG_TOKEN],
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
provide:
|
|
78
|
+
provide: constants_1.LOGGER_SERVICE_TOKEN,
|
|
79
79
|
useFactory: (config, formatterFactory, writer, contextResolver) => {
|
|
80
80
|
const formatter = formatterFactory.create(config.format, {
|
|
81
81
|
colors: config.colors,
|
|
@@ -98,7 +98,7 @@ let LoggerModule = LoggerModule_1 = class LoggerModule {
|
|
|
98
98
|
return new http_logger_interceptor_1.HttpLoggerInterceptor(logger, dataSanitizer, requestIdGenerator, config, reflector);
|
|
99
99
|
},
|
|
100
100
|
inject: [
|
|
101
|
-
|
|
101
|
+
constants_1.LOGGER_SERVICE_TOKEN,
|
|
102
102
|
data_sanitizer_1.DataSanitizer,
|
|
103
103
|
request_id_generator_1.RequestIdGenerator,
|
|
104
104
|
constants_1.LOGGER_CONFIG_TOKEN,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { LOGGER_CONTEXT_METADATA, LOGGER_METADATA_METADATA, LOGGER_EXCLUDE_METADATA } from "../constants";
|
|
1
2
|
/**
|
|
2
3
|
* Decorator to set logging context for a class or method
|
|
3
4
|
*/
|
|
4
|
-
export declare const LogContext: (context: string) => import("@nestjs/common").CustomDecorator<
|
|
5
|
+
export declare const LogContext: (context: string) => import("@nestjs/common").CustomDecorator<typeof LOGGER_CONTEXT_METADATA>;
|
|
5
6
|
/**
|
|
6
7
|
* Decorator to add metadata to logs
|
|
7
8
|
*/
|
|
8
|
-
export declare const LogMetadata: (metadata: Record<string, unknown>) => import("@nestjs/common").CustomDecorator<
|
|
9
|
+
export declare const LogMetadata: (metadata: Record<string, unknown>) => import("@nestjs/common").CustomDecorator<typeof LOGGER_METADATA_METADATA>;
|
|
9
10
|
/**
|
|
10
11
|
* Decorator to exclude logging for a controller or method
|
|
11
12
|
*/
|
|
12
|
-
export declare const ExcludeLogging: () => import("@nestjs/common").CustomDecorator<
|
|
13
|
+
export declare const ExcludeLogging: () => import("@nestjs/common").CustomDecorator<typeof LOGGER_EXCLUDE_METADATA>;
|
|
@@ -9,4 +9,6 @@ export declare abstract class BaseFormatter implements ILogFormatter {
|
|
|
9
9
|
protected formatTimestamp(timestamp: Date): string;
|
|
10
10
|
protected colorize(text: string, color: keyof typeof COLORS): string;
|
|
11
11
|
protected getColorForLevel(level: string | number): keyof typeof COLORS;
|
|
12
|
+
private getColorForStringLevel;
|
|
13
|
+
private getColorForNumericLevel;
|
|
12
14
|
}
|
|
@@ -16,22 +16,21 @@ class BaseFormatter {
|
|
|
16
16
|
}
|
|
17
17
|
getColorForLevel(level) {
|
|
18
18
|
if (typeof level === "string") {
|
|
19
|
-
|
|
20
|
-
case "error":
|
|
21
|
-
return "red";
|
|
22
|
-
case "warn":
|
|
23
|
-
return "yellow";
|
|
24
|
-
case "info":
|
|
25
|
-
return "green";
|
|
26
|
-
case "debug":
|
|
27
|
-
return "blue";
|
|
28
|
-
case "verbose":
|
|
29
|
-
return "magenta";
|
|
30
|
-
default:
|
|
31
|
-
return "gray";
|
|
32
|
-
}
|
|
19
|
+
return this.getColorForStringLevel(level);
|
|
33
20
|
}
|
|
34
|
-
|
|
21
|
+
return this.getColorForNumericLevel(level);
|
|
22
|
+
}
|
|
23
|
+
getColorForStringLevel(level) {
|
|
24
|
+
const colorMap = {
|
|
25
|
+
error: "red",
|
|
26
|
+
warn: "yellow",
|
|
27
|
+
info: "green",
|
|
28
|
+
debug: "blue",
|
|
29
|
+
verbose: "magenta",
|
|
30
|
+
};
|
|
31
|
+
return colorMap[level] || "gray";
|
|
32
|
+
}
|
|
33
|
+
getColorForNumericLevel(level) {
|
|
35
34
|
if (level >= 50)
|
|
36
35
|
return "red";
|
|
37
36
|
if (level >= 40)
|
|
@@ -11,23 +11,35 @@ const common_1 = require("@nestjs/common");
|
|
|
11
11
|
const base_formatter_1 = require("./base-formatter");
|
|
12
12
|
let JsonFormatter = class JsonFormatter extends base_formatter_1.BaseFormatter {
|
|
13
13
|
format(entry) {
|
|
14
|
-
const logObject =
|
|
14
|
+
const logObject = this.buildLogObject(entry);
|
|
15
|
+
return JSON.stringify(logObject);
|
|
16
|
+
}
|
|
17
|
+
formatHttpRequest(entry) {
|
|
18
|
+
const jsonString = JSON.stringify(entry);
|
|
19
|
+
return this.options.colors
|
|
20
|
+
? this.colorize(jsonString, this.getColorForLevel(entry.level))
|
|
21
|
+
: jsonString;
|
|
22
|
+
}
|
|
23
|
+
buildLogObject(entry) {
|
|
24
|
+
const baseObject = {
|
|
15
25
|
timestamp: this.formatTimestamp(entry.timestamp),
|
|
16
26
|
level: entry.level,
|
|
17
27
|
message: entry.message,
|
|
18
|
-
...(entry.context && { context: entry.context }),
|
|
19
|
-
...(entry.metadata && { metadata: entry.metadata }),
|
|
20
|
-
...(entry.trace && { trace: entry.trace }),
|
|
21
28
|
};
|
|
22
|
-
return
|
|
29
|
+
return this.addOptionalFields(baseObject, entry);
|
|
23
30
|
}
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
addOptionalFields(baseObject, entry) {
|
|
32
|
+
const result = { ...baseObject };
|
|
33
|
+
if (entry.context) {
|
|
34
|
+
result.context = entry.context;
|
|
35
|
+
}
|
|
36
|
+
if (entry.metadata) {
|
|
37
|
+
result.metadata = entry.metadata;
|
|
38
|
+
}
|
|
39
|
+
if (entry.trace) {
|
|
40
|
+
result.trace = entry.trace;
|
|
29
41
|
}
|
|
30
|
-
return
|
|
42
|
+
return result;
|
|
31
43
|
}
|
|
32
44
|
};
|
|
33
45
|
exports.JsonFormatter = JsonFormatter;
|
|
@@ -15,11 +15,9 @@ let PinoFormatter = class PinoFormatter extends base_formatter_1.BaseFormatter {
|
|
|
15
15
|
}
|
|
16
16
|
formatHttpRequest(entry) {
|
|
17
17
|
const jsonString = JSON.stringify(entry);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
return jsonString;
|
|
18
|
+
return this.options.colors
|
|
19
|
+
? this.colorize(jsonString, this.getColorForLevel(entry.level))
|
|
20
|
+
: jsonString;
|
|
23
21
|
}
|
|
24
22
|
};
|
|
25
23
|
exports.PinoFormatter = PinoFormatter;
|
|
@@ -3,4 +3,12 @@ import { LogEntry, HttpRequestLogEntry } from "../types";
|
|
|
3
3
|
export declare class TextFormatter extends BaseFormatter {
|
|
4
4
|
format(entry: LogEntry): string;
|
|
5
5
|
formatHttpRequest(entry: HttpRequestLogEntry): string;
|
|
6
|
+
private buildLogParts;
|
|
7
|
+
private addTimestamp;
|
|
8
|
+
private addLevel;
|
|
9
|
+
private addContext;
|
|
10
|
+
private addMessage;
|
|
11
|
+
private addMetadata;
|
|
12
|
+
private hasMetadata;
|
|
13
|
+
private addTrace;
|
|
6
14
|
}
|
|
@@ -11,32 +11,54 @@ const common_1 = require("@nestjs/common");
|
|
|
11
11
|
const base_formatter_1 = require("./base-formatter");
|
|
12
12
|
let TextFormatter = class TextFormatter extends base_formatter_1.BaseFormatter {
|
|
13
13
|
format(entry) {
|
|
14
|
+
const parts = this.buildLogParts(entry);
|
|
15
|
+
const result = parts.join(" ");
|
|
16
|
+
return entry.trace ? this.addTrace(result, entry.trace) : result;
|
|
17
|
+
}
|
|
18
|
+
formatHttpRequest(entry) {
|
|
19
|
+
return JSON.stringify(entry);
|
|
20
|
+
}
|
|
21
|
+
buildLogParts(entry) {
|
|
14
22
|
const parts = [];
|
|
23
|
+
this.addTimestamp(parts, entry);
|
|
24
|
+
this.addLevel(parts, entry);
|
|
25
|
+
this.addContext(parts, entry);
|
|
26
|
+
this.addMessage(parts, entry);
|
|
27
|
+
this.addMetadata(parts, entry);
|
|
28
|
+
return parts;
|
|
29
|
+
}
|
|
30
|
+
addTimestamp(parts, entry) {
|
|
15
31
|
if (this.options.timestamp) {
|
|
16
32
|
const timestamp = this.colorize(`[${this.formatTimestamp(entry.timestamp)}]`, "gray");
|
|
17
33
|
parts.push(timestamp);
|
|
18
34
|
}
|
|
35
|
+
}
|
|
36
|
+
addLevel(parts, entry) {
|
|
19
37
|
const level = this.colorize(`[${entry.level.toUpperCase()}]`, this.getColorForLevel(entry.level));
|
|
20
38
|
parts.push(level);
|
|
39
|
+
}
|
|
40
|
+
addContext(parts, entry) {
|
|
21
41
|
if (entry.context) {
|
|
22
42
|
const context = this.colorize(`[${entry.context}]`, "cyan");
|
|
23
43
|
parts.push(context);
|
|
24
44
|
}
|
|
45
|
+
}
|
|
46
|
+
addMessage(parts, entry) {
|
|
25
47
|
const message = this.colorize(entry.message, "bright");
|
|
26
48
|
parts.push(message);
|
|
27
|
-
|
|
49
|
+
}
|
|
50
|
+
addMetadata(parts, entry) {
|
|
51
|
+
if (this.hasMetadata(entry)) {
|
|
28
52
|
const metadata = this.colorize(JSON.stringify(entry.metadata), "gray");
|
|
29
53
|
parts.push(metadata);
|
|
30
54
|
}
|
|
31
|
-
let result = parts.join(" ");
|
|
32
|
-
if (entry.trace) {
|
|
33
|
-
const trace = this.colorize(entry.trace, "red");
|
|
34
|
-
result += `\n${trace}`;
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
55
|
}
|
|
38
|
-
|
|
39
|
-
return
|
|
56
|
+
hasMetadata(entry) {
|
|
57
|
+
return Boolean(entry.metadata && Object.keys(entry.metadata).length > 0);
|
|
58
|
+
}
|
|
59
|
+
addTrace(result, trace) {
|
|
60
|
+
const coloredTrace = this.colorize(trace, "red");
|
|
61
|
+
return `${result}\n${coloredTrace}`;
|
|
40
62
|
}
|
|
41
63
|
};
|
|
42
64
|
exports.TextFormatter = TextFormatter;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { LoggerModule, LoggerModuleOptions, LoggerModuleAsyncOptions, } from "./core/logger.module";
|
|
2
|
+
export { InjectLogger } from "./core/logger.di-tokens";
|
|
2
3
|
export { LoggerService } from "./core/logger.service";
|
|
3
4
|
export { HttpLoggerInterceptor } from "./core/http-logger.interceptor";
|
|
4
5
|
export { LogContext, LogMetadata, ExcludeLogging } from "./decorators";
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.ExcludeLogging = exports.LogMetadata = exports.LogContext = exports.HttpLoggerInterceptor = exports.LoggerService = exports.LoggerModule = void 0;
|
|
17
|
+
exports.ExcludeLogging = exports.LogMetadata = exports.LogContext = exports.HttpLoggerInterceptor = exports.LoggerService = exports.InjectLogger = exports.LoggerModule = void 0;
|
|
18
18
|
// Core
|
|
19
19
|
var logger_module_1 = require("./core/logger.module");
|
|
20
20
|
Object.defineProperty(exports, "LoggerModule", { enumerable: true, get: function () { return logger_module_1.LoggerModule; } });
|
|
21
|
+
var logger_di_tokens_1 = require("./core/logger.di-tokens");
|
|
22
|
+
Object.defineProperty(exports, "InjectLogger", { enumerable: true, get: function () { return logger_di_tokens_1.InjectLogger; } });
|
|
21
23
|
var logger_service_1 = require("./core/logger.service");
|
|
22
24
|
Object.defineProperty(exports, "LoggerService", { enumerable: true, get: function () { return logger_service_1.LoggerService; } });
|
|
23
25
|
var http_logger_interceptor_1 = require("./core/http-logger.interceptor");
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RequestMethod } from "@nestjs/common";
|
|
1
2
|
export type LogLevel = "error" | "warn" | "info" | "debug" | "verbose";
|
|
2
3
|
export type LogFormat = "json" | "text" | "pino";
|
|
3
4
|
export declare const LOG_LEVELS: Record<LogLevel, number>;
|
|
@@ -35,6 +36,10 @@ export interface HttpResponse {
|
|
|
35
36
|
readonly statusCode: number;
|
|
36
37
|
readonly headers: Record<string, string>;
|
|
37
38
|
}
|
|
39
|
+
export interface ExcludeOption {
|
|
40
|
+
method: RequestMethod;
|
|
41
|
+
path: string;
|
|
42
|
+
}
|
|
38
43
|
export interface LoggerConfiguration {
|
|
39
44
|
readonly level: LogLevel;
|
|
40
45
|
readonly timestamp: boolean;
|
|
@@ -42,7 +47,7 @@ export interface LoggerConfiguration {
|
|
|
42
47
|
readonly context?: string;
|
|
43
48
|
readonly format: LogFormat;
|
|
44
49
|
readonly sensitiveFields: readonly string[];
|
|
45
|
-
readonly exclude: readonly
|
|
50
|
+
readonly exclude: readonly ExcludeOption[];
|
|
46
51
|
}
|
|
47
52
|
export interface FormatterOptions {
|
|
48
53
|
readonly colors: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@globalart/nestjs-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "A advanced logger for NestJS",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "GlobalArt, Inc"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@nestjs/common": "^11.1.3",
|
|
44
44
|
"@nestjs/core": "^11.1.3",
|
|
45
|
-
"@nestjs/microservices": "11.1.
|
|
45
|
+
"@nestjs/microservices": "11.1.6",
|
|
46
46
|
"@nestjs/swagger": "^11.2.0",
|
|
47
47
|
"@nestjs/testing": "^11.1.3",
|
|
48
48
|
"@nestjs/typeorm": "^11.0.0"
|
package/README.md
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
# @globalart/nestjs-logger
|
|
2
|
-
|
|
3
|
-
A professional logging module for NestJS with clean architecture, strict typing, and extensibility.
|
|
4
|
-
|
|
5
|
-
## ๐ Features
|
|
6
|
-
|
|
7
|
-
- **Clean Architecture** - separation of concerns, SOLID principles
|
|
8
|
-
- **Strict Typing** - full TypeScript type safety
|
|
9
|
-
- **Performance** - optimized architecture with minimal allocations
|
|
10
|
-
- **Extensibility** - easy to add new formatters and transports
|
|
11
|
-
- **Testability** - dependency injection, easy mocking
|
|
12
|
-
- **Automatic Context** - class detection from call stack
|
|
13
|
-
- **HTTP Logging** - detailed request logging with format consistency
|
|
14
|
-
- **Security** - automatic sanitization of sensitive data
|
|
15
|
-
- **Colored Output** - beautiful console logs
|
|
16
|
-
- **Multiple Formats** - Text, JSON, and Pino support
|
|
17
|
-
|
|
18
|
-
## ๐ฆ Installation
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install @globalart/nestjs-logger
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## ๐ฏ Quick Start
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import { Module } from '@nestjs/common';
|
|
28
|
-
import { LoggerModule } from '@globalart/nestjs-logger';
|
|
29
|
-
|
|
30
|
-
@Module({
|
|
31
|
-
imports: [
|
|
32
|
-
LoggerModule.forRoot({
|
|
33
|
-
level: 'info',
|
|
34
|
-
timestamp: true,
|
|
35
|
-
colors: true,
|
|
36
|
-
format: 'text',
|
|
37
|
-
}),
|
|
38
|
-
],
|
|
39
|
-
})
|
|
40
|
-
export class AppModule {}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## ๐ง Configuration
|
|
44
|
-
|
|
45
|
-
### Synchronous Configuration
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
LoggerModule.forRoot({
|
|
49
|
-
level: 'debug',
|
|
50
|
-
timestamp: true,
|
|
51
|
-
colors: true,
|
|
52
|
-
format: 'pino',
|
|
53
|
-
sensitiveFields: ['password', 'secret'],
|
|
54
|
-
exclude: ['/health', '/metrics'],
|
|
55
|
-
})
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Asynchronous Configuration
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
LoggerModule.forRootAsync({
|
|
62
|
-
useFactory: (configService: ConfigService) => ({
|
|
63
|
-
level: configService.get('LOG_LEVEL', 'info'),
|
|
64
|
-
format: configService.get('LOG_FORMAT', 'text'),
|
|
65
|
-
colors: !configService.get('PRODUCTION'),
|
|
66
|
-
sensitiveFields: configService.get('SENSITIVE_FIELDS', []),
|
|
67
|
-
}),
|
|
68
|
-
inject: [ConfigService],
|
|
69
|
-
})
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## ๐ Usage
|
|
73
|
-
|
|
74
|
-
### In Services
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { Injectable } from '@nestjs/common';
|
|
78
|
-
import { LoggerService } from '@globalart/nestjs-logger';
|
|
79
|
-
|
|
80
|
-
@Injectable()
|
|
81
|
-
export class UserService {
|
|
82
|
-
constructor(private readonly logger: LoggerService) {}
|
|
83
|
-
|
|
84
|
-
async createUser(userData: CreateUserDto) {
|
|
85
|
-
this.logger.log({
|
|
86
|
-
message: 'Creating new user',
|
|
87
|
-
metadata: { userId: userData.email }
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const user = await this.userRepository.save(userData);
|
|
92
|
-
this.logger.log({
|
|
93
|
-
message: 'User created successfully',
|
|
94
|
-
metadata: { id: user.id }
|
|
95
|
-
});
|
|
96
|
-
return user;
|
|
97
|
-
} catch (error) {
|
|
98
|
-
this.logger.error({
|
|
99
|
-
message: 'Failed to create user',
|
|
100
|
-
trace: error.stack,
|
|
101
|
-
metadata: { email: userData.email }
|
|
102
|
-
});
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### HTTP Logging
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
import { Controller, UseInterceptors } from '@nestjs/common';
|
|
113
|
-
import { HttpLoggerInterceptor, LogContext } from '@globalart/nestjs-logger';
|
|
114
|
-
|
|
115
|
-
@Controller('users')
|
|
116
|
-
@UseInterceptors(HttpLoggerInterceptor)
|
|
117
|
-
@LogContext('UserController')
|
|
118
|
-
export class UserController {
|
|
119
|
-
// All HTTP requests will be automatically logged
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Global HTTP Logging
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import { Module } from '@nestjs/common';
|
|
127
|
-
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
128
|
-
import { LoggerModule, HttpLoggerInterceptor } from '@globalart/nestjs-logger';
|
|
129
|
-
|
|
130
|
-
@Module({
|
|
131
|
-
imports: [LoggerModule.forRoot()],
|
|
132
|
-
providers: [
|
|
133
|
-
{
|
|
134
|
-
provide: APP_INTERCEPTOR,
|
|
135
|
-
useClass: HttpLoggerInterceptor,
|
|
136
|
-
},
|
|
137
|
-
],
|
|
138
|
-
})
|
|
139
|
-
export class AppModule {}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## ๐จ Output Formats
|
|
143
|
-
|
|
144
|
-
### Text Format (Default)
|
|
145
|
-
```
|
|
146
|
-
[2024-01-15T10:30:45.123Z] [INFO] [UserService] Creating new user {"userId":"123"}
|
|
147
|
-
[2024-01-15T10:30:45.335Z] [INFO] [HttpLogger] GET /users - 200 (12ms)
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### JSON Format
|
|
151
|
-
```json
|
|
152
|
-
{
|
|
153
|
-
"timestamp": "2024-01-15T10:30:45.123Z",
|
|
154
|
-
"level": "info",
|
|
155
|
-
"message": "Creating new user",
|
|
156
|
-
"context": "UserService",
|
|
157
|
-
"metadata": {"userId": "123"}
|
|
158
|
-
}
|
|
159
|
-
{
|
|
160
|
-
"timestamp": "2024-01-15T10:30:45.335Z",
|
|
161
|
-
"level": "info",
|
|
162
|
-
"message": "GET /users - 200 (12ms)",
|
|
163
|
-
"context": "HttpLogger",
|
|
164
|
-
"metadata": {
|
|
165
|
-
"requestId": "req-123",
|
|
166
|
-
"method": "GET",
|
|
167
|
-
"url": "/users",
|
|
168
|
-
"statusCode": 200,
|
|
169
|
-
"responseTime": 12,
|
|
170
|
-
"remoteAddress": "127.0.0.1"
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Pino Format
|
|
176
|
-
```json
|
|
177
|
-
{"level":30,"time":1642247445123,"pid":1234,"hostname":"app-server","req":{"id":"req-123","method":"GET","url":"/users","query":{},"params":{},"headers":{},"remoteAddress":"127.0.0.1"},"res":{"statusCode":200,"headers":{}},"responseTime":12,"msg":"request completed"}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## ๐ฏ API Reference
|
|
181
|
-
|
|
182
|
-
### LoggerService
|
|
183
|
-
|
|
184
|
-
| Method | Description |
|
|
185
|
-
|--------|-------------|
|
|
186
|
-
| `log(options: LogOptions)` | Information message |
|
|
187
|
-
| `error(options: LogOptions)` | Error with trace |
|
|
188
|
-
| `warn(options: LogOptions)` | Warning message |
|
|
189
|
-
| `debug(options: LogOptions)` | Debug information |
|
|
190
|
-
| `verbose(options: LogOptions)` | Verbose logging |
|
|
191
|
-
| `setContext(context: string)` | Set context |
|
|
192
|
-
| `logHttpRequest(entry: HttpRequestLogEntry)` | Log HTTP request (Pino format only) |
|
|
193
|
-
|
|
194
|
-
### LogOptions Interface
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
interface LogOptions {
|
|
198
|
-
message: string;
|
|
199
|
-
context?: string;
|
|
200
|
-
metadata?: Record<string, unknown>;
|
|
201
|
-
trace?: string;
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Decorators
|
|
206
|
-
|
|
207
|
-
- `@LogContext(context: string)` - Set context for class/method
|
|
208
|
-
- `@LogMetadata(metadata: Record<string, unknown>)` - Add metadata to logs
|
|
209
|
-
- `@ExcludeLogging()` - Exclude logging for controller/method
|
|
210
|
-
|
|
211
|
-
### Configuration Options
|
|
212
|
-
|
|
213
|
-
| Option | Type | Default | Description |
|
|
214
|
-
|--------|------|---------|-------------|
|
|
215
|
-
| `level` | `LogLevel` | `'info'` | Logging level |
|
|
216
|
-
| `timestamp` | `boolean` | `true` | Show timestamp |
|
|
217
|
-
| `colors` | `boolean` | `true` | Colored output |
|
|
218
|
-
| `format` | `LogFormat` | `'text'` | Output format |
|
|
219
|
-
| `context` | `string` | `undefined` | Default context |
|
|
220
|
-
| `sensitiveFields` | `string[]` | `[...]` | Fields to sanitize |
|
|
221
|
-
| `exclude` | `string[]` | `[]` | URLs to exclude from HTTP logging |
|
|
222
|
-
|
|
223
|
-
## ๐๏ธ Architecture
|
|
224
|
-
|
|
225
|
-
The package is built on Clean Architecture principles:
|
|
226
|
-
|
|
227
|
-
### Core Components
|
|
228
|
-
|
|
229
|
-
- **LoggerModule** - Main module with configuration
|
|
230
|
-
- **LoggerService** - Primary logging service
|
|
231
|
-
- **HttpLoggerInterceptor** - HTTP request logging interceptor
|
|
232
|
-
|
|
233
|
-
### Formatters
|
|
234
|
-
|
|
235
|
-
- **TextFormatter** - Human-readable text output
|
|
236
|
-
- **JsonFormatter** - Structured JSON output
|
|
237
|
-
- **PinoFormatter** - Pino-compatible JSON output
|
|
238
|
-
|
|
239
|
-
### Utilities
|
|
240
|
-
|
|
241
|
-
- **ContextResolver** - Automatic context detection
|
|
242
|
-
- **DataSanitizer** - Sensitive data sanitization
|
|
243
|
-
- **RequestIdGenerator** - Unique request ID generation
|
|
244
|
-
|
|
245
|
-
### Writers
|
|
246
|
-
|
|
247
|
-
- **ConsoleWriter** - Console output (extensible for other transports)
|
|
248
|
-
|
|
249
|
-
### Contracts & Types
|
|
250
|
-
|
|
251
|
-
- **ILogger** - Logger interface
|
|
252
|
-
- **ILogFormatter** - Formatter interface
|
|
253
|
-
- **ILogWriter** - Writer interface
|
|
254
|
-
- **LogEntry** - Standard log entry structure
|
|
255
|
-
- **HttpRequestLogEntry** - HTTP-specific log entry structure
|
|
256
|
-
|
|
257
|
-
## ๐ Security
|
|
258
|
-
|
|
259
|
-
Automatic sanitization of sensitive fields:
|
|
260
|
-
- `password`, `pass`
|
|
261
|
-
- `token`, `accessToken`, `refreshToken`
|
|
262
|
-
- `secret`, `key`, `apiKey`
|
|
263
|
-
- `authorization`, `auth`
|
|
264
|
-
- `credential`, `credentials`
|
|
265
|
-
|
|
266
|
-
## ๐งช Testing
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
import { LoggerService } from '@globalart/nestjs-logger';
|
|
270
|
-
|
|
271
|
-
describe('UserService', () => {
|
|
272
|
-
let service: UserService;
|
|
273
|
-
let logger: jest.Mocked<LoggerService>;
|
|
274
|
-
|
|
275
|
-
beforeEach(() => {
|
|
276
|
-
logger = {
|
|
277
|
-
log: jest.fn(),
|
|
278
|
-
error: jest.fn(),
|
|
279
|
-
warn: jest.fn(),
|
|
280
|
-
debug: jest.fn(),
|
|
281
|
-
verbose: jest.fn(),
|
|
282
|
-
setContext: jest.fn(),
|
|
283
|
-
logHttpRequest: jest.fn(),
|
|
284
|
-
} as any;
|
|
285
|
-
|
|
286
|
-
service = new UserService(logger);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('should log user creation', () => {
|
|
290
|
-
service.createUser(userData);
|
|
291
|
-
expect(logger.log).toHaveBeenCalledWith({
|
|
292
|
-
message: 'Creating new user',
|
|
293
|
-
metadata: { userId: userData.email }
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
## ๐ Migration from v1
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
// v1 (old API)
|
|
303
|
-
import { LoggerModule, LoggerInterceptor } from '@globalart/nestjs-logger';
|
|
304
|
-
|
|
305
|
-
// v2 (new API)
|
|
306
|
-
import { LoggerModule, HttpLoggerInterceptor } from '@globalart/nestjs-logger';
|
|
307
|
-
|
|
308
|
-
// Legacy exports available for compatibility:
|
|
309
|
-
import { LegacyLoggerModule, LoggerInterceptor } from '@globalart/nestjs-logger';
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
## ๐ Performance
|
|
313
|
-
|
|
314
|
-
- Minimal allocations in hot paths
|
|
315
|
-
- Lazy formatter initialization
|
|
316
|
-
- Optimized context resolution
|
|
317
|
-
- Efficient data sanitization
|
|
318
|
-
- Format-specific HTTP logging optimization
|
|
319
|
-
|
|
320
|
-
## ๐จ Customization
|
|
321
|
-
|
|
322
|
-
### Custom Formatter
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
import { Injectable } from '@nestjs/common';
|
|
326
|
-
import { BaseFormatter } from '@globalart/nestjs-logger';
|
|
327
|
-
|
|
328
|
-
@Injectable()
|
|
329
|
-
export class CustomFormatter extends BaseFormatter {
|
|
330
|
-
format(entry: LogEntry): string {
|
|
331
|
-
return `[${entry.level.toUpperCase()}] ${entry.message}`;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
formatHttpRequest(entry: HttpRequestLogEntry): string {
|
|
335
|
-
return JSON.stringify(entry);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### Custom Writer
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
import { Injectable } from '@nestjs/common';
|
|
344
|
-
import { ILogWriter } from '@globalart/nestjs-logger';
|
|
345
|
-
|
|
346
|
-
@Injectable()
|
|
347
|
-
export class FileWriter implements ILogWriter {
|
|
348
|
-
write(formattedLog: string): void {
|
|
349
|
-
// Write to file
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
## ๐ค Contributing
|
|
355
|
-
|
|
356
|
-
1. Fork the repository
|
|
357
|
-
2. Create a feature branch
|
|
358
|
-
3. Make your changes
|
|
359
|
-
4. Add tests
|
|
360
|
-
5. Create a Pull Request
|
|
361
|
-
|
|
362
|
-
## ๐ License
|
|
363
|
-
|
|
364
|
-
MIT License
|