@globalart/nestjs-logger 1.0.4 → 1.0.5
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/core/http-logger.interceptor.d.ts +2 -1
- package/dist/core/http-logger.interceptor.js +31 -7
- package/dist/core/logger.module.d.ts +2 -1
- 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/types/index.d.ts +6 -1
- package/package.json +2 -2
- package/README.md +0 -364
|
@@ -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
|
};
|
|
@@ -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>;
|
|
@@ -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/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.5",
|
|
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
|