nest-logger-bundle 0.1.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/LICENSE +201 -0
- package/README.md +563 -0
- package/dist/lib/bundle/context/async-logger-context.service.d.ts +19 -0
- package/dist/lib/bundle/context/async-logger-context.service.js +101 -0
- package/dist/lib/bundle/context/async-logger-context.service.js.map +1 -0
- package/dist/lib/bundle/context/async-logger.hook.d.ts +10 -0
- package/dist/lib/bundle/context/async-logger.hook.js +14 -0
- package/dist/lib/bundle/context/async-logger.hook.js.map +1 -0
- package/dist/lib/bundle/context/logger.definitions.d.ts +4 -0
- package/dist/lib/bundle/context/logger.definitions.js +3 -0
- package/dist/lib/bundle/context/logger.definitions.js.map +1 -0
- package/dist/lib/bundle/dummy-logger.d.ts +18 -0
- package/dist/lib/bundle/dummy-logger.js +16 -0
- package/dist/lib/bundle/dummy-logger.js.map +1 -0
- package/dist/lib/bundle/index.d.ts +10 -0
- package/dist/lib/bundle/index.js +27 -0
- package/dist/lib/bundle/index.js.map +1 -0
- package/dist/lib/bundle/logger-branch/branch.utils.d.ts +10 -0
- package/dist/lib/bundle/logger-branch/branch.utils.js +32 -0
- package/dist/lib/bundle/logger-branch/branch.utils.js.map +1 -0
- package/dist/lib/bundle/logger-branch/logger-branch.d.ts +24 -0
- package/dist/lib/bundle/logger-branch/logger-branch.js +91 -0
- package/dist/lib/bundle/logger-branch/logger-branch.js.map +1 -0
- package/dist/lib/bundle/logger-branch/logger-leaf.d.ts +19 -0
- package/dist/lib/bundle/logger-branch/logger-leaf.js +27 -0
- package/dist/lib/bundle/logger-branch/logger-leaf.js.map +1 -0
- package/dist/lib/bundle/logger-branch/logger-node.d.ts +7 -0
- package/dist/lib/bundle/logger-branch/logger-node.js +3 -0
- package/dist/lib/bundle/logger-branch/logger-node.js.map +1 -0
- package/dist/lib/bundle/logger-bundle.module.d.ts +2 -0
- package/dist/lib/bundle/logger-bundle.module.js +22 -0
- package/dist/lib/bundle/logger-bundle.module.js.map +1 -0
- package/dist/lib/bundle/logger-bundle.service.d.ts +41 -0
- package/dist/lib/bundle/logger-bundle.service.js +107 -0
- package/dist/lib/bundle/logger-bundle.service.js.map +1 -0
- package/dist/lib/core/filters/logger-exception.filter.d.ts +10 -0
- package/dist/lib/core/filters/logger-exception.filter.js +48 -0
- package/dist/lib/core/filters/logger-exception.filter.js.map +1 -0
- package/dist/lib/core/filters/logger-http.interceptor.d.ts +9 -0
- package/dist/lib/core/filters/logger-http.interceptor.js +57 -0
- package/dist/lib/core/filters/logger-http.interceptor.js.map +1 -0
- package/dist/lib/core/index.d.ts +4 -0
- package/dist/lib/core/index.js +21 -0
- package/dist/lib/core/index.js.map +1 -0
- package/dist/lib/core/middleware/bind-logger.middleware.d.ts +8 -0
- package/dist/lib/core/middleware/bind-logger.middleware.js +32 -0
- package/dist/lib/core/middleware/bind-logger.middleware.js.map +1 -0
- package/dist/lib/core/providers/pino-logger.provider.d.ts +2 -0
- package/dist/lib/core/providers/pino-logger.provider.js +56 -0
- package/dist/lib/core/providers/pino-logger.provider.js.map +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +23 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logger/index.d.ts +2 -0
- package/dist/lib/logger/index.js +19 -0
- package/dist/lib/logger/index.js.map +1 -0
- package/dist/lib/logger/internal-logger.service.d.ts +22 -0
- package/dist/lib/logger/internal-logger.service.js +77 -0
- package/dist/lib/logger/internal-logger.service.js.map +1 -0
- package/dist/lib/logger/logger.service.d.ts +36 -0
- package/dist/lib/logger/logger.service.js +118 -0
- package/dist/lib/logger/logger.service.js.map +1 -0
- package/dist/lib/nest-logger.module-definition.d.ts +6 -0
- package/dist/lib/nest-logger.module-definition.js +16 -0
- package/dist/lib/nest-logger.module-definition.js.map +1 -0
- package/dist/lib/nest-logger.module.d.ts +10 -0
- package/dist/lib/nest-logger.module.js +66 -0
- package/dist/lib/nest-logger.module.js.map +1 -0
- package/dist/lib/nest-logger.params.d.ts +58 -0
- package/dist/lib/nest-logger.params.js +23 -0
- package/dist/lib/nest-logger.params.js.map +1 -0
- package/dist/lib/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="http://nestjs.com/" target="blank"><img src="" width="120" alt="Nest Logo" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
|
|
6
|
+
[travis-url]: https://travis-ci.org/nestjs/nest
|
|
7
|
+
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
|
|
8
|
+
[linux-url]: https://travis-ci.org/nestjs/nest
|
|
9
|
+
|
|
10
|
+
<p align="center">A flexible Logger created to use with <a href="http://nodejs.org" target="blank">Nest.js</a> framework for wrap all logs with an Request or Async context in a bundle.</p>
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@pedrohcdo/nest-logger-bundle"><img src="https://img.shields.io/npm/v/@pedrohcdo/nest-logger-bundle" alt="NPM Version" /></a>
|
|
13
|
+
<a href="https://github.com/pedrohcdo/nest-logger-bundle/blob/master/LICENSE"><img src="https://img.shields.io/github/license/pedrohcdo/nest-logger-bundle" alt="Package License" /></a>
|
|
14
|
+
<a href="https://snyk.io/test/github/pedrohcdo/nest-logger-bundle">
|
|
15
|
+
<img alt="Snyk Vulnerabilities for npm package" src="https://img.shields.io/snyk/vulnerabilities/npm/@pedrohcdo/nest-logger-bundle" />
|
|
16
|
+
</a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
## Description
|
|
20
|
+
|
|
21
|
+
This library made to be used with <a href="http://nodejs.org" target="blank">Nest.js</a> it offers more flexibility for controlling logs in the application. The strongest point is that it offers a way to pack all the logs originating from an request or some asynchronous flow into a single bundle, also later it provides ways to transport the bundles to some cloud observability service.
|
|
22
|
+
|
|
23
|
+
For example, in a request several logs can occur and organizing this later or finding yourself in the middle of so many logs becomes a complicated task, with the LoggerBundle all the logs that occur in that request will be packed in a bundle and this bundle shows exactly the order that these logs were displayed in a tree, you can even create branches of these logs using the `enter()/ exit()` methods as will be explained later. This bundle will include a lot of useful information, such as the request that originated these logs and in the log tree you will be able to see a time profiling telling you how long it took in each branch tree.
|
|
24
|
+
|
|
25
|
+
Don't worry if some function of a service calls other functions of other services that contain another injected LoggerBundle, because it can find itself within the current context of the request, so no matter how many different services interact, the output will be in the same bundle.
|
|
26
|
+
|
|
27
|
+
________________
|
|
28
|
+
|
|
29
|
+
## Internal Dependencies
|
|
30
|
+
|
|
31
|
+
You don't need to install any extra dependencies. Internally this library is also made using some bases that are made on top of the <a href="https://github.com/pinojs/pino" target="blank">pino</a>, but I have plans to expose this dependencies in the future and leave the user free to choose which one to use.
|
|
32
|
+
|
|
33
|
+
________________
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
$ npm i --save @pedrohcdo/nest-logger-bundle
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Samples
|
|
42
|
+
|
|
43
|
+
________________
|
|
44
|
+
|
|
45
|
+
If you want to see some usage examples use this repo <a href="https://github.com/pedrohcdo/nest-logger-bundle-samples" target="blank">NestLoggerBundleSamples</a>, In it you will find some projects with some use cases, the codes are commented for a better understanding.
|
|
46
|
+
|
|
47
|
+
> If you want to see an simple example of how to configure it, see the test project [Example](test).
|
|
48
|
+
|
|
49
|
+
________________
|
|
50
|
+
|
|
51
|
+
## How to use
|
|
52
|
+
|
|
53
|
+
First we need to import the NestLoggerModule module in the module we want to use. Follow the minimum configuration:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { Global, Module } from '@nestjs/common';
|
|
57
|
+
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
|
|
58
|
+
import {
|
|
59
|
+
NestLoggerModule,
|
|
60
|
+
LoggerExceptionFilter,
|
|
61
|
+
LoggerHttpInterceptor
|
|
62
|
+
} from '@pedrohcd/nest-logger';
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
//
|
|
66
|
+
@Global()
|
|
67
|
+
@Module({
|
|
68
|
+
imports: [
|
|
69
|
+
// .. imports
|
|
70
|
+
|
|
71
|
+
NestLoggerModule.forRoot({})
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
providers: [
|
|
75
|
+
{
|
|
76
|
+
provide: APP_FILTER,
|
|
77
|
+
useClass: LoggerExceptionFilter,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
provide: APP_INTERCEPTOR,
|
|
81
|
+
useClass: LoggerHttpInterceptor,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
|
|
85
|
+
exports: [NestLoggerModule /**, ... others exports */],
|
|
86
|
+
})
|
|
87
|
+
export class GlobalModule {}
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
For the LoggerBundle to work correctly, it needs some points to be handled, for that there are two classes that are used to handle requests and errors, they are: `LoggerExceptionFilter` and `LoggerHttpInterceptor`.
|
|
91
|
+
These classes need to be used in the global-scoped filters and interceptors like the example to be work across the whole application. `Remember to provide this filter and interceptor as in the example above in a global module or in the main module of your application.`
|
|
92
|
+
|
|
93
|
+
> If you already have a global scope filter or interceptor, follow the [tutorial](#custom-filter-and-interceptor)
|
|
94
|
+
|
|
95
|
+
### Injecting LoggerBundle
|
|
96
|
+
|
|
97
|
+
To inject the Logger in some injectable service of your project follow the example below
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
@Injectable()
|
|
101
|
+
export class SampleUserService {
|
|
102
|
+
|
|
103
|
+
constructor(
|
|
104
|
+
private logService: NestLoggerService
|
|
105
|
+
) {
|
|
106
|
+
this.logService.setContextToken(SampleService.name)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async findUserByEmail(email: string){
|
|
110
|
+
this.logService.enter('finding user by email ', email)
|
|
111
|
+
this.logService.log('finding...')
|
|
112
|
+
// ....
|
|
113
|
+
this.logService.exit()
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private async saveUser(email: string, username: string){
|
|
119
|
+
this.logService.enter('creating user', email, username)
|
|
120
|
+
|
|
121
|
+
this.logService.log('checking if the user already exists...')
|
|
122
|
+
const user = await this.findUserByEmail(email);
|
|
123
|
+
|
|
124
|
+
if(!user) {
|
|
125
|
+
// create user ....
|
|
126
|
+
this.logService.log('user created %s %s', email, username)
|
|
127
|
+
} else {
|
|
128
|
+
this.logService.log('A user with that email already exists')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ...
|
|
132
|
+
this.logService.exit()
|
|
133
|
+
|
|
134
|
+
return {}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async createUser(email: string, username: string){
|
|
138
|
+
this.logService.log('log example')
|
|
139
|
+
this.logService.putTag("test", "test 123")
|
|
140
|
+
await this.saveUser(email, username);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
> An important point is that asynchronous function calls without an await allow the flow of a request to end even before everything is executed, in this case read the section [Async Call's](#async-calls)
|
|
146
|
+
|
|
147
|
+
> Remembering that the name of the current service can be acquired by `<Class>.name`, so you can change the name of the context of the LoggerBundle right at the beginning using as shown in the example above: `this.logService.setContextToken(SampleService.name)`
|
|
148
|
+
|
|
149
|
+
If the `SampleUserService.createUser(email, username)` function is called, the log structure that will be generated will be like the example below:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
logs: {
|
|
154
|
+
"profiling": "6ms",
|
|
155
|
+
"name": "root",
|
|
156
|
+
"logs": [
|
|
157
|
+
{
|
|
158
|
+
"level": "info",
|
|
159
|
+
"message": "log example",
|
|
160
|
+
"context": "SampleService"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"profiling": "0ms",
|
|
164
|
+
"name": "creating user",
|
|
165
|
+
"logs": [
|
|
166
|
+
{
|
|
167
|
+
"level": "info",
|
|
168
|
+
"message": "checking if the user with email 'teste@teste.com' already exists...",
|
|
169
|
+
"context": "SampleService"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"profiling": "0ms",
|
|
173
|
+
"name": "finding user by email ",
|
|
174
|
+
"logs": [
|
|
175
|
+
{
|
|
176
|
+
"level": "info",
|
|
177
|
+
"message": "finding...",
|
|
178
|
+
"context": "SampleService"
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"level": "info",
|
|
184
|
+
"message": "user created teste@teste.com Teste 123",
|
|
185
|
+
"context": "SampleService"
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
context: {
|
|
192
|
+
"requestDuration": <duration>,
|
|
193
|
+
"method": "GET",
|
|
194
|
+
"path": "/sample/create-user/teste%40teste.com/Teste%20123",
|
|
195
|
+
"ip": <ip>
|
|
196
|
+
},
|
|
197
|
+
tags: {
|
|
198
|
+
"test": "test 123"
|
|
199
|
+
},
|
|
200
|
+
req: <request object>,
|
|
201
|
+
res: <response object>
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The log will display 5 objects, they are:
|
|
206
|
+
|
|
207
|
+
| Object | Description
|
|
208
|
+
| :--- | :----:
|
|
209
|
+
| `logs` | A bundle containing the entire log tree including a time profiling between each log.
|
|
210
|
+
| `context` | The context in which this log bundle was created, containing information such as api path, method..
|
|
211
|
+
| `tags` | The tags created in this context
|
|
212
|
+
| `req` | The body of the request that originated this bundle
|
|
213
|
+
| `res` | If it is a complete request context here you will be able to see the response of that request
|
|
214
|
+
|
|
215
|
+
The generated bundle follows the following structure, where the `logs` array can contain more log nodes like the example
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
{
|
|
219
|
+
"profiling": number, // Here the overall time is displayed
|
|
220
|
+
"name": "root", // The first branch is always root
|
|
221
|
+
"logs": [
|
|
222
|
+
// Structure of a log
|
|
223
|
+
{
|
|
224
|
+
"level": string,
|
|
225
|
+
"message": string,
|
|
226
|
+
"context": string
|
|
227
|
+
},
|
|
228
|
+
// Structure of an 'enter/ exit' branch
|
|
229
|
+
{
|
|
230
|
+
"profiling": number, // The time this node took to run
|
|
231
|
+
"name": string, // The branch nrame, where it is passed on 'enter(whiteName)'
|
|
232
|
+
"logs": [
|
|
233
|
+
... // Logs of this branch, remembering that it can have as many levels as necessary
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
// ... others logs
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
There are some methods available for use in NestLoggerService, here is a list of them
|
|
242
|
+
|
|
243
|
+
- Log Methods <br/>
|
|
244
|
+
```ts
|
|
245
|
+
/** Trace Level */
|
|
246
|
+
trace(...args)
|
|
247
|
+
|
|
248
|
+
/** Debug Level */
|
|
249
|
+
debug(...args)
|
|
250
|
+
|
|
251
|
+
/** Info Level */
|
|
252
|
+
info(...args)
|
|
253
|
+
|
|
254
|
+
/** Warn Level */
|
|
255
|
+
warn(...args)
|
|
256
|
+
|
|
257
|
+
/** Error Level */
|
|
258
|
+
error(...args)
|
|
259
|
+
|
|
260
|
+
/** Fatal Level */
|
|
261
|
+
fatal(...args)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Where all log levels follow the same argument model, there are thre call combinations, here is an example with `log()` level
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
// The first way is sending a text that can contain special characters for formatting and then the arguments referring to the formatting.
|
|
268
|
+
this.logService.log("message to format %d %d", 10, 20)
|
|
269
|
+
|
|
270
|
+
// The second form precedes an object that will be merged together with the formatted message
|
|
271
|
+
this.logService.log({ example: 'hello' }, "message to format %d %d", 10, 20)
|
|
272
|
+
|
|
273
|
+
// The third form precedes an error object that will be merged together with the formatted message
|
|
274
|
+
this.logService.log(new Error('example'), "message to format %d %d", 10, 20)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- Context Methods <br/>
|
|
278
|
+
|
|
279
|
+
There are also some methods to control the context of the logs in your project, these methods provide a simple and easy way for you to structure the logs using a log tree structure, follow available methods
|
|
280
|
+
|
|
281
|
+
| Method | Description
|
|
282
|
+
| :--- | :----:
|
|
283
|
+
| `enter(`branchName`)` | This method creates a node in the log tree where the '`branchName`' is an string that will be the name of the subtree of logs
|
|
284
|
+
| `exit()` | This method closes the current subtree, remembering that the same amount opened with `enter()` must be closed with `exit()`
|
|
285
|
+
| `putTag(`tagName, tagValue`)` | Where the '`tagName`' and '`tagValue`' are strings. This method adds a tag in the current context, the tags have no direct relation with the `enter()` and `exit()` methods, so regardless of the current state of the tree, the tags will be added separately in the bundle.
|
|
286
|
+
|
|
287
|
+
- Async Methods
|
|
288
|
+
|
|
289
|
+
If you need to make non-blocking asynchronous calls, for example calling an asynchronous function which will also perform logs without giving an `await`, so this can cause loss of logs from this asynchronous function, to solve it use the function below `(For more details read the section `[Async Call's](#async-calls)`)`
|
|
290
|
+
|
|
291
|
+
| Method | Description
|
|
292
|
+
| :--- | :----:
|
|
293
|
+
| `createAsyncLogger()` | Creates an asynchronous LoggerBundle, where the responsibility for transporting the bundle is on your side
|
|
294
|
+
|
|
295
|
+
______
|
|
296
|
+
|
|
297
|
+
## Setting-up
|
|
298
|
+
|
|
299
|
+
The NestLoggerModule provides two ways of configuration, they are:
|
|
300
|
+
|
|
301
|
+
- *Statically Config*<br/>
|
|
302
|
+
If you want to configure it statically, just use
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
NestLoggerModule.forRoot({
|
|
306
|
+
// ... params
|
|
307
|
+
})
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
- *Asynchronously Config*<br/>
|
|
311
|
+
In case you want to pass the settings asynchronously
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
NestLoggerModule.forRootAsync({
|
|
315
|
+
isGlobal: boolean, //
|
|
316
|
+
useFactory: (config: ConfigService): NestLoggerParams => {
|
|
317
|
+
return {
|
|
318
|
+
// ... params
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
inject: [ConfigService],
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
You must provide the desired parameters for the LoggerBundle, the parameters follow the following schema
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
// default config
|
|
329
|
+
{
|
|
330
|
+
pinoStream: {
|
|
331
|
+
type: 'default',
|
|
332
|
+
prettyPrint: {
|
|
333
|
+
disabled: boolean,
|
|
334
|
+
options: pino.PrettyOptions,
|
|
335
|
+
},
|
|
336
|
+
streams: pinoms.Streams,
|
|
337
|
+
timestamp: {
|
|
338
|
+
format: {
|
|
339
|
+
template: string,
|
|
340
|
+
timezone: string,
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
// You can change this
|
|
346
|
+
contextBundle: {
|
|
347
|
+
strategy: {
|
|
348
|
+
onDispatch: NestLoggerDispatchStrategy,
|
|
349
|
+
level: NestLoggerLevelStrategy,
|
|
350
|
+
onError: NestLoggerOnErrorStrategy,
|
|
351
|
+
},
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
```ts
|
|
356
|
+
// custom config
|
|
357
|
+
{
|
|
358
|
+
pinoStream: {
|
|
359
|
+
type: 'custom',
|
|
360
|
+
logger: pino.Logger
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// You can change this
|
|
364
|
+
contextBundle: {
|
|
365
|
+
strategy: {
|
|
366
|
+
onDispatch: NestLoggerDispatchStrategy,
|
|
367
|
+
level: NestLoggerLevelStrategy,
|
|
368
|
+
onError: NestLoggerOnErrorStrategy,
|
|
369
|
+
},
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Below is the description of each parameter
|
|
375
|
+
|
|
376
|
+
- **NestLoggerParams**<br/>
|
|
377
|
+
|
|
378
|
+
| Param | Description
|
|
379
|
+
| :--- | :----:
|
|
380
|
+
| `pinoStream`: NestLoggerParamsPinoStream \| NestLoggerParamsCustomPino | The NestLoggerBundle uses the `pino-multi-stream ` to transport the logs to several different destinations at the same time, for that it is necessary to use the `type: 'default'` so some parameters of `NestLoggerParamsPinoStream` will be provided, but if you choose to use a `type: 'custom'` some parameters of `NestLoggerParamsCustomPino` will be provided and you can use a pin logger configured in your own way.
|
|
381
|
+
| `contextBundle`: NestLoggerParamsContextBundle | Here you can configure some behaviors related to how the bundle is created, for example, configure what the bundle's marjoritary level will be..
|
|
382
|
+
|
|
383
|
+
- **NestLoggerParamsPinoStream**<br/>
|
|
384
|
+
If you choose to use the default configuration in `NestLoggerParams`, using '`{ type: 'default', ... }`' the options for these parameters will be provided
|
|
385
|
+
> It is worth remembering that it is recommended to use this configuration if you do not have the need to create your own configuration.
|
|
386
|
+
|
|
387
|
+
| Param | Description
|
|
388
|
+
| :--- | :----:
|
|
389
|
+
| `type: 'default'` | For the options to follow this pattern you must set the type to `'default'`
|
|
390
|
+
| `prettyPrint`: NestLoggerParamsPrettyStream | Here you can configure `prettyStream`, choosing to disable it if necessary and also provide your `pin.PrettyOptions`
|
|
391
|
+
| `streams`: pinoms.Streams | You can also tell which streams you want pinoms handles, you can find implementations of various transporters that can be used here https://github.com/pinojs/pino/blob/master/docs/transports.md#legacy
|
|
392
|
+
| `timestamp`: NestLoggerParamsPinoTimestamp | You can also configure how the timestamp will be formatted in the logs informing a template and a timezone, the template is created with the help of `dayjs` to assemble the desired string you can use the symbols informed here https://day.js.org/docs/en/display/format
|
|
393
|
+
|
|
394
|
+
- **NestLoggerParamsPrettyStream**<br/>
|
|
395
|
+
|
|
396
|
+
| Param | Description
|
|
397
|
+
| :--- | :----:
|
|
398
|
+
| `disabled`: boolean | If you want to disable the `prettyStream` you can pass `false` in this option `(remembering that, as it will be disabled the 'options' will not have any effects)`
|
|
399
|
+
| `options`: pino.PrettyOptions | Here you can pass some options provided by `pin`, like `{colorize: true}`
|
|
400
|
+
|
|
401
|
+
- **pinoms.Streams**<br/>
|
|
402
|
+
|
|
403
|
+
Here is an example of how to use a transport `(In this example, datadog is used)`
|
|
404
|
+
> To find more transporters, have a look at the pino repository in this section [Legacy](https://github.com/pinojs/pino/blob/master/docs/transports.md#legacy)
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import datadog from 'pino-datadog';
|
|
408
|
+
|
|
409
|
+
// ...
|
|
410
|
+
NestLoggerModule.forRootAsync({
|
|
411
|
+
useFactory: async (config: ConfigService): Promise<NestLoggerParams> => {
|
|
412
|
+
const datadogStream = await datadog.createWriteStream({
|
|
413
|
+
apiKey: config.get('datadog.apiKey'),
|
|
414
|
+
service: config.get('datadog.serviceName'),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
// ...
|
|
419
|
+
pinoStream: {
|
|
420
|
+
type: 'default',
|
|
421
|
+
streams: [
|
|
422
|
+
{
|
|
423
|
+
stream: datadogStream,
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
inject: [ConfigService],
|
|
430
|
+
}),
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
- **NestLoggerParamsPinoTimestamp**<br/>
|
|
435
|
+
|
|
436
|
+
| Param | Description
|
|
437
|
+
| :--- | :----:
|
|
438
|
+
| `template`: string | To format the timezone your way, use a string that follows the pattern informed here [dayjs-formar](https://day.js.org/docs/en/display/format), eg: `'DD/MM/YYYY - HH:mm:ss.SSS'`
|
|
439
|
+
| `timezone`: string | Inform the timezone, you can find the valid timezones here [IANA database](https://www.iana.org/time-zones)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
- **NestLoggerParamsCustomPino**<br/>
|
|
444
|
+
But if you choose to use the default configuration in `NestLoggerParamsCustomPino`, using '`{ type: 'custom', ... }`' the options for these parameters will be provided
|
|
445
|
+
|
|
446
|
+
| Param | Description
|
|
447
|
+
| :--- | :----:
|
|
448
|
+
| `type: 'custom'` | For the options to follow this pattern you must set the type to `'custom'`
|
|
449
|
+
| `logger`: pino.Logger | You can pass a logger that was configured your way
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
### Custom Filter and Interceptor
|
|
453
|
+
|
|
454
|
+
If your application is already using a global/ interceptor scope filter, then you will probably have to extend these two classes (`LoggerExceptionFilter`, `LoggerHttpInterceptor`) as follows:
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
// example-global-exception-filter.ts
|
|
458
|
+
|
|
459
|
+
import { ArgumentsHost, Catch } from '@nestjs/common';
|
|
460
|
+
import { LoggerExceptionFilter } from '@pedrohcd/nest-logger';
|
|
461
|
+
|
|
462
|
+
@Catch()
|
|
463
|
+
export class GlobalExceptionFilter extends LoggerExceptionFilter {
|
|
464
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
465
|
+
// Your treatment
|
|
466
|
+
super.catch(exception, host);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
// example-http-interceptor.ts
|
|
474
|
+
|
|
475
|
+
import { CallHandler, Catch, ExecutionContext } from '@nestjs/common';
|
|
476
|
+
import { LoggerHttpInterceptor } from '@pedrohcd/nest-logger';
|
|
477
|
+
import { Observable } from 'rxjs';
|
|
478
|
+
|
|
479
|
+
@Injectable()
|
|
480
|
+
export class GlobalInterceptor extends LoggerHttpInterceptor {
|
|
481
|
+
intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
|
|
482
|
+
// Your treatment
|
|
483
|
+
return super.intercept(context, next);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
// example.ts
|
|
490
|
+
// ...
|
|
491
|
+
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
|
|
492
|
+
import { GlobalExceptionFilter } from './example-global-exception-filter.ts'
|
|
493
|
+
import { GlobalInterceptor } from './example-http-interceptor.ts'
|
|
494
|
+
|
|
495
|
+
//
|
|
496
|
+
@Module({
|
|
497
|
+
|
|
498
|
+
// ...
|
|
499
|
+
providers: [
|
|
500
|
+
{
|
|
501
|
+
provide: APP_FILTER,
|
|
502
|
+
useClass: GlobalExceptionFilter,
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
provide: APP_INTERCEPTOR,
|
|
506
|
+
useClass: GlobalInterceptor,
|
|
507
|
+
},
|
|
508
|
+
],
|
|
509
|
+
|
|
510
|
+
// ..
|
|
511
|
+
})
|
|
512
|
+
// ..
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Async Call's
|
|
516
|
+
|
|
517
|
+
In case you need to call some asynchronous function and not block the execution with `await` this can create a point of failure for the `LoggerBundle`, this failure is not serious but it can create confusion when interpreting the logs, this happens because a request that originated this call can end before the async function finishes, so when the request is finished the `LoggerBundle` assembles a bundle and transports it, so the async call that can still be loose and calling logging in will not be packaged in the same bundle, these logs they would be lost. For this there is a function that creates an asynchronous `LoggerBundle` and transfers you the responsibility of transporting the log at the end of the asynchronous flow. An example of usage is shown below
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
import { AsyncLoggerService, NestLoggerService } from '@pedrohcd/nest-logger';
|
|
521
|
+
|
|
522
|
+
@Injectable()
|
|
523
|
+
export class SampleUserService {
|
|
524
|
+
|
|
525
|
+
constructor(
|
|
526
|
+
private logService: NestLoggerService
|
|
527
|
+
) {
|
|
528
|
+
this.logService.setContextToken(SampleService.name)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private async saveUser(email: string, username: string){
|
|
532
|
+
// Here a new LoggerBundle will be created from the context of the request
|
|
533
|
+
const asyncLogger: AsyncLoggerService = await this.logService.createAsyncLogger()
|
|
534
|
+
|
|
535
|
+
// codes....
|
|
536
|
+
|
|
537
|
+
asyncLogger.log('async logs example')
|
|
538
|
+
|
|
539
|
+
// Dispatch this loger bundle (so it can be transported, eg: to console)
|
|
540
|
+
asyncLogger.dispatch("dispatch message")
|
|
541
|
+
|
|
542
|
+
return {}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async createUser(email: string, username: string){
|
|
546
|
+
this.logService.log('log example')
|
|
547
|
+
|
|
548
|
+
/** Non-blocking, no 'await' */
|
|
549
|
+
// This makes it possible for the 'createUser' function to finish before the 'saveUser' function call finishes, so the logs that will happen in the 'saveUser' function can be lost.
|
|
550
|
+
this.saveUser(email, username);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
## Stay in touch
|
|
558
|
+
|
|
559
|
+
- Author - [Pedro Henrique C.](https://github.com/pedrohcdo)
|
|
560
|
+
|
|
561
|
+
## License
|
|
562
|
+
|
|
563
|
+
NestLoggerBundle is [MIT licensed](LICENSE).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ModuleRef } from '@nestjs/core';
|
|
2
|
+
import pino from 'pino';
|
|
3
|
+
import { NestLoggerParams } from '../../nest-logger.params';
|
|
4
|
+
export declare class NestAsyncLoggerContext {
|
|
5
|
+
private params;
|
|
6
|
+
private streamLogger;
|
|
7
|
+
private moduleRef;
|
|
8
|
+
private detachedContext;
|
|
9
|
+
constructor(params: NestLoggerParams, streamLogger: pino.Logger, moduleRef: ModuleRef);
|
|
10
|
+
getCurrent(): {
|
|
11
|
+
logger: import("pino").Logger<import("pino").LoggerOptions>;
|
|
12
|
+
reqId: any;
|
|
13
|
+
loggerBundle: any;
|
|
14
|
+
};
|
|
15
|
+
dispatchCurrentLoggerBundle(message: string): void;
|
|
16
|
+
dispatchCurrentLoggerBundle(innerObject: unknown, message?: string): void;
|
|
17
|
+
hasContext(): boolean;
|
|
18
|
+
createDetachedContext(): Promise<NestAsyncLoggerContext>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
var NestAsyncLoggerContext_1;
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.NestAsyncLoggerContext = void 0;
|
|
20
|
+
const common_1 = require("@nestjs/common");
|
|
21
|
+
const core_1 = require("@nestjs/core");
|
|
22
|
+
const pino_1 = __importDefault(require("pino"));
|
|
23
|
+
const nest_logger_module_definition_1 = require("../../nest-logger.module-definition");
|
|
24
|
+
const nest_logger_params_1 = require("../../nest-logger.params");
|
|
25
|
+
const logger_bundle_service_1 = require("../logger-bundle.service");
|
|
26
|
+
const async_logger_hook_1 = require("./async-logger.hook");
|
|
27
|
+
let NestAsyncLoggerContext = NestAsyncLoggerContext_1 = class NestAsyncLoggerContext {
|
|
28
|
+
constructor(params, streamLogger, moduleRef) {
|
|
29
|
+
this.params = params;
|
|
30
|
+
this.streamLogger = streamLogger;
|
|
31
|
+
this.moduleRef = moduleRef;
|
|
32
|
+
}
|
|
33
|
+
getCurrent() {
|
|
34
|
+
if (this.detachedContext)
|
|
35
|
+
return this.detachedContext;
|
|
36
|
+
if (!this.hasContext()) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const fromStore = async_logger_hook_1.NestLoggerStorage.getStore();
|
|
40
|
+
return {
|
|
41
|
+
logger: fromStore.logger,
|
|
42
|
+
reqId: fromStore.reqId,
|
|
43
|
+
loggerBundle: fromStore.loggerContext,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
dispatchCurrentLoggerBundle(innerObject, message) {
|
|
47
|
+
var _a, _b, _c;
|
|
48
|
+
if (!this.hasContext()) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const { logger, loggerBundle } = this.getCurrent();
|
|
52
|
+
if (((_c = (_b = (_a = this.params) === null || _a === void 0 ? void 0 : _a.contextBundle) === null || _b === void 0 ? void 0 : _b.strategy) === null || _c === void 0 ? void 0 : _c.onDispatch) === nest_logger_params_1.NestLoggerDispatchStrategy.DISPATCH) {
|
|
53
|
+
const { object, level } = loggerBundle.build();
|
|
54
|
+
const childLogger = logger.child(object);
|
|
55
|
+
if (message)
|
|
56
|
+
childLogger[level](innerObject, message);
|
|
57
|
+
else
|
|
58
|
+
childLogger[level](innerObject);
|
|
59
|
+
childLogger.flush();
|
|
60
|
+
}
|
|
61
|
+
loggerBundle.expireNow();
|
|
62
|
+
}
|
|
63
|
+
hasContext() {
|
|
64
|
+
return !!async_logger_hook_1.NestLoggerStorage.getStore();
|
|
65
|
+
}
|
|
66
|
+
async createDetachedContext() {
|
|
67
|
+
const context = await this.moduleRef.create(NestAsyncLoggerContext_1);
|
|
68
|
+
const detachedLoggerBundle = await this.moduleRef.create(logger_bundle_service_1.NestLoggerBundle);
|
|
69
|
+
let getFrom;
|
|
70
|
+
if (this.detachedContext) {
|
|
71
|
+
getFrom = this.detachedContext;
|
|
72
|
+
}
|
|
73
|
+
else if (this.hasContext()) {
|
|
74
|
+
getFrom = this.getCurrent();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
getFrom = {
|
|
78
|
+
logger: this.streamLogger,
|
|
79
|
+
loggerBundle: null,
|
|
80
|
+
reqId: '<unknown>',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const { logger, loggerBundle, reqId } = getFrom;
|
|
84
|
+
if (loggerBundle)
|
|
85
|
+
detachedLoggerBundle.copyFrom(loggerBundle);
|
|
86
|
+
context.detachedContext = {
|
|
87
|
+
logger,
|
|
88
|
+
loggerBundle: detachedLoggerBundle,
|
|
89
|
+
reqId,
|
|
90
|
+
};
|
|
91
|
+
return context;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
NestAsyncLoggerContext = NestAsyncLoggerContext_1 = __decorate([
|
|
95
|
+
(0, common_1.Injectable)({}),
|
|
96
|
+
__param(0, (0, common_1.Inject)(nest_logger_module_definition_1.MODULE_OPTIONS_TOKEN)),
|
|
97
|
+
__param(1, (0, common_1.Inject)(nest_logger_params_1.PINO_LOGGER_PROVIDER_TOKEN)),
|
|
98
|
+
__metadata("design:paramtypes", [Object, Object, core_1.ModuleRef])
|
|
99
|
+
], NestAsyncLoggerContext);
|
|
100
|
+
exports.NestAsyncLoggerContext = NestAsyncLoggerContext;
|
|
101
|
+
//# sourceMappingURL=async-logger-context.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-logger-context.service.js","sourceRoot":"","sources":["../../../../lib/src/bundle/context/async-logger-context.service.ts"],"names":[],"mappings":"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"}
|