@ddd-ts-kit/winston 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 +282 -2
- package/dist/create-winston-logger.d.ts +33 -0
- package/dist/create-winston-logger.d.ts.map +1 -0
- package/dist/create-winston-logger.js +34 -0
- package/dist/create-winston-logger.js.map +1 -0
- package/dist/end-of.d.ts +4 -0
- package/dist/end-of.d.ts.map +1 -0
- package/dist/end-of.js +15 -0
- package/dist/end-of.js.map +1 -0
- package/dist/formats/correlation-fields.d.ts +9 -0
- package/dist/formats/correlation-fields.d.ts.map +1 -0
- package/dist/formats/correlation-fields.js +25 -0
- package/dist/formats/correlation-fields.js.map +1 -0
- package/dist/formats/iso-time.d.ts +4 -0
- package/dist/formats/iso-time.d.ts.map +1 -0
- package/dist/formats/iso-time.js +9 -0
- package/dist/formats/iso-time.js.map +1 -0
- package/dist/formats/readable-line.d.ts +4 -0
- package/dist/formats/readable-line.d.ts.map +1 -0
- package/dist/formats/readable-line.js +18 -0
- package/dist/formats/readable-line.js.map +1 -0
- package/dist/formats/redact-credentials.d.ts +7 -0
- package/dist/formats/redact-credentials.d.ts.map +1 -0
- package/dist/formats/redact-credentials.js +32 -0
- package/dist/formats/redact-credentials.js.map +1 -0
- package/dist/formats/unbreakable.d.ts +8 -0
- package/dist/formats/unbreakable.d.ts.map +1 -0
- package/dist/formats/unbreakable.js +24 -0
- package/dist/formats/unbreakable.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/winston-logger.d.ts +49 -0
- package/dist/winston-logger.d.ts.map +1 -0
- package/dist/winston-logger.js +83 -0
- package/dist/winston-logger.js.map +1 -0
- package/package.json +36 -4
package/README.md
CHANGED
|
@@ -1,3 +1,283 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ddd-ts-kit/winston
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The kernel `Logger` port on [winston](https://github.com/winstonjs/winston): JSON lines on the
|
|
4
|
+
standard output, the correlation fields of the work in progress on every line, credentials
|
|
5
|
+
redacted, a flush for the shutdown. Nothing in `domain/` or `application/` imports this
|
|
6
|
+
package, or logs anything: the composition root binds the adapter, and infrastructure,
|
|
7
|
+
presentation and the aspects write through the port.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i @ddd-ts-kit/winston winston
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Written against winston 3.19.
|
|
14
|
+
|
|
15
|
+
## Composition root
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// platform/observability/create-logger.ts
|
|
19
|
+
import type { ExecutionScope, LogLevel } from '@ddd-ts-kit/kernel';
|
|
20
|
+
import { traceLogFields } from '@ddd-ts-kit/tracing';
|
|
21
|
+
import { createWinstonLogger, type WinstonLogger } from '@ddd-ts-kit/winston';
|
|
22
|
+
|
|
23
|
+
export interface LogSettings {
|
|
24
|
+
readonly level: LogLevel; // LOG_LEVEL
|
|
25
|
+
readonly format: 'json' | 'pretty'; // LOG_FORMAT
|
|
26
|
+
readonly service: string;
|
|
27
|
+
readonly environment: string;
|
|
28
|
+
readonly scrubMessages: boolean; // LOG_SCRUB_MESSAGES
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createLogger(scope: ExecutionScope, settings: LogSettings): WinstonLogger {
|
|
32
|
+
return createWinstonLogger({ ...settings, fields: () => traceLogFields(scope) });
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// main.ts
|
|
38
|
+
import { tracing } from '@/platform/tracing.js'; // first
|
|
39
|
+
import { AsyncLocalStorageExecutionScope } from '@ddd-ts-kit/kernel';
|
|
40
|
+
import { createApplication, kernelModule } from '@ddd-ts-kit/di';
|
|
41
|
+
|
|
42
|
+
const scope = new AsyncLocalStorageExecutionScope();
|
|
43
|
+
const logger = createLogger(scope, config.log);
|
|
44
|
+
|
|
45
|
+
const app = createApplication({
|
|
46
|
+
modules: [kernelModule({ executionScope: scope, logger }), ...contexts],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
process.once('SIGTERM', () => void shutdown());
|
|
50
|
+
|
|
51
|
+
async function shutdown(): Promise<void> {
|
|
52
|
+
await app.stop(); // flushes the error reporter, then the logger, last: that ends the winston logger
|
|
53
|
+
await tracing.shutdown();
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`init --logger winston` writes this `create-logger.ts` (it answers a `NoopLogger` when given no
|
|
58
|
+
settings, which is how the tools and the tests stay silent); `platform/lay-foundations.ts`
|
|
59
|
+
calls it once and `composeApplication` hands the logger to `kernelModule({ logger })`.
|
|
60
|
+
|
|
61
|
+
One line of the application, as it is written:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{"actorId":"user-1","context":"shipping","correlationId":"9f2c","environment":"production","level":"info","message":"order shipped","origin":"http","service":"shop-api","span_id":"00f067aa0ba902b7","tenantId":"acme","time":"2026-03-01T10:00:00.000Z","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736"}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Correlation.** `fields` is read at each line that is written, by a winston format: no call
|
|
68
|
+
site passes an id. `traceLogFields(scope)` answers the trace and span ids inside a span, and
|
|
69
|
+
inside the execution scope the correlation id, the causation id, the origin, the actor and the
|
|
70
|
+
tenant. This package does not depend on the tracing package: any `() => LogFields` fits,
|
|
71
|
+
`() => logFieldsOf(scope.current())` included. A function that throws gives no field, never a
|
|
72
|
+
lost line. The fields of the line win over those of a child, which win over `fields`: the
|
|
73
|
+
format only fills the keys nobody gave. `StdoutLogger` and `@ddd-ts-kit/pino` differ on the
|
|
74
|
+
second step (there `fields` win over a child), so do not bind in a child a key that `fields`
|
|
75
|
+
answers if the logger may be swapped.
|
|
76
|
+
|
|
77
|
+
**An instance of your own.** The project that already configures winston keeps doing so, and
|
|
78
|
+
takes the formats it wants:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { CREDENTIAL_KEYS } from '@ddd-ts-kit/kernel';
|
|
82
|
+
import winston from 'winston';
|
|
83
|
+
import { WinstonLogger, correlationFields, redactCredentials } from '@ddd-ts-kit/winston';
|
|
84
|
+
|
|
85
|
+
const instance = winston.createLogger({
|
|
86
|
+
level: 'info',
|
|
87
|
+
format: winston.format.combine(
|
|
88
|
+
correlationFields(() => traceLogFields(scope)),
|
|
89
|
+
redactCredentials([...CREDENTIAL_KEYS, 'iban']), // after the formats that add fields
|
|
90
|
+
winston.format.timestamp(),
|
|
91
|
+
winston.format.json(),
|
|
92
|
+
),
|
|
93
|
+
transports: [new winston.transports.Console()],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const logger = new WinstonLogger(instance);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`createWinstonLogger` writes the instant under `time`, the level by its name and the message
|
|
100
|
+
under `message`, like the `StdoutLogger` of the aspects package and like `@ddd-ts-kit/pino`:
|
|
101
|
+
one dashboard reads the three. The keys of a line come out sorted: winston's JSON format
|
|
102
|
+
serializes with a stable stringifier.
|
|
103
|
+
|
|
104
|
+
## Development mode
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
createWinstonLogger({ level: 'debug', format: 'pretty', fields: () => traceLogFields(scope) });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
10:00:00.000 info order shipped { context: 'shipping', service: 'shop-api', correlationId: '9f2c' }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `pretty` format is made of winston's own combinators (`colorize`, `printf`), in the
|
|
115
|
+
process: no other package, the local time of day, the level in colour, the fields as Node
|
|
116
|
+
prints an object, a stack over several lines. Choose the format from configuration
|
|
117
|
+
(`LOG_FORMAT=pretty` in `.env`, `json` everywhere else).
|
|
118
|
+
|
|
119
|
+
## Privacy
|
|
120
|
+
|
|
121
|
+
Log lines are where personal data leaks: they are written everywhere, read by everyone and kept
|
|
122
|
+
for long. Three things are done in the adapter, so that no call site has to remember them.
|
|
123
|
+
|
|
124
|
+
**Credentials are redacted** by a winston format, in the fields of a line, of a child and of
|
|
125
|
+
`fields` alike. The value of these keys is replaced by `[redacted]`, at the top of a line and
|
|
126
|
+
one level below (`headers.authorization`, `body.password`):
|
|
127
|
+
|
|
128
|
+
`password`, `authorization`, `cookie`, `set-cookie`, `token`, `secret`, `apiKey`, `api_key`,
|
|
129
|
+
`access_token`, `refresh_token`, `accessToken`, `refreshToken`, `client_secret`
|
|
130
|
+
|
|
131
|
+
`redact: ['iban', 'ssn']` adds to the list. Names are matched exactly, case included, and
|
|
132
|
+
nothing is looked for deeper than one level: log the part you mean, not a whole request. The
|
|
133
|
+
objects you pass are never written to: a nested one is copied when a key has to be hidden.
|
|
134
|
+
|
|
135
|
+
**An error passed as a field** (`logger.error('payment failed', { error })`: the `LogValue` of
|
|
136
|
+
the port admits an `Error`) is written as `describeError` of the kernel describes it, the same
|
|
137
|
+
from every logger of the port: its class as `type`, its `code` when
|
|
138
|
+
it is a `DomainError`, its `message`, its own `stack`, then its `cause` the same way, up to
|
|
139
|
+
eight deep. Each error of a chain is written once. Errors are looked for among the fields of
|
|
140
|
+
the line, not inside nested objects. `CREDENTIAL_KEYS`, `REDACTED` and `SCRUBBED_MESSAGE` are
|
|
141
|
+
those of the kernel too.
|
|
142
|
+
|
|
143
|
+
**Messages of unexpected errors can be dropped.** The message of an error is the first place
|
|
144
|
+
where personal data shows up: a driver quotes the row it refused. The default keeps it, because
|
|
145
|
+
a log line that says `[message removed]` costs every investigation. With one option only the
|
|
146
|
+
messages the project wrote itself survive, those of its `DomainError`s; class, code, stack and
|
|
147
|
+
cause remain for the others, and the stack loses its first line, which repeats the message:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
createWinstonLogger({ scrubMessages: true }); // or new WinstonLogger(instance, { scrubMessages: true })
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This is the `scrubMessages` every logger of the port takes (`StdoutLogger`, pino, winston,
|
|
154
|
+
`InMemoryLogger`) and the one of `@ddd-ts-kit/sentry`, with the same rule, the same
|
|
155
|
+
`SCRUBBED_MESSAGE` and the same trade-off: turn it on when the logs leave your hands or when no
|
|
156
|
+
operator should read that data there. What the message of a log line itself says stays yours:
|
|
157
|
+
write `order shipped` and put the identifiers in fields.
|
|
158
|
+
|
|
159
|
+
The kit itself never hands an error to a logger: the `logged` aspect, `loggingMiddleware` and
|
|
160
|
+
the job scheduler write `errorType` and `errorCode` only (`identifyError` of the kernel), never
|
|
161
|
+
a message, whatever `scrubMessages` says, and the error goes to the `ErrorReporter`. The
|
|
162
|
+
description above is for the error your own code chooses to log:
|
|
163
|
+
`logger.error('payment failed', { error })`.
|
|
164
|
+
|
|
165
|
+
## Levels
|
|
166
|
+
|
|
167
|
+
| Port | winston (`npm` levels) | |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `debug` | `debug` | below it, winston also has `silly` |
|
|
170
|
+
| `info` | `info` | the default threshold; `http` and `verbose` stand between `info` and `debug` |
|
|
171
|
+
| `warn` | `warn` | |
|
|
172
|
+
| `error` | `error` | |
|
|
173
|
+
|
|
174
|
+
The names are the same in the `cli` levels. An instance on the `syslog` levels has no `warn`:
|
|
175
|
+
say where it goes, or the line is dropped, since winston does not write at a level it does
|
|
176
|
+
not know.
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
new WinstonLogger(instance, { levels: { warn: 'warning' } });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
A line below the threshold costs one comparison: the adapter asks `isLevelEnabled` first,
|
|
183
|
+
because winston alone would run every format before its transports drop the line. The
|
|
184
|
+
threshold of an instance changed while running (`instance.level = 'debug'`) is followed by the
|
|
185
|
+
adapter and by its children. `isLevelEnabled(level)` of the port answers the same question for
|
|
186
|
+
the mapped level: ask it before building fields that cost. Once the logger was flushed it
|
|
187
|
+
answers `false` for every level.
|
|
188
|
+
|
|
189
|
+
`child(fields)` is a winston child: the fields are bound once, and winston merges them under
|
|
190
|
+
the fields of each line. Three things differ from what winston does when called directly, all
|
|
191
|
+
on purpose: a field named `level` or `message` never replaces the level or the message of the
|
|
192
|
+
line; the message is never read as a format string (`%s`); and with `createWinstonLogger` a
|
|
193
|
+
field named `time` is overwritten by the instant of the line. One thing is winston's:
|
|
194
|
+
`service`, `environment` and `base` are its `defaultMeta`, which wins over a field of the
|
|
195
|
+
same name on a line or in a child, where the other two loggers of the kit let the line and
|
|
196
|
+
the child win.
|
|
197
|
+
|
|
198
|
+
## Flush
|
|
199
|
+
|
|
200
|
+
`flush(): Promise<void>` ends the winston logger and resolves once each of its transports has
|
|
201
|
+
written what it was handed (their `finish` event). It never rejects, and a second call answers
|
|
202
|
+
the same promise. `app.stop()` calls it, last, after the modules stopped and the error reporter
|
|
203
|
+
was flushed: stopping logs too. Without the application of the di package, call it yourself at
|
|
204
|
+
that place.
|
|
205
|
+
|
|
206
|
+
The port calls a flush once, at shutdown, and leaves what is logged afterwards unspecified.
|
|
207
|
+
Here the flush is final: a winston logger does not reopen, so what is logged afterwards,
|
|
208
|
+
through this logger or one of its children, is dropped without an error, and `isLevelEnabled`
|
|
209
|
+
answers `false`. Whatever must be logged about the shutdown is logged before `app.stop()`
|
|
210
|
+
returns. A transport over a stream you opened yourself
|
|
211
|
+
(a socket, a file stream given to the `Stream` transport) has handed its lines to that stream
|
|
212
|
+
when `flush()` resolves: ending that stream is yours.
|
|
213
|
+
|
|
214
|
+
## Performance
|
|
215
|
+
|
|
216
|
+
winston writes each line through a Node stream, a chain of formats and its transports, all in
|
|
217
|
+
the process and in the call: about 7 microseconds a line where `@ddd-ts-kit/pino` takes 3
|
|
218
|
+
(Node 24, a child, four correlation fields, three fields, a sink that discards), and more
|
|
219
|
+
with each format you add. Each line costs here one copy of its fields, one merge per level of
|
|
220
|
+
child, the format reading `fields`, the redaction pass over the top-level values, the instant,
|
|
221
|
+
and `JSON.stringify` through a safe serializer. That is nothing at a few hundred lines per
|
|
222
|
+
second and visible at tens of thousands: there, lower the level or take `@ddd-ts-kit/pino`.
|
|
223
|
+
|
|
224
|
+
Nothing is buffered by the adapter, so nothing is lost on a crash beyond what the transport
|
|
225
|
+
itself had not written. Log calls never throw: fields that cannot be read or serialized (a
|
|
226
|
+
getter that throws) give the line without them, marked `logError: "unserializable fields"`,
|
|
227
|
+
and `createWinstonLogger` holds its formats to the same rule inside the stream.
|
|
228
|
+
|
|
229
|
+
## Which one
|
|
230
|
+
|
|
231
|
+
Take `StdoutLogger` of the aspects package when JSON lines on the standard output are all you
|
|
232
|
+
need: no dependency, the same line shape. Take `@ddd-ts-kit/pino` when logging volume matters
|
|
233
|
+
or when the framework already speaks pino (Fastify). Take winston when the team already runs
|
|
234
|
+
its transports and formats (files with rotation, syslog, a vendor transport) and wants to keep
|
|
235
|
+
them. The three implement one port and are held by one contract suite: swapping is one line of
|
|
236
|
+
the composition root.
|
|
237
|
+
|
|
238
|
+
## NestJS
|
|
239
|
+
|
|
240
|
+
Nest has its own `LoggerService`, used by the framework for its own messages and by
|
|
241
|
+
`new Logger(context)` in Nest classes. Application code of the kit never needs it: handlers and
|
|
242
|
+
adapters receive the kernel `Logger`. To send what Nest itself logs through the same instance,
|
|
243
|
+
hand it a bridge of a few lines; the kit ships none, since it would bind this package to Nest:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const nest = logger.child({ context: 'nest' });
|
|
247
|
+
const bridge: LoggerService = {
|
|
248
|
+
log: (message: unknown) => nest.info(String(message)),
|
|
249
|
+
warn: (message: unknown) => nest.warn(String(message)),
|
|
250
|
+
error: (message: unknown) => nest.error(String(message)),
|
|
251
|
+
debug: (message: unknown) => nest.debug(String(message)),
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const app = await NestFactory.create(AppModule, { logger: bridge });
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`nest-winston` is the alternative when Nest classes log a lot: give it the winston instance
|
|
258
|
+
you built, and wrap the same instance in `new WinstonLogger(instance)` for the kit.
|
|
259
|
+
|
|
260
|
+
## In a test
|
|
261
|
+
|
|
262
|
+
Nothing of this package: the test application binds `InMemoryLogger` of the kernel, and its
|
|
263
|
+
`entries` are data. To hold another adapter of the port to the same behaviour, run the contract:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import { loggerContract } from '@ddd-ts-kit/testing/contracts';
|
|
267
|
+
|
|
268
|
+
loggerContract({ describe, it, expect, beforeEach }, {
|
|
269
|
+
name: 'WinstonLogger',
|
|
270
|
+
createLogger: (setup) => { // { level, fields, scrubMessages }
|
|
271
|
+
const written: string[] = [];
|
|
272
|
+
const stream = new Writable({
|
|
273
|
+
write: (chunk, _encoding, done) => {
|
|
274
|
+
written.push(String(chunk));
|
|
275
|
+
done();
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const logger = createWinstonLogger({ ...setup, transports: [new winston.transports.Stream({ stream })] });
|
|
279
|
+
|
|
280
|
+
return { logger, lines: () => written.map((line) => lineOf(JSON.parse(line))) }; // { level, message, fields }
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type LogFields, type LogLevel } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
import { WinstonLogger, type WinstonLoggerOptions } from './winston-logger.js';
|
|
4
|
+
export interface CreateWinstonLoggerOptions extends Pick<WinstonLoggerOptions, 'scrubMessages'> {
|
|
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`: one
|
|
9
|
+
* coloured line for a human, with winston's own formats. 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
|
+
/** Where the lines go. Default: the console transport, on the standard output. */
|
|
26
|
+
readonly transports?: NonNullable<winston.LoggerOptions['transports']>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A `WinstonLogger` on a winston logger with the defaults of the kit: `time` in ISO 8601,
|
|
30
|
+
* `level` by its name, `message`, then the fields; credentials redacted; errors described once.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createWinstonLogger(options?: CreateWinstonLoggerOptions): WinstonLogger;
|
|
33
|
+
//# sourceMappingURL=create-winston-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-winston-logger.d.ts","sourceRoot":"","sources":["../src/create-winston-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,OAAO,MAAM,SAAS,CAAC;AAM9B,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAC;IAC7F,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,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;CACxE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,CAW3F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CREDENTIAL_KEYS } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
import { correlationFields } from './formats/correlation-fields.js';
|
|
4
|
+
import { isoTime } from './formats/iso-time.js';
|
|
5
|
+
import { readableLine } from './formats/readable-line.js';
|
|
6
|
+
import { redactCredentials } from './formats/redact-credentials.js';
|
|
7
|
+
import { unbreakable } from './formats/unbreakable.js';
|
|
8
|
+
import { WinstonLogger } from './winston-logger.js';
|
|
9
|
+
/**
|
|
10
|
+
* A `WinstonLogger` on a winston logger with the defaults of the kit: `time` in ISO 8601,
|
|
11
|
+
* `level` by its name, `message`, then the fields; credentials redacted; errors described once.
|
|
12
|
+
*/
|
|
13
|
+
export function createWinstonLogger(options = {}) {
|
|
14
|
+
const logger = winston.createLogger({
|
|
15
|
+
level: options.level ?? 'info',
|
|
16
|
+
levels: winston.config.npm.levels,
|
|
17
|
+
defaultMeta: baseFields(options),
|
|
18
|
+
format: unbreakable(lineFormat(options)),
|
|
19
|
+
transports: options.transports ?? [new winston.transports.Console()],
|
|
20
|
+
exitOnError: false,
|
|
21
|
+
});
|
|
22
|
+
return new WinstonLogger(logger, options.scrubMessages === undefined ? {} : { scrubMessages: options.scrubMessages });
|
|
23
|
+
}
|
|
24
|
+
function baseFields(options) {
|
|
25
|
+
return {
|
|
26
|
+
...(options.service === undefined ? {} : { service: options.service }),
|
|
27
|
+
...(options.environment === undefined ? {} : { environment: options.environment }),
|
|
28
|
+
...options.base,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function lineFormat(options) {
|
|
32
|
+
return winston.format.combine(...(options.fields === undefined ? [] : [correlationFields(options.fields)]), redactCredentials([...CREDENTIAL_KEYS, ...(options.redact ?? [])]), isoTime(), options.format === 'pretty' ? readableLine() : winston.format.json());
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=create-winston-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-winston-logger.js","sourceRoot":"","sources":["../src/create-winston-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiC,MAAM,oBAAoB,CAAC;AACpF,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AA2B/E;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAA+B,EAAE;IAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,MAAM;QAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM;QACjC,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC;QAChC,MAAM,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACxC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACpE,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IAEH,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AACxH,CAAC;AAED,SAAS,UAAU,CAAC,OAAmC;IACrD,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACtE,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAClF,GAAG,OAAO,CAAC,IAAI;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,OAAmC;IACrD,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAC3B,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAC5E,iBAAiB,CAAC,CAAC,GAAG,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,EAClE,OAAO,EAAE,EACT,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACrE,CAAC;AACJ,CAAC"}
|
package/dist/end-of.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"end-of.d.ts","sourceRoot":"","sources":["../src/end-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,+FAA+F;AAC/F,wBAAsB,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjE"}
|
package/dist/end-of.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Ends the logger and resolves once each of its transports has written what it was handed. */
|
|
2
|
+
export async function endOf(logger) {
|
|
3
|
+
const finished = logger.transports.map(finishOf);
|
|
4
|
+
logger.end();
|
|
5
|
+
await Promise.all(finished);
|
|
6
|
+
}
|
|
7
|
+
function finishOf(transport) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
transport.once('finish', resolve);
|
|
10
|
+
transport.once('error', () => {
|
|
11
|
+
resolve();
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=end-of.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"end-of.js","sourceRoot":"","sources":["../src/end-of.ts"],"names":[],"mappings":"AAIA,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,MAAsB;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEjD,MAAM,CAAC,GAAG,EAAE,CAAC;IACb,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,QAAQ,CAAC,SAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAElC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YAC3B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LogFields } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
/**
|
|
4
|
+
* A winston format reading the fields of the work in progress at each line that is written,
|
|
5
|
+
* under the fields of the line and of its children. A source that throws (no active span, no
|
|
6
|
+
* execution context) gives no field, never a lost line.
|
|
7
|
+
*/
|
|
8
|
+
export declare function correlationFields(fields: () => LogFields): winston.Logform.Format;
|
|
9
|
+
//# sourceMappingURL=correlation-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation-fields.d.ts","sourceRoot":"","sources":["../../src/formats/correlation-fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAUjF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
/**
|
|
3
|
+
* A winston format reading the fields of the work in progress at each line that is written,
|
|
4
|
+
* under the fields of the line and of its children. A source that throws (no active span, no
|
|
5
|
+
* execution context) gives no field, never a lost line.
|
|
6
|
+
*/
|
|
7
|
+
export function correlationFields(fields) {
|
|
8
|
+
return winston.format((info) => {
|
|
9
|
+
for (const [key, value] of Object.entries(fieldsOrNone(fields))) {
|
|
10
|
+
if (info[key] === undefined) {
|
|
11
|
+
info[key] = value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return info;
|
|
15
|
+
})();
|
|
16
|
+
}
|
|
17
|
+
function fieldsOrNone(fields) {
|
|
18
|
+
try {
|
|
19
|
+
return fields();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=correlation-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlation-fields.js","sourceRoot":"","sources":["../../src/formats/correlation-fields.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAuB;IACvD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YAChE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACpB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAED,SAAS,YAAY,CAAC,MAAuB;IAC3C,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iso-time.d.ts","sourceRoot":"","sources":["../../src/formats/iso-time.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,0EAA0E;AAC1E,wBAAgB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAMhD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
/** Stamps the line with `time` in ISO 8601, over a field of that name. */
|
|
3
|
+
export function isoTime() {
|
|
4
|
+
return winston.format((info) => {
|
|
5
|
+
info.time = new Date().toISOString();
|
|
6
|
+
return info;
|
|
7
|
+
})();
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=iso-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iso-time.js","sourceRoot":"","sources":["../../src/formats/iso-time.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,0EAA0E;AAC1E,MAAM,UAAU,OAAO;IACrB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,EAAE,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readable-line.d.ts","sourceRoot":"","sources":["../../src/formats/readable-line.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAS9B,sGAAsG;AACtG,wBAAgB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAWrD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
const TIME_OF_DAY = new Intl.DateTimeFormat('en-GB', {
|
|
4
|
+
hour: '2-digit',
|
|
5
|
+
minute: '2-digit',
|
|
6
|
+
second: '2-digit',
|
|
7
|
+
fractionalSecondDigits: 3,
|
|
8
|
+
});
|
|
9
|
+
/** One line for a human: the local time of day, the level in colour, the message, then the fields. */
|
|
10
|
+
export function readableLine() {
|
|
11
|
+
return winston.format.combine(winston.format.colorize(), winston.format.printf(({ level, message, time, ...rest }) => {
|
|
12
|
+
// By its string keys: winston keeps the level and the rendered line under symbols.
|
|
13
|
+
const fields = Object.fromEntries(Object.entries(rest));
|
|
14
|
+
const shown = Object.keys(fields).length === 0 ? '' : ` ${inspect(fields, { depth: 6, breakLength: 120 })}`;
|
|
15
|
+
return `${TIME_OF_DAY.format(new Date(String(time)))} ${level} ${String(message)}${shown}`;
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=readable-line.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readable-line.js","sourceRoot":"","sources":["../../src/formats/readable-line.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;IACnD,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,sBAAsB,EAAE,CAAC;CAC1B,CAAC,CAAC;AAEH,sGAAsG;AACtG,MAAM,UAAU,YAAY;IAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EACzB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;QAC1D,mFAAmF;QACnF,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QAE5G,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC;IAC7F,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
/**
|
|
3
|
+
* A winston format hiding the value of the given keys at the top of a line and one level
|
|
4
|
+
* below. Names are matched exactly, case included. Place it after the formats that add fields.
|
|
5
|
+
*/
|
|
6
|
+
export declare function redactCredentials(keys?: readonly string[]): winston.Logform.Format;
|
|
7
|
+
//# sourceMappingURL=redact-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact-credentials.d.ts","sourceRoot":"","sources":["../../src/formats/redact-credentials.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAcnG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CREDENTIAL_KEYS, REDACTED } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
/**
|
|
4
|
+
* A winston format hiding the value of the given keys at the top of a line and one level
|
|
5
|
+
* below. Names are matched exactly, case included. Place it after the formats that add fields.
|
|
6
|
+
*/
|
|
7
|
+
export function redactCredentials(keys = CREDENTIAL_KEYS) {
|
|
8
|
+
const hidden = new Set(keys);
|
|
9
|
+
return winston.format((info) => {
|
|
10
|
+
for (const [key, value] of Object.entries(info)) {
|
|
11
|
+
if (hidden.has(key)) {
|
|
12
|
+
info[key] = REDACTED;
|
|
13
|
+
}
|
|
14
|
+
else if (isRecord(value)) {
|
|
15
|
+
info[key] = withoutCredentials(value, hidden);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return info;
|
|
19
|
+
})();
|
|
20
|
+
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
23
|
+
}
|
|
24
|
+
/** A nested object belongs to the caller: it is copied when a key has to be hidden, never written to. */
|
|
25
|
+
function withoutCredentials(nested, hidden) {
|
|
26
|
+
const credentials = Object.keys(nested).filter((key) => hidden.has(key));
|
|
27
|
+
if (credentials.length === 0) {
|
|
28
|
+
return nested;
|
|
29
|
+
}
|
|
30
|
+
return { ...nested, ...Object.fromEntries(credentials.map((key) => [key, REDACTED])) };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=redact-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact-credentials.js","sourceRoot":"","sources":["../../src/formats/redact-credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAsB,eAAe;IACzE,MAAM,MAAM,GAAwB,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAElD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;YACvB,CAAC;iBAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,CAAC;AAC1G,CAAC;AAED,yGAAyG;AACzG,SAAS,kBAAkB,CACzB,MAAyC,EACzC,MAA2B;IAE3B,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
/**
|
|
3
|
+
* The format, and when it throws on a line (a getter that throws, a `toJSON` that fails), that
|
|
4
|
+
* line as JSON without its fields. winston formats a line inside its stream: an exception
|
|
5
|
+
* there reaches the caller, or the process when the stream was holding the line back.
|
|
6
|
+
*/
|
|
7
|
+
export declare function unbreakable(format: winston.Logform.Format): winston.Logform.Format;
|
|
8
|
+
//# sourceMappingURL=unbreakable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unbreakable.d.ts","sourceRoot":"","sources":["../../src/formats/unbreakable.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAM9B;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAYlF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UNWRITABLE_FIELDS } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
import { isoTime } from './iso-time.js';
|
|
4
|
+
/** The key under which winston and its transports read the level of a line (package `triple-beam`). */
|
|
5
|
+
const LEVEL = Symbol.for('level');
|
|
6
|
+
/**
|
|
7
|
+
* The format, and when it throws on a line (a getter that throws, a `toJSON` that fails), that
|
|
8
|
+
* line as JSON without its fields. winston formats a line inside its stream: an exception
|
|
9
|
+
* there reaches the caller, or the process when the stream was holding the line back.
|
|
10
|
+
*/
|
|
11
|
+
export function unbreakable(format) {
|
|
12
|
+
const bare = winston.format.combine(isoTime(), winston.format.json());
|
|
13
|
+
return {
|
|
14
|
+
transform: (info, options) => {
|
|
15
|
+
try {
|
|
16
|
+
return format.transform(info, options);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return bare.transform({ level: info.level, message: info.message, ...UNWRITABLE_FIELDS, [LEVEL]: info[LEVEL] });
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=unbreakable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unbreakable.js","sourceRoot":"","sources":["../../src/formats/unbreakable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,uGAAuG;AACvG,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAElC;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAA8B;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAEtE,OAAO;QACL,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;YAC3B,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,iBAAiB,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createWinstonLogger, type CreateWinstonLoggerOptions } from './create-winston-logger.js';
|
|
2
|
+
export { correlationFields } from './formats/correlation-fields.js';
|
|
3
|
+
export { redactCredentials } from './formats/redact-credentials.js';
|
|
4
|
+
export { WinstonLogger, type WinstonLoggerOptions } from './winston-logger.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,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createWinstonLogger } from './create-winston-logger.js';
|
|
2
|
+
export { correlationFields } from './formats/correlation-fields.js';
|
|
3
|
+
export { redactCredentials } from './formats/redact-credentials.js';
|
|
4
|
+
export { WinstonLogger } from './winston-logger.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,mBAAmB,EAAmC,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type LogFields, type LogLevel, type Logger } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type winston from 'winston';
|
|
3
|
+
export interface WinstonLoggerOptions {
|
|
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
|
+
* The winston level each level of the port is written at. Default: the level of the same
|
|
11
|
+
* name, which the `npm` and `cli` levels of winston have. An instance on the `syslog`
|
|
12
|
+
* levels needs `{ warn: 'warning' }`: a level the instance does not know is dropped.
|
|
13
|
+
*/
|
|
14
|
+
readonly levels?: Partial<Readonly<Record<LogLevel, string>>>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The `Logger` port on a winston logger: a child is a winston child, and the formats and the
|
|
18
|
+
* transports the instance was configured with apply to every line. `createWinstonLogger`
|
|
19
|
+
* builds one with the defaults of the kit; build it yourself around an instance you configured.
|
|
20
|
+
*/
|
|
21
|
+
export declare class WinstonLogger implements Logger {
|
|
22
|
+
private readonly winston;
|
|
23
|
+
private readonly options;
|
|
24
|
+
private root;
|
|
25
|
+
private ended;
|
|
26
|
+
constructor(winston: winston.Logger, options?: WinstonLoggerOptions);
|
|
27
|
+
debug(message: string, fields?: LogFields): void;
|
|
28
|
+
info(message: string, fields?: LogFields): void;
|
|
29
|
+
warn(message: string, fields?: LogFields): void;
|
|
30
|
+
error(message: string, fields?: LogFields): void;
|
|
31
|
+
child(fields: LogFields): WinstonLogger;
|
|
32
|
+
/**
|
|
33
|
+
* Whether winston would hand an entry of that level to a transport. winston filters by level
|
|
34
|
+
* in its transports, after the formats: asked here, a dropped entry costs nothing. `false`
|
|
35
|
+
* for every level once the logger was flushed.
|
|
36
|
+
*/
|
|
37
|
+
isLevelEnabled(level: LogLevel): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Ends the winston logger and resolves once its transports have written everything. Final:
|
|
40
|
+
* what is logged afterwards, through this logger or a child, is dropped without a throw. A
|
|
41
|
+
* second call answers the same promise. Never rejects.
|
|
42
|
+
*/
|
|
43
|
+
flush(): Promise<void>;
|
|
44
|
+
private nativeChild;
|
|
45
|
+
private write;
|
|
46
|
+
private nativeLevel;
|
|
47
|
+
private writeWithoutFields;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=winston-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"winston-logger.d.ts","sourceRoot":"","sources":["../src/winston-logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAGnC,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CAC/D;AAED;;;;GAIG;AACH,qBAAa,aAAc,YAAW,MAAM;IAKxC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,KAAK,CAA4B;IAEzC,YACmB,OAAO,EAAE,OAAO,CAAC,MAAM,EACvB,OAAO,GAAE,oBAAyB,EACjD;IAEJ,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,aAAa,CAMtC;IAED;;;;OAIG;IACH,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvC;IAED;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAIrB;IAED,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,KAAK;IAcb,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;CAO3B"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { UNWRITABLE_FIELDS, describeErrorFields } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import { endOf } from './end-of.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `Logger` port on a winston logger: a child is a winston child, and the formats and the
|
|
5
|
+
* transports the instance was configured with apply to every line. `createWinstonLogger`
|
|
6
|
+
* builds one with the defaults of the kit; build it yourself around an instance you configured.
|
|
7
|
+
*/
|
|
8
|
+
export class WinstonLogger {
|
|
9
|
+
winston;
|
|
10
|
+
options;
|
|
11
|
+
root = this;
|
|
12
|
+
ended;
|
|
13
|
+
constructor(winston, options = {}) {
|
|
14
|
+
this.winston = winston;
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
debug(message, fields) {
|
|
18
|
+
this.write('debug', message, fields);
|
|
19
|
+
}
|
|
20
|
+
info(message, fields) {
|
|
21
|
+
this.write('info', message, fields);
|
|
22
|
+
}
|
|
23
|
+
warn(message, fields) {
|
|
24
|
+
this.write('warn', message, fields);
|
|
25
|
+
}
|
|
26
|
+
error(message, fields) {
|
|
27
|
+
this.write('error', message, fields);
|
|
28
|
+
}
|
|
29
|
+
child(fields) {
|
|
30
|
+
const child = new WinstonLogger(this.nativeChild(fields), this.options);
|
|
31
|
+
child.root = this.root;
|
|
32
|
+
return child;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether winston would hand an entry of that level to a transport. winston filters by level
|
|
36
|
+
* in its transports, after the formats: asked here, a dropped entry costs nothing. `false`
|
|
37
|
+
* for every level once the logger was flushed.
|
|
38
|
+
*/
|
|
39
|
+
isLevelEnabled(level) {
|
|
40
|
+
return this.root.ended === undefined && this.winston.isLevelEnabled(this.nativeLevel(level));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Ends the winston logger and resolves once its transports have written everything. Final:
|
|
44
|
+
* what is logged afterwards, through this logger or a child, is dropped without a throw. A
|
|
45
|
+
* second call answers the same promise. Never rejects.
|
|
46
|
+
*/
|
|
47
|
+
flush() {
|
|
48
|
+
this.root.ended ??= endOf(this.root.winston).catch(() => undefined);
|
|
49
|
+
return this.root.ended;
|
|
50
|
+
}
|
|
51
|
+
nativeChild(fields) {
|
|
52
|
+
try {
|
|
53
|
+
return this.winston.child(describeErrorFields(fields, this.options));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return this.winston.child(UNWRITABLE_FIELDS);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
write(level, message, fields = {}) {
|
|
60
|
+
if (!this.isLevelEnabled(level)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const native = this.nativeLevel(level);
|
|
64
|
+
try {
|
|
65
|
+
this.winston.log({ ...describeErrorFields(fields, this.options), level: native, message });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
this.writeWithoutFields(native, message);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
nativeLevel(level) {
|
|
72
|
+
return this.options.levels?.[level] ?? level;
|
|
73
|
+
}
|
|
74
|
+
writeWithoutFields(level, message) {
|
|
75
|
+
try {
|
|
76
|
+
this.winston.log({ ...UNWRITABLE_FIELDS, level, message });
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=winston-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"winston-logger.js","sourceRoot":"","sources":["../src/winston-logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddd-ts-kit/winston",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Adapter of the kernel Logger port on winston, 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/winston"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/gbourgeat/ddd-ts-kit/tree/main/packages/winston#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
|
+
"winston": "^3.19.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"winston": "^3.19.0"
|
|
37
|
+
}
|
|
38
|
+
}
|