@ddd-ts-kit/pino 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -2
- package/dist/correlation/correlation-mixin.d.ts +7 -0
- package/dist/correlation/correlation-mixin.d.ts.map +1 -0
- package/dist/correlation/correlation-mixin.js +16 -0
- package/dist/correlation/correlation-mixin.js.map +1 -0
- package/dist/create-pino-logger.d.ts +41 -0
- package/dist/create-pino-logger.d.ts.map +1 -0
- package/dist/create-pino-logger.js +58 -0
- package/dist/create-pino-logger.js.map +1 -0
- package/dist/fields/line-fields.d.ts +8 -0
- package/dist/fields/line-fields.d.ts.map +1 -0
- package/dist/fields/line-fields.js +11 -0
- package/dist/fields/line-fields.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/pino-logger.d.ts +36 -0
- package/dist/pino-logger.d.ts.map +1 -0
- package/dist/pino-logger.js +91 -0
- package/dist/pino-logger.js.map +1 -0
- package/dist/redaction/credential-redaction.d.ts +13 -0
- package/dist/redaction/credential-redaction.d.ts.map +1 -0
- package/dist/redaction/credential-redaction.js +16 -0
- package/dist/redaction/credential-redaction.js.map +1 -0
- package/package.json +43 -4
package/README.md
CHANGED
|
@@ -1,3 +1,273 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ddd-ts-kit/pino
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The kernel `Logger` port on [pino](https://getpino.io): JSON lines on the standard output, the
|
|
4
|
+
correlation fields of the work in progress on every line, credentials redacted, a flush for the
|
|
5
|
+
shutdown. Nothing in `domain/` or `application/` imports this package, or logs anything: the
|
|
6
|
+
composition root binds the adapter, and infrastructure, presentation and the aspects write
|
|
7
|
+
through the port.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i @ddd-ts-kit/pino pino
|
|
11
|
+
npm i -D pino-pretty # only for the pretty format
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Written against pino 10. `pino-pretty` 13 is an optional peer: it is loaded only when the
|
|
15
|
+
`pretty` format is asked for.
|
|
16
|
+
|
|
17
|
+
## Composition root
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// platform/observability/create-logger.ts
|
|
21
|
+
import type { ExecutionScope, LogLevel } from '@ddd-ts-kit/kernel';
|
|
22
|
+
import { createPinoLogger, type PinoLogger } from '@ddd-ts-kit/pino';
|
|
23
|
+
import { traceLogFields } from '@ddd-ts-kit/tracing';
|
|
24
|
+
|
|
25
|
+
export interface LogSettings {
|
|
26
|
+
readonly level: LogLevel; // LOG_LEVEL
|
|
27
|
+
readonly format: 'json' | 'pretty'; // LOG_FORMAT
|
|
28
|
+
readonly service: string;
|
|
29
|
+
readonly environment: string;
|
|
30
|
+
readonly scrubMessages: boolean; // LOG_SCRUB_MESSAGES
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createLogger(scope: ExecutionScope, settings: LogSettings): PinoLogger {
|
|
34
|
+
return createPinoLogger({ ...settings, fields: () => traceLogFields(scope) });
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// main.ts
|
|
40
|
+
import { tracing } from '@/platform/tracing.js'; // first
|
|
41
|
+
import { AsyncLocalStorageExecutionScope } from '@ddd-ts-kit/kernel';
|
|
42
|
+
import { createApplication, kernelModule } from '@ddd-ts-kit/di';
|
|
43
|
+
|
|
44
|
+
const scope = new AsyncLocalStorageExecutionScope();
|
|
45
|
+
const logger = createLogger(scope, config.log);
|
|
46
|
+
|
|
47
|
+
const app = createApplication({
|
|
48
|
+
modules: [kernelModule({ executionScope: scope, logger }), ...contexts],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
process.once('SIGTERM', () => void shutdown());
|
|
52
|
+
|
|
53
|
+
async function shutdown(): Promise<void> {
|
|
54
|
+
await app.stop(); // flushes the error reporter, then the logger, last
|
|
55
|
+
await tracing.shutdown();
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`init --logger pino` writes this `create-logger.ts` (it answers a `NoopLogger` when given no
|
|
60
|
+
settings, which is how the tools and the tests stay silent); `platform/lay-foundations.ts`
|
|
61
|
+
calls it once and `composeApplication` hands the logger to `kernelModule({ logger })`.
|
|
62
|
+
|
|
63
|
+
One line of the application, as it is written:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"level":"info","time":"2026-03-01T10:00:00.000Z","service":"shop-api","environment":"production","correlationId":"9f2c","origin":"http","actorId":"user-1","tenantId":"acme","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","context":"shipping","message":"order shipped"}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Correlation.** `fields` is read at each line that is written, through the pino `mixin`: no
|
|
70
|
+
call site passes an id. `traceLogFields(scope)` answers the trace and span ids inside a span,
|
|
71
|
+
and inside the execution scope the correlation id, the causation id, the origin, the actor and
|
|
72
|
+
the tenant. This package does not depend on the tracing package: any `() => LogFields` fits,
|
|
73
|
+
`() => logFieldsOf(scope.current())` included. A function that throws gives no field, never a
|
|
74
|
+
lost line. The fields of the line win over `fields`, which win over those of a child: pino
|
|
75
|
+
writes the bindings of a child first and what the mixin answers after them, so a key given
|
|
76
|
+
by both is written twice and a JSON parser reads the one of `fields`. `StdoutLogger` orders
|
|
77
|
+
them the same way; `@ddd-ts-kit/winston` does not (there a child wins over `fields`). Do not
|
|
78
|
+
bind in a child a key that `fields` answers.
|
|
79
|
+
|
|
80
|
+
**An instance of your own.** The project that already configures pino keeps doing so, and
|
|
81
|
+
takes the pieces it wants:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { CREDENTIAL_KEYS } from '@ddd-ts-kit/kernel';
|
|
85
|
+
import { pino } from 'pino';
|
|
86
|
+
import { PinoLogger, correlationMixin, credentialRedaction } from '@ddd-ts-kit/pino';
|
|
87
|
+
|
|
88
|
+
const instance = pino({
|
|
89
|
+
mixin: correlationMixin(() => traceLogFields(scope)),
|
|
90
|
+
redact: credentialRedaction([...CREDENTIAL_KEYS, 'iban']),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const logger = new PinoLogger(instance);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`createPinoLogger` writes `level` by its name and the message under `message`, like the
|
|
97
|
+
`StdoutLogger` of the aspects package and like `@ddd-ts-kit/winston`: one dashboard reads the
|
|
98
|
+
three. An instance of your own keeps the keys you gave it (`msg` and a numeric level by default).
|
|
99
|
+
|
|
100
|
+
## Development mode
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
createPinoLogger({ level: 'debug', format: 'pretty', fields: () => traceLogFields(scope) });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
[10:00:00.000] INFO: order shipped
|
|
108
|
+
service: "shop-api"
|
|
109
|
+
correlationId: "9f2c"
|
|
110
|
+
context: "shipping"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The `pretty` format is the transport pino documents for `pino-pretty`: it runs in a worker
|
|
114
|
+
thread, so it takes a file descriptor or a path as `destination`, not a stream, and `flush()`
|
|
115
|
+
waits for the worker. For pretty lines on a stream of your own, build the instance on
|
|
116
|
+
`pino-pretty` used as a stream, `pino(options, pretty({ destination }))`, and wrap it in
|
|
117
|
+
`new PinoLogger(instance)`. Keep it out of production: choose the format from configuration
|
|
118
|
+
(`LOG_FORMAT=pretty` in `.env`, `json` everywhere else).
|
|
119
|
+
|
|
120
|
+
## Privacy
|
|
121
|
+
|
|
122
|
+
Log lines are where personal data leaks: they are written everywhere, read by everyone and kept
|
|
123
|
+
for long. Three things are done in the adapter, so that no call site has to remember them. The
|
|
124
|
+
list of credential keys, the description of an error and the two markers (`CREDENTIAL_KEYS`,
|
|
125
|
+
`describeError`, `REDACTED`, `SCRUBBED_MESSAGE`) are those of the kernel, shared by every
|
|
126
|
+
logger of the port.
|
|
127
|
+
|
|
128
|
+
**Credentials are redacted** by pino's own `redact`, in the fields of a line, of a child and of
|
|
129
|
+
`fields` alike. The value of these keys is replaced by `[redacted]`, at the top of a line and
|
|
130
|
+
one level below (`headers.authorization`, `body.password`):
|
|
131
|
+
|
|
132
|
+
`password`, `authorization`, `cookie`, `set-cookie`, `token`, `secret`, `apiKey`, `api_key`,
|
|
133
|
+
`access_token`, `refresh_token`, `accessToken`, `refreshToken`, `client_secret`
|
|
134
|
+
|
|
135
|
+
`redact: ['iban', 'ssn']` adds to the list. Names are matched exactly, case included, and
|
|
136
|
+
nothing is looked for deeper than one level: log the part you mean, not a whole request.
|
|
137
|
+
|
|
138
|
+
**An error passed as a field** (`logger.error('payment failed', { error })`: the `LogValue` of
|
|
139
|
+
the port admits an `Error`) is written as `describeError` of the kernel describes it, the same
|
|
140
|
+
from every logger of the port: its class as `type`, its `code` when
|
|
141
|
+
it is a `DomainError`, its `message`, its own `stack`, then its `cause` the same way, up to
|
|
142
|
+
eight deep. Each error of a chain is written once: pino's standard serializer of the `err` key
|
|
143
|
+
would fold every cause into the message and the stack of the first, so the adapter turns it off
|
|
144
|
+
for the lines that go through the port. Errors are looked for among the fields of the line, not
|
|
145
|
+
inside nested objects.
|
|
146
|
+
|
|
147
|
+
**Messages of unexpected errors can be dropped.** The message of an error is the first place
|
|
148
|
+
where personal data shows up: a driver quotes the row it refused. The default keeps it, because
|
|
149
|
+
a log line that says `[message removed]` costs every investigation. With one option only the
|
|
150
|
+
messages the project wrote itself survive, those of its `DomainError`s; class, code, stack and
|
|
151
|
+
cause remain for the others, and the stack loses its first line, which repeats the message:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
createPinoLogger({ scrubMessages: true }); // or new PinoLogger(instance, { scrubMessages: true })
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This is the `scrubMessages` every logger of the port takes (`StdoutLogger`, pino, winston,
|
|
158
|
+
`InMemoryLogger`) and the one of `@ddd-ts-kit/sentry`, with the same rule, the same
|
|
159
|
+
`SCRUBBED_MESSAGE` and the same trade-off: turn it on when the logs leave your hands or when no
|
|
160
|
+
operator should read that data there. What the message of a log line itself says stays yours:
|
|
161
|
+
write `order shipped` and put the identifiers in fields.
|
|
162
|
+
|
|
163
|
+
The kit itself never hands an error to a logger: the `logged` aspect, `loggingMiddleware` and
|
|
164
|
+
the job scheduler write `errorType` and `errorCode` only (`identifyError` of the kernel), never
|
|
165
|
+
a message, whatever `scrubMessages` says, and the error goes to the `ErrorReporter`. The
|
|
166
|
+
description above is for the error your own code chooses to log:
|
|
167
|
+
`logger.error('payment failed', { error })`.
|
|
168
|
+
|
|
169
|
+
## Levels
|
|
170
|
+
|
|
171
|
+
| Port | pino | |
|
|
172
|
+
|---|---|---|
|
|
173
|
+
| `debug` | `debug` (20) | |
|
|
174
|
+
| `info` | `info` (30) | the default threshold |
|
|
175
|
+
| `warn` | `warn` (40) | |
|
|
176
|
+
| `error` | `error` (50) | |
|
|
177
|
+
|
|
178
|
+
The port has no `trace` and no `fatal`: nothing written through it uses them. A line below the
|
|
179
|
+
threshold costs one comparison: its fields are not read, and `fields` is not called. The
|
|
180
|
+
threshold of an instance changed while running (`instance.level = 'debug'`) is followed by the
|
|
181
|
+
adapter and by its children. `isLevelEnabled(level)` answers that threshold: ask it before
|
|
182
|
+
building fields that cost.
|
|
183
|
+
|
|
184
|
+
`child(fields)` is a pino child: the fields are serialized once, when the child is created,
|
|
185
|
+
and cost nothing per line. A key bound twice (by a child and by its parent, by a child and by
|
|
186
|
+
`fields`, or by any of them and by the line) is written twice on the line, as pino does; a
|
|
187
|
+
JSON parser reads the last one, in this order: `service`, `environment` and `base`, the
|
|
188
|
+
children from the oldest to the closest, `fields`, the line. `level` and `time` are the
|
|
189
|
+
exception: a field of that name is dropped,
|
|
190
|
+
so that no field forges the header of a line.
|
|
191
|
+
|
|
192
|
+
## Flush
|
|
193
|
+
|
|
194
|
+
`flush(): Promise<void>` resolves once what pino buffered was handed to its destination: the
|
|
195
|
+
worker of a transport, or the buffer of an asynchronous destination. It never rejects.
|
|
196
|
+
`app.stop()` calls it, last, after the modules stopped and the error reporter was flushed:
|
|
197
|
+
stopping logs too. Without the application of the di package, call it yourself at that place.
|
|
198
|
+
|
|
199
|
+
The port calls a flush once, at shutdown, and leaves what is logged afterwards unspecified.
|
|
200
|
+
Here the logger stays usable: a line logged after the flush is written like the others, and
|
|
201
|
+
with `asynchronous: true` it waits in the buffer for pino's exit hook.
|
|
202
|
+
|
|
203
|
+
## Performance
|
|
204
|
+
|
|
205
|
+
By default every line is written to the standard output before the call returns. Nothing is
|
|
206
|
+
ever lost, and a slow reader of the standard output slows the application down.
|
|
207
|
+
|
|
208
|
+
`asynchronous: true` buffers 4 KiB and writes off the hot path (`pino.destination({ sync:
|
|
209
|
+
false })`), which is what makes pino fast under load. The price is what a dead process had not
|
|
210
|
+
written yet. `flush()` writes the buffer, and so does pino's own exit hook on `process.exit`
|
|
211
|
+
and on an uncaught exception. A signal that ends the process with no handler (`SIGKILL`, the
|
|
212
|
+
out-of-memory killer, a `SIGTERM` nobody listens to) loses up to 4 KiB of the most recent
|
|
213
|
+
lines, the ones that would have explained it; so does an exit in the first instants, before
|
|
214
|
+
the destination is open. Keep the default unless logging shows up in a profile.
|
|
215
|
+
|
|
216
|
+
A line costs one object copy of its fields (errors described, header keys dropped), then pino:
|
|
217
|
+
about 3 microseconds in all where `@ddd-ts-kit/winston` takes 7 (Node 24, a child, four
|
|
218
|
+
correlation fields, three fields, a sink that discards).
|
|
219
|
+
Redaction is compiled by pino: a lookup per credential key at the top of the line, and one
|
|
220
|
+
wildcard pass for the level below, whose censor looks at the name of each key. A wildcard path
|
|
221
|
+
per key would be the obvious way to write it and costs ten times more: measured on pino 10,
|
|
222
|
+
thirteen of them take a line from about one microsecond to about nine. Log calls never throw: fields that cannot be read or serialized
|
|
223
|
+
(a getter that throws) give the line without them, marked `logError: "unserializable fields"`.
|
|
224
|
+
|
|
225
|
+
## Which one
|
|
226
|
+
|
|
227
|
+
Take `StdoutLogger` of the aspects package when JSON lines on the standard output are all you
|
|
228
|
+
need: no dependency, the same line shape. Take pino when logging volume matters, when the
|
|
229
|
+
framework already speaks pino (Fastify), or when you want its transports. Take
|
|
230
|
+
`@ddd-ts-kit/winston` when the team already runs winston transports and formats. The three
|
|
231
|
+
implement one port and are held by one contract suite: swapping is one line of the composition
|
|
232
|
+
root.
|
|
233
|
+
|
|
234
|
+
## NestJS
|
|
235
|
+
|
|
236
|
+
Nest has its own `LoggerService`, used by the framework for its own messages and by
|
|
237
|
+
`new Logger(context)` in Nest classes. Application code of the kit never needs it: handlers and
|
|
238
|
+
adapters receive the kernel `Logger`. To send what Nest itself logs through the same instance,
|
|
239
|
+
hand it a bridge of a few lines; the kit ships none, since it would bind this package to Nest:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const nest = logger.child({ context: 'nest' });
|
|
243
|
+
const bridge: LoggerService = {
|
|
244
|
+
log: (message: unknown) => nest.info(String(message)),
|
|
245
|
+
warn: (message: unknown) => nest.warn(String(message)),
|
|
246
|
+
error: (message: unknown) => nest.error(String(message)),
|
|
247
|
+
debug: (message: unknown) => nest.debug(String(message)),
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const app = await NestFactory.create(AppModule, { logger: bridge });
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`nestjs-pino` is the alternative when Nest classes log a lot: give it the pino instance you
|
|
254
|
+
built, and wrap the same instance in `new PinoLogger(instance)` for the kit.
|
|
255
|
+
|
|
256
|
+
## In a test
|
|
257
|
+
|
|
258
|
+
Nothing of this package: the test application binds `InMemoryLogger` of the kernel, and its
|
|
259
|
+
`entries` are data. To hold another adapter of the port to the same behaviour, run the contract:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { loggerContract } from '@ddd-ts-kit/testing/contracts';
|
|
263
|
+
|
|
264
|
+
loggerContract({ describe, it, expect, beforeEach }, {
|
|
265
|
+
name: 'PinoLogger',
|
|
266
|
+
createLogger: (setup) => { // { level, fields, scrubMessages }
|
|
267
|
+
const written: string[] = [];
|
|
268
|
+
const logger = createPinoLogger({ ...setup, destination: { write: (line) => written.push(line) } });
|
|
269
|
+
|
|
270
|
+
return { logger, lines: () => written.map((line) => lineOf(JSON.parse(line))) }; // { level, message, fields }
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LogFields } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* A pino `mixin` reading the fields of the work in progress at each line that is written. A
|
|
4
|
+
* source that throws (no active span, no execution context) gives no field, never a lost line.
|
|
5
|
+
*/
|
|
6
|
+
export declare function correlationMixin(fields: () => LogFields): () => LogFields;
|
|
7
|
+
//# sourceMappingURL=correlation-mixin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation-mixin.d.ts","sourceRoot":"","sources":["../../src/correlation/correlation-mixin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,SAAS,GAAG,MAAM,SAAS,CASzE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pino `mixin` reading the fields of the work in progress at each line that is written. A
|
|
3
|
+
* source that throws (no active span, no execution context) gives no field, never a lost line.
|
|
4
|
+
*/
|
|
5
|
+
export function correlationMixin(fields) {
|
|
6
|
+
return () => {
|
|
7
|
+
try {
|
|
8
|
+
// A copy: pino merges the fields of the line into the object the mixin answers.
|
|
9
|
+
return { ...fields() };
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=correlation-mixin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation-mixin.js","sourceRoot":"","sources":["../../src/correlation/correlation-mixin.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAuB;IACtD,OAAO,GAAG,EAAE;QACV,IAAI,CAAC;YACH,gFAAgF;YAChF,OAAO,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type LogFields, type LogLevel } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import { type DestinationStream } from 'pino';
|
|
3
|
+
import { PinoLogger, type PinoLoggerOptions } from './pino-logger.js';
|
|
4
|
+
export interface CreatePinoLoggerOptions extends PinoLoggerOptions {
|
|
5
|
+
/** Entries below it are not written. Default `info`. */
|
|
6
|
+
readonly level?: LogLevel;
|
|
7
|
+
/**
|
|
8
|
+
* `json`: one JSON object per line, what a container platform collects. `pretty`: lines for
|
|
9
|
+
* a human, through the `pino-pretty` transport, which must then be installed. Default `json`.
|
|
10
|
+
*/
|
|
11
|
+
readonly format?: 'json' | 'pretty';
|
|
12
|
+
/** Written on every line as `service`. */
|
|
13
|
+
readonly service?: string;
|
|
14
|
+
/** Written on every line as `environment`. */
|
|
15
|
+
readonly environment?: string;
|
|
16
|
+
/** Added to every line next to `service` and `environment`: version, region. */
|
|
17
|
+
readonly base?: LogFields;
|
|
18
|
+
/**
|
|
19
|
+
* Read at each line that is written: correlation fields of the work in progress, such as
|
|
20
|
+
* `traceLogFields` of the tracing package or `logFieldsOf` of the current execution context.
|
|
21
|
+
*/
|
|
22
|
+
readonly fields?: () => LogFields;
|
|
23
|
+
/** Field names hidden in addition to `CREDENTIAL_KEYS`, at the top of a line and one level below. */
|
|
24
|
+
readonly redact?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* Buffers the lines and writes them off the hot path. Default `false`: every line is
|
|
27
|
+
* written before the call returns. See "Performance" in the README for what a crash loses.
|
|
28
|
+
*/
|
|
29
|
+
readonly asynchronous?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* A stream, a file descriptor or a path. Default: the standard output. The `pretty` format
|
|
32
|
+
* runs in a worker thread and takes a descriptor or a path only.
|
|
33
|
+
*/
|
|
34
|
+
readonly destination?: DestinationStream | number | string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A `PinoLogger` on a pino instance with the defaults of the kit: `time` in ISO 8601, `level`
|
|
38
|
+
* by its name, `message`, then the fields; credentials redacted; errors described once.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createPinoLogger(options?: CreatePinoLoggerOptions): PinoLogger;
|
|
41
|
+
//# sourceMappingURL=create-pino-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-pino-logger.d.ts","sourceRoot":"","sources":["../src/create-pino-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAQ,KAAK,iBAAiB,EAAsB,MAAM,MAAM,CAAC;AAExE,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGtE,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACpC,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,SAAS,CAAC;IAClC,qGAAqG;IACrG,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;CAC5D;AAKD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAOlF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CREDENTIAL_KEYS } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import { pino } from 'pino';
|
|
3
|
+
import { correlationMixin } from './correlation/correlation-mixin.js';
|
|
4
|
+
import { PinoLogger } from './pino-logger.js';
|
|
5
|
+
import { credentialRedaction } from './redaction/credential-redaction.js';
|
|
6
|
+
const STANDARD_OUTPUT = 1;
|
|
7
|
+
const BUFFERED_BYTES = 4096;
|
|
8
|
+
/**
|
|
9
|
+
* A `PinoLogger` on a pino instance with the defaults of the kit: `time` in ISO 8601, `level`
|
|
10
|
+
* by its name, `message`, then the fields; credentials redacted; errors described once.
|
|
11
|
+
*/
|
|
12
|
+
export function createPinoLogger(options = {}) {
|
|
13
|
+
const instance = pino(pinoOptions(options), options.format === 'pretty' ? prettyTransport(options) : destinationOf(options));
|
|
14
|
+
return new PinoLogger(instance, options.scrubMessages === undefined ? {} : { scrubMessages: options.scrubMessages });
|
|
15
|
+
}
|
|
16
|
+
function pinoOptions(options) {
|
|
17
|
+
return {
|
|
18
|
+
level: options.level ?? 'info',
|
|
19
|
+
messageKey: 'message',
|
|
20
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
21
|
+
base: baseFields(options),
|
|
22
|
+
formatters: { level: (label) => ({ level: label }) },
|
|
23
|
+
redact: credentialRedaction([...CREDENTIAL_KEYS, ...(options.redact ?? [])]),
|
|
24
|
+
...(options.fields === undefined ? {} : { mixin: correlationMixin(options.fields) }),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function baseFields(options) {
|
|
28
|
+
return {
|
|
29
|
+
...(options.service === undefined ? {} : { service: options.service }),
|
|
30
|
+
...(options.environment === undefined ? {} : { environment: options.environment }),
|
|
31
|
+
...options.base,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function prettyTransport(options) {
|
|
35
|
+
if (typeof options.destination === 'object') {
|
|
36
|
+
throw new TypeError('The pretty format writes to a file descriptor or a path: pino-pretty runs in a worker thread');
|
|
37
|
+
}
|
|
38
|
+
return pino.transport({
|
|
39
|
+
target: 'pino-pretty',
|
|
40
|
+
options: {
|
|
41
|
+
destination: options.destination ?? STANDARD_OUTPUT,
|
|
42
|
+
messageKey: 'message',
|
|
43
|
+
translateTime: 'SYS:HH:MM:ss.l',
|
|
44
|
+
sync: options.asynchronous !== true,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function destinationOf(options) {
|
|
49
|
+
if (typeof options.destination === 'object') {
|
|
50
|
+
return options.destination;
|
|
51
|
+
}
|
|
52
|
+
return pino.destination({
|
|
53
|
+
dest: options.destination ?? STANDARD_OUTPUT,
|
|
54
|
+
sync: options.asynchronous !== true,
|
|
55
|
+
...(options.asynchronous === true ? { minLength: BUFFERED_BYTES } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=create-pino-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-pino-logger.js","sourceRoot":"","sources":["../src/create-pino-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DescribeErrorOptions, type LogFields, type LogValue } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The fields as pino receives them: errors described, and no key that would forge the header.
|
|
4
|
+
* pino writes `level` and `time` before the fields: a field of that name would be a second key
|
|
5
|
+
* of the line, and the last one is read.
|
|
6
|
+
*/
|
|
7
|
+
export declare function lineFields(fields: LogFields, options: DescribeErrorOptions): Record<string, LogValue>;
|
|
8
|
+
//# sourceMappingURL=line-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-fields.d.ts","sourceRoot":"","sources":["../../src/fields/line-fields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,oBAAoB,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnH;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAIrG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describeErrorFields } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The fields as pino receives them: errors described, and no key that would forge the header.
|
|
4
|
+
* pino writes `level` and `time` before the fields: a field of that name would be a second key
|
|
5
|
+
* of the line, and the last one is read.
|
|
6
|
+
*/
|
|
7
|
+
export function lineFields(fields, options) {
|
|
8
|
+
const { level: _level, time: _time, ...line } = describeErrorFields(fields, options);
|
|
9
|
+
return line;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=line-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-fields.js","sourceRoot":"","sources":["../../src/fields/line-fields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAA4D,MAAM,oBAAoB,CAAC;AAEnH;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAiB,EAAE,OAA6B;IACzE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErF,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { correlationMixin } from './correlation/correlation-mixin.js';
|
|
2
|
+
export { createPinoLogger, type CreatePinoLoggerOptions } from './create-pino-logger.js';
|
|
3
|
+
export { PinoLogger, type PinoLoggerOptions } from './pino-logger.js';
|
|
4
|
+
export { credentialRedaction, type CredentialRedaction } from './redaction/credential-redaction.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,qCAAqC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { correlationMixin } from './correlation/correlation-mixin.js';
|
|
2
|
+
export { createPinoLogger } from './create-pino-logger.js';
|
|
3
|
+
export { PinoLogger } from './pino-logger.js';
|
|
4
|
+
export { credentialRedaction } from './redaction/credential-redaction.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,yBAAyB,CAAC;AACzF,OAAO,EAAE,UAAU,EAA0B,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAA4B,MAAM,qCAAqC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type LogFields, type LogLevel, type Logger } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type { Logger as Pino } from 'pino';
|
|
3
|
+
export interface PinoLoggerOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Writes `SCRUBBED_MESSAGE` for the message of every logged error that is not a
|
|
6
|
+
* `DomainError`. Default `false`: see "Privacy" in the README for the trade-off.
|
|
7
|
+
*/
|
|
8
|
+
readonly scrubMessages?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The `Logger` port on a pino instance: the levels of the port are those of pino, a child is
|
|
12
|
+
* a pino child, and what the instance was configured with (mixin, redaction, destination)
|
|
13
|
+
* applies to every line. `createPinoLogger` builds one with the defaults of the kit; build it
|
|
14
|
+
* yourself around an instance you configured.
|
|
15
|
+
*/
|
|
16
|
+
export declare class PinoLogger implements Logger {
|
|
17
|
+
private readonly options;
|
|
18
|
+
private readonly pino;
|
|
19
|
+
constructor(pino: Pino, options?: PinoLoggerOptions);
|
|
20
|
+
debug(message: string, fields?: LogFields): void;
|
|
21
|
+
info(message: string, fields?: LogFields): void;
|
|
22
|
+
warn(message: string, fields?: LogFields): void;
|
|
23
|
+
error(message: string, fields?: LogFields): void;
|
|
24
|
+
child(fields: LogFields): PinoLogger;
|
|
25
|
+
/** The threshold of the instance, followed when it changes while running. */
|
|
26
|
+
isLevelEnabled(level: LogLevel): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves once what pino buffered was handed to its destination. Never rejects, and the
|
|
29
|
+
* logger stays usable: an entry logged afterwards is written like the others.
|
|
30
|
+
*/
|
|
31
|
+
flush(): Promise<void>;
|
|
32
|
+
private nativeChild;
|
|
33
|
+
private write;
|
|
34
|
+
private writeWithoutFields;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=pino-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pino-logger.d.ts","sourceRoot":"","sources":["../src/pino-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAG3C,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAKD;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,MAAM;IAKrC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAE5B,YACE,IAAI,EAAE,IAAI,EACO,OAAO,GAAE,iBAAsB,EAGjD;IAED,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAE/C;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAE9C;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAE9C;IAED,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAE/C;IAED,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAMnC;IAED,6EAA6E;IAC7E,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvC;IAED;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAUrB;IAED,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,KAAK;IAYb,OAAO,CAAC,kBAAkB;CAO3B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { UNWRITABLE_FIELDS } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import { lineFields } from './fields/line-fields.js';
|
|
3
|
+
/** Children of an instance the adapter already prepared: they are adopted as they are. */
|
|
4
|
+
const adopted = new WeakSet();
|
|
5
|
+
/**
|
|
6
|
+
* The `Logger` port on a pino instance: the levels of the port are those of pino, a child is
|
|
7
|
+
* a pino child, and what the instance was configured with (mixin, redaction, destination)
|
|
8
|
+
* applies to every line. `createPinoLogger` builds one with the defaults of the kit; build it
|
|
9
|
+
* yourself around an instance you configured.
|
|
10
|
+
*/
|
|
11
|
+
export class PinoLogger {
|
|
12
|
+
options;
|
|
13
|
+
pino;
|
|
14
|
+
constructor(pino, options = {}) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.pino = adopted.has(pino) ? pino : describingErrorsItself(pino);
|
|
17
|
+
}
|
|
18
|
+
debug(message, fields) {
|
|
19
|
+
this.write('debug', message, fields);
|
|
20
|
+
}
|
|
21
|
+
info(message, fields) {
|
|
22
|
+
this.write('info', message, fields);
|
|
23
|
+
}
|
|
24
|
+
warn(message, fields) {
|
|
25
|
+
this.write('warn', message, fields);
|
|
26
|
+
}
|
|
27
|
+
error(message, fields) {
|
|
28
|
+
this.write('error', message, fields);
|
|
29
|
+
}
|
|
30
|
+
child(fields) {
|
|
31
|
+
const child = this.nativeChild(fields);
|
|
32
|
+
adopted.add(child);
|
|
33
|
+
return new PinoLogger(child, this.options);
|
|
34
|
+
}
|
|
35
|
+
/** The threshold of the instance, followed when it changes while running. */
|
|
36
|
+
isLevelEnabled(level) {
|
|
37
|
+
return this.pino.isLevelEnabled(level);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves once what pino buffered was handed to its destination. Never rejects, and the
|
|
41
|
+
* logger stays usable: an entry logged afterwards is written like the others.
|
|
42
|
+
*/
|
|
43
|
+
flush() {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
try {
|
|
46
|
+
this.pino.flush(() => {
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
resolve();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
nativeChild(fields) {
|
|
56
|
+
try {
|
|
57
|
+
return this.pino.child(lineFields(fields, this.options));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return this.pino.child(UNWRITABLE_FIELDS);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
write(level, message, fields = {}) {
|
|
64
|
+
if (!this.isLevelEnabled(level)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
this.pino[level](lineFields(fields, this.options), message);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
this.writeWithoutFields(level, message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
writeWithoutFields(level, message) {
|
|
75
|
+
try {
|
|
76
|
+
this.pino[level](UNWRITABLE_FIELDS, message);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The adapter describes errors before pino sees them. Left in place, the standard serializer
|
|
85
|
+
* of the `err` key would take that description for an error and describe it a second time,
|
|
86
|
+
* folding the causes into one message and one stack.
|
|
87
|
+
*/
|
|
88
|
+
function describingErrorsItself(pino) {
|
|
89
|
+
return pino.child({}, { serializers: { err: (description) => description } });
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=pino-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pino-logger.js","sourceRoot":"","sources":["../src/pino-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** A value for the `redact` option of pino. */
|
|
2
|
+
export interface CredentialRedaction {
|
|
3
|
+
readonly paths: string[];
|
|
4
|
+
censor(value: unknown, path: readonly string[]): unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The pino `redact` option hiding the given keys at the top of a line and one level below, in
|
|
8
|
+
* the fields of an entry, of a child and of the mixin alike. Names are matched exactly, case
|
|
9
|
+
* included. The level below is one wildcard path whose censor looks at the name of the key:
|
|
10
|
+
* a wildcard path per key would cost pino as many passes over each line.
|
|
11
|
+
*/
|
|
12
|
+
export declare function credentialRedaction(keys?: readonly string[]): CredentialRedaction;
|
|
13
|
+
//# sourceMappingURL=credential-redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-redaction.d.ts","sourceRoot":"","sources":["../../src/redaction/credential-redaction.ts"],"names":[],"mappings":"AAEA,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC;CAC1D;AAID;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAoB,GAAG,mBAAmB,CAOlG"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CREDENTIAL_KEYS, REDACTED } from '@ddd-ts-kit/kernel';
|
|
2
|
+
const ONE_LEVEL_BELOW = '*.*';
|
|
3
|
+
/**
|
|
4
|
+
* The pino `redact` option hiding the given keys at the top of a line and one level below, in
|
|
5
|
+
* the fields of an entry, of a child and of the mixin alike. Names are matched exactly, case
|
|
6
|
+
* included. The level below is one wildcard path whose censor looks at the name of the key:
|
|
7
|
+
* a wildcard path per key would cost pino as many passes over each line.
|
|
8
|
+
*/
|
|
9
|
+
export function credentialRedaction(keys = CREDENTIAL_KEYS) {
|
|
10
|
+
const hidden = new Set(keys);
|
|
11
|
+
return {
|
|
12
|
+
paths: [...[...hidden].map((key) => `["${key}"]`), ONE_LEVEL_BELOW],
|
|
13
|
+
censor: (value, path) => (hidden.has(path.at(-1) ?? '') ? REDACTED : value),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=credential-redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-redaction.js","sourceRoot":"","sources":["../../src/redaction/credential-redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAI,GAAsB,eAAe;IAC3E,MAAM,MAAM,GAAwB,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAElD,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,eAAe,CAAC;QACnE,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;KAC5E,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddd-ts-kit/pino",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Adapter of the kernel Logger port on pino, with trace and execution correlation on every line.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/gbourgeat/ddd-ts-kit.git",
|
|
9
|
+
"directory": "packages/pino"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/gbourgeat/ddd-ts-kit/tree/main/packages/pino#readme",
|
|
12
|
+
"bugs": "https://github.com/gbourgeat/ddd-ts-kit/issues",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"provenance": true
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@ddd-ts-kit/kernel": "0.1.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@ddd-ts-kit/testing": "0.1.1",
|
|
33
|
+
"pino": "^10.4.0",
|
|
34
|
+
"pino-pretty": "^13.2.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"pino": "^10.4.0",
|
|
38
|
+
"pino-pretty": "^13.2.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"pino-pretty": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|