@eventuras/logger 0.8.0 → 0.9.0
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 +11 -10
- package/dist/Logger-B22dX10w.js +338 -0
- package/dist/Logger-B22dX10w.js.map +1 -0
- package/dist/index.js +33 -12
- package/dist/index.js.map +1 -0
- package/dist/node.js +125 -59
- package/dist/node.js.map +1 -0
- package/dist/opentelemetry.d.ts +63 -44
- package/dist/opentelemetry.d.ts.map +1 -1
- package/dist/opentelemetry.js +194 -33
- package/dist/opentelemetry.js.map +1 -0
- package/dist/sink.d.ts +17 -0
- package/dist/sink.d.ts.map +1 -0
- package/dist/transports/pino.d.ts.map +1 -1
- package/package.json +12 -23
- package/LICENSE +0 -674
- package/dist/Logger-CcNEmm6u.js +0 -208
- package/dist/chunk-NnHqS4_Y.js +0 -20
- package/dist/esm-CIhYjsQQ.js +0 -528
- package/dist/esm-Dido2CZe.js +0 -1580
- package/dist/src-15l0SmY8.js +0 -407
package/README.md
CHANGED
|
@@ -234,16 +234,15 @@ Redacts `authorization`, `cookie`, `set-cookie`, `x-api-key`, `x-auth-token`, an
|
|
|
234
234
|
|
|
235
235
|
## OpenTelemetry Integration
|
|
236
236
|
|
|
237
|
-
Send logs to any OTel-compatible backend (Sentry, Grafana,
|
|
237
|
+
Send logs to any OTel-compatible backend (Sentry, Grafana, the Aspire dashboard, etc.) without vendor lock-in. Every line the Pino transport writes — after redaction, from every logger, including ones created before setup — is also emitted as an OpenTelemetry log record.
|
|
238
238
|
|
|
239
239
|
### Install OTel Packages
|
|
240
240
|
|
|
241
241
|
```bash
|
|
242
|
-
pnpm add @opentelemetry/
|
|
243
|
-
@opentelemetry/instrumentation-pino @opentelemetry/exporter-logs-otlp-http
|
|
242
|
+
pnpm add @opentelemetry/sdk-logs @opentelemetry/exporter-logs-otlp-http
|
|
244
243
|
```
|
|
245
244
|
|
|
246
|
-
|
|
245
|
+
`@opentelemetry/sdk-logs` is an optional peer dependency — the logger uses your app's copy, the same one your processor comes from. Nothing else is needed.
|
|
247
246
|
|
|
248
247
|
### Setup
|
|
249
248
|
|
|
@@ -252,16 +251,18 @@ import { setupOpenTelemetryLogger } from "@eventuras/logger/opentelemetry";
|
|
|
252
251
|
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
253
252
|
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
254
253
|
|
|
255
|
-
setupOpenTelemetryLogger({
|
|
254
|
+
await setupOpenTelemetryLogger({
|
|
256
255
|
serviceName: "my-app",
|
|
257
|
-
logRecordProcessor: new BatchLogRecordProcessor(
|
|
258
|
-
new OTLPLogExporter(
|
|
259
|
-
|
|
260
|
-
}),
|
|
261
|
-
),
|
|
256
|
+
logRecordProcessor: new BatchLogRecordProcessor({
|
|
257
|
+
exporter: new OTLPLogExporter(), // reads the OTEL_EXPORTER_OTLP_* variables
|
|
258
|
+
}),
|
|
262
259
|
});
|
|
263
260
|
```
|
|
264
261
|
|
|
262
|
+
Already running the OpenTelemetry Node SDK? Call `setupOpenTelemetryLogger()` without options to emit to the globally registered LoggerProvider, or pass one as `loggerProvider`.
|
|
263
|
+
|
|
264
|
+
Setup never throws or rejects: if it can't start, it logs an error and logging to stdout carries on.
|
|
265
|
+
|
|
265
266
|
### Shutdown
|
|
266
267
|
|
|
267
268
|
```typescript
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
//#region src/sink.ts
|
|
3
|
+
var SINK_KEY = Symbol.for("@eventuras/logger:line-sink");
|
|
4
|
+
function setLogLineSink(sink) {
|
|
5
|
+
globalThis[SINK_KEY] = sink;
|
|
6
|
+
}
|
|
7
|
+
/** Forward a line to the sink, if any. Never throws — telemetry must not break logging. */
|
|
8
|
+
function forwardLogLine(line) {
|
|
9
|
+
const sink = globalThis[SINK_KEY];
|
|
10
|
+
if (!sink) return;
|
|
11
|
+
try {
|
|
12
|
+
sink(line);
|
|
13
|
+
} catch {}
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/transports/pino.ts
|
|
17
|
+
/**
|
|
18
|
+
* Pino-based transport — the default production backend.
|
|
19
|
+
*
|
|
20
|
+
* Wraps a Pino logger instance to satisfy the `LogTransport` interface,
|
|
21
|
+
* keeping Pino as an implementation detail that consumers never interact with directly.
|
|
22
|
+
*
|
|
23
|
+
* For pretty-printed dev output, see `configureNodeLogger` in
|
|
24
|
+
* `@eventuras/logger/node` — this module intentionally stays free of
|
|
25
|
+
* `node:stream` imports so the main entry stays browser/edge-safe.
|
|
26
|
+
*/
|
|
27
|
+
var PinoTransport = class {
|
|
28
|
+
/** The underlying Pino instance. Exposed for advanced integrations only. */
|
|
29
|
+
pino;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
const pinoOpts = {
|
|
32
|
+
level: options.level ?? "info",
|
|
33
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
34
|
+
formatters: { level: (label) => ({ level: label }) },
|
|
35
|
+
...options.redact && { redact: {
|
|
36
|
+
paths: options.redact,
|
|
37
|
+
censor: "[REDACTED]"
|
|
38
|
+
} },
|
|
39
|
+
...options.pinoOptions
|
|
40
|
+
};
|
|
41
|
+
const userStreamWrite = options.pinoOptions?.hooks?.streamWrite;
|
|
42
|
+
pinoOpts.hooks = {
|
|
43
|
+
...options.pinoOptions?.hooks,
|
|
44
|
+
streamWrite: (line) => {
|
|
45
|
+
const out = userStreamWrite ? userStreamWrite(line) : line;
|
|
46
|
+
forwardLogLine(out);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
if (options.destinationStream) this.pino = pino(pinoOpts, options.destinationStream);
|
|
51
|
+
else if (options.destination) this.pino = pino(pinoOpts, pino.destination(options.destination));
|
|
52
|
+
else this.pino = pino(pinoOpts);
|
|
53
|
+
}
|
|
54
|
+
log(level, data, msg) {
|
|
55
|
+
if (msg) this.pino[level](data, msg);
|
|
56
|
+
else this.pino[level](data);
|
|
57
|
+
}
|
|
58
|
+
child(bindings) {
|
|
59
|
+
return new PinoChildTransport(this.pino.child(bindings));
|
|
60
|
+
}
|
|
61
|
+
async flush() {
|
|
62
|
+
this.pino.flush();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Lightweight wrapper for a Pino child logger.
|
|
67
|
+
* Created by `PinoTransport.child()` — not intended for direct use.
|
|
68
|
+
*/
|
|
69
|
+
var PinoChildTransport = class PinoChildTransport {
|
|
70
|
+
pinoChild;
|
|
71
|
+
constructor(pinoChild) {
|
|
72
|
+
this.pinoChild = pinoChild;
|
|
73
|
+
}
|
|
74
|
+
log(level, data, msg) {
|
|
75
|
+
if (msg) this.pinoChild[level](data, msg);
|
|
76
|
+
else this.pinoChild[level](data);
|
|
77
|
+
}
|
|
78
|
+
child(bindings) {
|
|
79
|
+
return new PinoChildTransport(this.pinoChild.child(bindings));
|
|
80
|
+
}
|
|
81
|
+
async flush() {
|
|
82
|
+
this.pinoChild.flush();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/transports/console.ts
|
|
87
|
+
var LEVEL_TO_CONSOLE = {
|
|
88
|
+
trace: "debug",
|
|
89
|
+
debug: "debug",
|
|
90
|
+
info: "log",
|
|
91
|
+
warn: "warn",
|
|
92
|
+
error: "error",
|
|
93
|
+
fatal: "error"
|
|
94
|
+
};
|
|
95
|
+
var ConsoleTransport = class ConsoleTransport {
|
|
96
|
+
bindings;
|
|
97
|
+
/** Create a ConsoleTransport with optional pre-bound context fields. */
|
|
98
|
+
constructor(bindings) {
|
|
99
|
+
this.bindings = bindings ?? {};
|
|
100
|
+
}
|
|
101
|
+
/** Write a log entry to the appropriate `console` method. */
|
|
102
|
+
log(level, data, msg) {
|
|
103
|
+
const method = LEVEL_TO_CONSOLE[level];
|
|
104
|
+
const merged = {
|
|
105
|
+
...this.bindings,
|
|
106
|
+
...data
|
|
107
|
+
};
|
|
108
|
+
const hasData = Object.keys(merged).length > 0;
|
|
109
|
+
if (msg && hasData) console[method](`[${level}]`, msg, merged);
|
|
110
|
+
else if (msg) console[method](`[${level}]`, msg);
|
|
111
|
+
else if (hasData) console[method](`[${level}]`, merged);
|
|
112
|
+
}
|
|
113
|
+
/** Return a new ConsoleTransport with the given bindings merged in. */
|
|
114
|
+
child(bindings) {
|
|
115
|
+
return new ConsoleTransport({
|
|
116
|
+
...this.bindings,
|
|
117
|
+
...bindings
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/Logger.ts
|
|
123
|
+
var DEFAULT_REDACT = [
|
|
124
|
+
"password",
|
|
125
|
+
"token",
|
|
126
|
+
"apiKey",
|
|
127
|
+
"authorization",
|
|
128
|
+
"secret"
|
|
129
|
+
];
|
|
130
|
+
function getEnv(key) {
|
|
131
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.process === "object") return globalThis.process.env[key];
|
|
132
|
+
}
|
|
133
|
+
/** Detect Node.js runtime (vs browser / edge). */
|
|
134
|
+
function isNodeRuntime() {
|
|
135
|
+
try {
|
|
136
|
+
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function createDefaultTransport(config) {
|
|
142
|
+
if (!isNodeRuntime()) return new ConsoleTransport();
|
|
143
|
+
return new PinoTransport({
|
|
144
|
+
level: config.level ?? getEnv("LOG_LEVEL") ?? "info",
|
|
145
|
+
redact: config.redact ?? DEFAULT_REDACT,
|
|
146
|
+
destination: config.destination
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
var Logger = class Logger {
|
|
150
|
+
static transport;
|
|
151
|
+
static config = {};
|
|
152
|
+
options;
|
|
153
|
+
childTransport;
|
|
154
|
+
static {
|
|
155
|
+
Logger.transport = createDefaultTransport(Logger.config);
|
|
156
|
+
}
|
|
157
|
+
constructor(options = {}) {
|
|
158
|
+
this.options = options;
|
|
159
|
+
if (options.context || options.correlationId || options.namespace) {
|
|
160
|
+
const bindings = {
|
|
161
|
+
...options.namespace && { namespace: options.namespace },
|
|
162
|
+
...options.correlationId && { correlationId: options.correlationId },
|
|
163
|
+
...options.context
|
|
164
|
+
};
|
|
165
|
+
this.childTransport = Logger.transport.child(bindings);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Configure global logger settings. Call once at application startup.
|
|
170
|
+
*
|
|
171
|
+
* Supply a custom `transport` to replace the default Pino backend,
|
|
172
|
+
* or omit it to keep PinoTransport with the provided options.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* Logger.configure({ level: 'debug', redact: ['password', 'apiKey'] });
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* import { ConsoleTransport } from '@eventuras/logger';
|
|
179
|
+
* Logger.configure({ transport: new ConsoleTransport() });
|
|
180
|
+
*/
|
|
181
|
+
static configure(config) {
|
|
182
|
+
Logger.config = {
|
|
183
|
+
...Logger.config,
|
|
184
|
+
...config
|
|
185
|
+
};
|
|
186
|
+
Logger.transport = Logger.config.transport ?? createDefaultTransport(Logger.config);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get the active transport for advanced integrations.
|
|
190
|
+
*
|
|
191
|
+
* If you need access to the underlying Pino instance (e.g. for OTel
|
|
192
|
+
* instrumentation), check `transport instanceof PinoTransport` and
|
|
193
|
+
* access `.pino` on it.
|
|
194
|
+
*/
|
|
195
|
+
static getTransport() {
|
|
196
|
+
return Logger.transport;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* @deprecated Since 0.7 — will be removed in 1.0. Use `Logger.getTransport()`
|
|
200
|
+
* instead. If you need the raw Pino instance, cast the transport:
|
|
201
|
+
* `(Logger.getTransport() as PinoTransport).pino`.
|
|
202
|
+
*/
|
|
203
|
+
static getPinoInstance() {
|
|
204
|
+
if (Logger.transport instanceof PinoTransport) return Logger.transport.pino;
|
|
205
|
+
throw new Error("getPinoInstance() requires PinoTransport. Use Logger.getTransport() for the active transport.");
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Normalize arguments for static log methods.
|
|
209
|
+
* Supports both `Logger.info('msg')` and `Logger.info({ namespace: 'x' }, 'msg')`.
|
|
210
|
+
*/
|
|
211
|
+
static normalizeArgs(optionsOrMsg, rest) {
|
|
212
|
+
if (typeof optionsOrMsg === "string") return [{}, [optionsOrMsg, ...rest]];
|
|
213
|
+
return [optionsOrMsg, rest];
|
|
214
|
+
}
|
|
215
|
+
static isDevelopment() {
|
|
216
|
+
return getEnv("NODE_ENV") === "development";
|
|
217
|
+
}
|
|
218
|
+
static formatError(error) {
|
|
219
|
+
if (error instanceof Error) return `${error.name}: ${error.message}\nStack: ${error.stack}`;
|
|
220
|
+
return String(error);
|
|
221
|
+
}
|
|
222
|
+
static buildLogData(options) {
|
|
223
|
+
return {
|
|
224
|
+
...options.namespace && { namespace: options.namespace },
|
|
225
|
+
...options.correlationId && { correlationId: options.correlationId },
|
|
226
|
+
...options.context
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
static staticLog(level, options, ...msg) {
|
|
230
|
+
if (options.developerOnly && !Logger.isDevelopment()) return;
|
|
231
|
+
const data = Logger.buildLogData(options);
|
|
232
|
+
Logger.transport.log(level, {
|
|
233
|
+
...data,
|
|
234
|
+
msg
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
static staticErrorLog(level, options, ...msg) {
|
|
238
|
+
if (options.developerOnly && !Logger.isDevelopment()) return;
|
|
239
|
+
const errorInfo = options.error ? { error: Logger.formatError(options.error) } : {};
|
|
240
|
+
const data = {
|
|
241
|
+
...Logger.buildLogData(options),
|
|
242
|
+
...errorInfo
|
|
243
|
+
};
|
|
244
|
+
Logger.transport.log(level, {
|
|
245
|
+
...data,
|
|
246
|
+
msg
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
static info(optionsOrMsg, ...msg) {
|
|
250
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
251
|
+
Logger.staticLog("info", options, ...messages);
|
|
252
|
+
}
|
|
253
|
+
static debug(optionsOrMsg, ...msg) {
|
|
254
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
255
|
+
Logger.staticLog("debug", options, ...messages);
|
|
256
|
+
}
|
|
257
|
+
static trace(optionsOrMsg, ...msg) {
|
|
258
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
259
|
+
Logger.staticLog("trace", options, ...messages);
|
|
260
|
+
}
|
|
261
|
+
static warn(optionsOrMsg, ...msg) {
|
|
262
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
263
|
+
Logger.staticLog("warn", options, ...messages);
|
|
264
|
+
}
|
|
265
|
+
static error(optionsOrMsg, ...msg) {
|
|
266
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
267
|
+
Logger.staticErrorLog("error", options, ...messages);
|
|
268
|
+
}
|
|
269
|
+
static fatal(optionsOrMsg, ...msg) {
|
|
270
|
+
const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);
|
|
271
|
+
Logger.staticErrorLog("fatal", options, ...messages);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Create a scoped logger instance with predefined options.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* const logger = Logger.create({ namespace: 'CollectionEditor' });
|
|
278
|
+
* logger.info('Something happened');
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* const logger = Logger.create({
|
|
282
|
+
* namespace: 'API',
|
|
283
|
+
* context: { userId: 123 },
|
|
284
|
+
* correlationId: req.headers['x-correlation-id'],
|
|
285
|
+
* });
|
|
286
|
+
* logger.info({ eventId: 789 }, 'Event added');
|
|
287
|
+
*/
|
|
288
|
+
static create(options = {}) {
|
|
289
|
+
return new Logger(options);
|
|
290
|
+
}
|
|
291
|
+
logInstance(level, data, msg) {
|
|
292
|
+
const transport = this.childTransport ?? Logger.transport;
|
|
293
|
+
if (typeof data === "string") transport.log(level, {}, data);
|
|
294
|
+
else if (msg) transport.log(level, data ?? {}, msg);
|
|
295
|
+
else transport.log(level, data ?? {});
|
|
296
|
+
}
|
|
297
|
+
/** Log at `trace` level. Pass a string or `{ data }` with an optional message. */
|
|
298
|
+
trace(data, msg) {
|
|
299
|
+
this.logInstance("trace", data, msg);
|
|
300
|
+
}
|
|
301
|
+
/** Log at `debug` level. */
|
|
302
|
+
debug(data, msg) {
|
|
303
|
+
this.logInstance("debug", data, msg);
|
|
304
|
+
}
|
|
305
|
+
/** Log at `info` level. */
|
|
306
|
+
info(data, msg) {
|
|
307
|
+
this.logInstance("info", data, msg);
|
|
308
|
+
}
|
|
309
|
+
/** Log at `warn` level. */
|
|
310
|
+
warn(data, msg) {
|
|
311
|
+
this.logInstance("warn", data, msg);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Log at `error` level. Accepts an `Error` instance, a data object, or a plain string.
|
|
315
|
+
* Error instances are serialized automatically.
|
|
316
|
+
*/
|
|
317
|
+
error(errorOrData, msg) {
|
|
318
|
+
const transport = this.childTransport ?? Logger.transport;
|
|
319
|
+
if (typeof errorOrData === "string") transport.log("error", {}, errorOrData);
|
|
320
|
+
else if (errorOrData instanceof Error) transport.log("error", { error: errorOrData }, msg);
|
|
321
|
+
else if (msg) transport.log("error", errorOrData ?? {}, msg);
|
|
322
|
+
else transport.log("error", errorOrData ?? {});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Log at `fatal` level. Same signature as `error()` but signals a critical/shutdown failure.
|
|
326
|
+
*/
|
|
327
|
+
fatal(errorOrData, msg) {
|
|
328
|
+
const transport = this.childTransport ?? Logger.transport;
|
|
329
|
+
if (typeof errorOrData === "string") transport.log("fatal", {}, errorOrData);
|
|
330
|
+
else if (errorOrData instanceof Error) transport.log("fatal", { error: errorOrData }, msg);
|
|
331
|
+
else if (msg) transport.log("fatal", errorOrData ?? {}, msg);
|
|
332
|
+
else transport.log("fatal", errorOrData ?? {});
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
//#endregion
|
|
336
|
+
export { setLogLineSink as i, ConsoleTransport as n, PinoTransport as r, Logger as t };
|
|
337
|
+
|
|
338
|
+
//# sourceMappingURL=Logger-B22dX10w.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Logger-B22dX10w.js","names":[],"sources":["../src/sink.ts","../src/transports/pino.ts","../src/transports/console.ts","../src/Logger.ts"],"sourcesContent":["/**\n * Process-wide hook for the serialized log lines Pino writes.\n *\n * `PinoTransport` hands every line (after redaction) to the sink, and\n * `@eventuras/logger/opentelemetry` sets the sink to forward lines as\n * OpenTelemetry log records.\n *\n * The sink lives on `globalThis` rather than in module state because a\n * bundler can load this package more than once in one process — Next.js\n * compiles `instrumentation.ts` into its own layer — and the copy that sets\n * the sink up is not necessarily the one whose loggers write the lines.\n */\n\nexport type LogLineSink = (line: string) => void;\n\nconst SINK_KEY = Symbol.for('@eventuras/logger:line-sink');\n\ntype SinkHolder = { [SINK_KEY]?: LogLineSink };\n\nexport function setLogLineSink(sink: LogLineSink | undefined): void {\n (globalThis as SinkHolder)[SINK_KEY] = sink;\n}\n\n/** Forward a line to the sink, if any. Never throws — telemetry must not break logging. */\nexport function forwardLogLine(line: string): void {\n const sink = (globalThis as SinkHolder)[SINK_KEY];\n if (!sink) return;\n try {\n sink(line);\n } catch {\n // Dropped: a failing exporter or an unparseable line must not stop the\n // line from reaching the primary destination.\n }\n}\n","/**\n * Pino-based transport — the default production backend.\n *\n * Wraps a Pino logger instance to satisfy the `LogTransport` interface,\n * keeping Pino as an implementation detail that consumers never interact with directly.\n *\n * For pretty-printed dev output, see `configureNodeLogger` in\n * `@eventuras/logger/node` — this module intentionally stays free of\n * `node:stream` imports so the main entry stays browser/edge-safe.\n */\nimport pino, { type Logger as PinoLogger, type LoggerOptions as PinoLoggerOptions } from 'pino';\nimport { forwardLogLine } from '../sink';\nimport type { LogLevel, LogTransport } from '../types';\n\n/**\n * Minimal structural type for a Pino destination stream — accepts anything\n * with a `write` method. Defined locally so this file (re-exported from the\n * universal `@eventuras/logger` entry) doesn't pull `NodeJS.*` types into\n * browser/edge consumers that don't ship `@types/node`.\n */\nexport interface PinoDestinationStream {\n write(chunk: string | Uint8Array): unknown;\n}\n\n/** Options for creating a PinoTransport. */\nexport type PinoTransportOptions = {\n /** Minimum log level. Defaults to `'info'`. */\n level?: LogLevel;\n /** Field paths to redact from output. */\n redact?: string[];\n /** File path destination (omit for stdout). */\n destination?: string;\n /**\n * Writable stream destination (e.g. a pretty-print stream from\n * `@eventuras/logger/node`). Takes precedence over `destination`.\n */\n destinationStream?: PinoDestinationStream;\n /** Raw Pino options for advanced tuning (merged after built-in defaults). */\n pinoOptions?: PinoLoggerOptions;\n};\n\nexport class PinoTransport implements LogTransport {\n /** The underlying Pino instance. Exposed for advanced integrations only. */\n readonly pino: PinoLogger;\n\n constructor(options: PinoTransportOptions = {}) {\n const pinoOpts: PinoLoggerOptions = {\n level: options.level ?? 'info',\n // ISO timestamps for Loki/Grafana compatibility\n timestamp: pino.stdTimeFunctions.isoTime,\n // Output level as string label — avoids numeric mapping in log pipelines\n formatters: {\n level: (label) => ({ level: label }),\n },\n ...(options.redact && {\n redact: { paths: options.redact, censor: '[REDACTED]' },\n }),\n ...options.pinoOptions,\n };\n\n // Hand every serialized line — after redaction, and from child loggers\n // too, since they inherit hooks — to the OpenTelemetry bridge when one is\n // set up. A caller-supplied streamWrite hook still runs first.\n const userStreamWrite = options.pinoOptions?.hooks?.streamWrite;\n pinoOpts.hooks = {\n ...options.pinoOptions?.hooks,\n streamWrite: (line) => {\n const out = userStreamWrite ? userStreamWrite(line) : line;\n forwardLogLine(out);\n return out;\n },\n };\n\n if (options.destinationStream) {\n // Pino's overload expects a NodeJS.WritableStream; the structural\n // PinoDestinationStream is a strict subset (only `.write` is read at\n // runtime), so the cast is safe.\n this.pino = pino(pinoOpts, options.destinationStream as Parameters<typeof pino>[1]);\n } else if (options.destination) {\n this.pino = pino(pinoOpts, pino.destination(options.destination));\n } else {\n this.pino = pino(pinoOpts);\n }\n }\n\n log(level: LogLevel, data: Record<string, unknown>, msg?: string): void {\n if (msg) {\n this.pino[level](data, msg);\n } else {\n this.pino[level](data);\n }\n }\n\n child(bindings: Record<string, unknown>): LogTransport {\n const childPino = this.pino.child(bindings);\n return new PinoChildTransport(childPino);\n }\n\n async flush(): Promise<void> {\n this.pino.flush();\n }\n}\n\n/**\n * Lightweight wrapper for a Pino child logger.\n * Created by `PinoTransport.child()` — not intended for direct use.\n */\nclass PinoChildTransport implements LogTransport {\n constructor(private readonly pinoChild: PinoLogger) { }\n\n log(level: LogLevel, data: Record<string, unknown>, msg?: string): void {\n if (msg) {\n this.pinoChild[level](data, msg);\n } else {\n this.pinoChild[level](data);\n }\n }\n\n child(bindings: Record<string, unknown>): LogTransport {\n return new PinoChildTransport(this.pinoChild.child(bindings));\n }\n\n async flush(): Promise<void> {\n this.pinoChild.flush();\n }\n}\n","/**\n * Console-based transport for browser environments and testing.\n *\n * Uses `console.log/warn/error` — no dependencies, works everywhere.\n * Useful as a lightweight fallback when Pino is not available or desired.\n */\nimport type { LogLevel, LogTransport } from '../types';\n\nconst LEVEL_TO_CONSOLE: Record<LogLevel, 'log' | 'warn' | 'error' | 'debug'> = {\n trace: 'debug',\n debug: 'debug',\n info: 'log',\n warn: 'warn',\n error: 'error',\n fatal: 'error',\n};\n\nexport class ConsoleTransport implements LogTransport {\n private readonly bindings: Record<string, unknown>;\n\n /** Create a ConsoleTransport with optional pre-bound context fields. */\n constructor(bindings?: Record<string, unknown>) {\n this.bindings = bindings ?? {};\n }\n\n /** Write a log entry to the appropriate `console` method. */\n log(level: LogLevel, data: Record<string, unknown>, msg?: string): void {\n const method = LEVEL_TO_CONSOLE[level];\n const merged = { ...this.bindings, ...data };\n const hasData = Object.keys(merged).length > 0;\n\n if (msg && hasData) {\n console[method](`[${level}]`, msg, merged);\n } else if (msg) {\n console[method](`[${level}]`, msg);\n } else if (hasData) {\n console[method](`[${level}]`, merged);\n }\n }\n\n /** Return a new ConsoleTransport with the given bindings merged in. */\n child(bindings: Record<string, unknown>): LogTransport {\n return new ConsoleTransport({ ...this.bindings, ...bindings });\n }\n}\n","/**\n * Structured logger with pluggable transports.\n *\n * Uses a `LogTransport` abstraction so the logging backend can be swapped\n * without changing application code. Ships with PinoTransport (default,\n * production-grade) and ConsoleTransport (browser/testing).\n *\n * Standard log levels:\n * fatal: 60 | error: 50 | warn: 40 | info: 30 | debug: 20 | trace: 10\n *\n * @example\n * // Scoped logger (recommended pattern)\n * const logger = Logger.create({\n * namespace: 'CollectionEditor',\n * context: { collectionId: 123 },\n * });\n * logger.info('Event added', { eventId: 456 });\n *\n * @example\n * // Static one-off logs\n * Logger.info('Simple message');\n * Logger.error({ error: err }, 'Something failed');\n *\n * @example\n * // Custom transport\n * import { ConsoleTransport } from '@eventuras/logger';\n * Logger.configure({ transport: new ConsoleTransport() });\n */\nimport type {\n ErrorLoggerOptions,\n LoggerConfig,\n LoggerOptions,\n LogLevel,\n LogTransport,\n} from './types';\nimport { PinoTransport } from './transports/pino';\nimport { ConsoleTransport } from './transports/console';\n\nconst DEFAULT_REDACT = ['password', 'token', 'apiKey', 'authorization', 'secret'];\n\nfunction getEnv(key: string): string | undefined {\n if (typeof globalThis !== 'undefined' && typeof (globalThis as Record<string, unknown>).process === 'object') {\n return (globalThis as unknown as { process: { env: Record<string, string | undefined>; }; }).process.env[key];\n }\n return undefined;\n}\n\n/** Detect Node.js runtime (vs browser / edge). */\nfunction isNodeRuntime(): boolean {\n try {\n return typeof process !== 'undefined' && typeof process.versions?.node === 'string';\n } catch {\n return false;\n }\n}\n\nfunction createDefaultTransport(config: LoggerConfig): LogTransport {\n // In non-Node environments (browser, edge), fall back to ConsoleTransport\n if (!isNodeRuntime()) {\n return new ConsoleTransport();\n }\n\n return new PinoTransport({\n level: config.level ?? (getEnv('LOG_LEVEL') as LogLevel | undefined) ?? 'info',\n redact: config.redact ?? DEFAULT_REDACT,\n destination: config.destination,\n });\n}\n\nexport class Logger {\n private static transport: LogTransport;\n private static config: LoggerConfig = {};\n\n // Instance properties for scoped logger\n private readonly options: LoggerOptions;\n private readonly childTransport?: LogTransport;\n\n static {\n Logger.transport = createDefaultTransport(Logger.config);\n }\n\n private constructor(options: LoggerOptions = {}) {\n this.options = options;\n\n if (options.context || options.correlationId || options.namespace) {\n const bindings: Record<string, unknown> = {\n ...(options.namespace && { namespace: options.namespace }),\n ...(options.correlationId && { correlationId: options.correlationId }),\n ...options.context,\n };\n this.childTransport = Logger.transport.child(bindings);\n }\n }\n\n /**\n * Configure global logger settings. Call once at application startup.\n *\n * Supply a custom `transport` to replace the default Pino backend,\n * or omit it to keep PinoTransport with the provided options.\n *\n * @example\n * Logger.configure({ level: 'debug', redact: ['password', 'apiKey'] });\n *\n * @example\n * import { ConsoleTransport } from '@eventuras/logger';\n * Logger.configure({ transport: new ConsoleTransport() });\n */\n static configure(config: Partial<LoggerConfig>): void {\n Logger.config = { ...Logger.config, ...config };\n Logger.transport = Logger.config.transport ?? createDefaultTransport(Logger.config);\n }\n\n /**\n * Get the active transport for advanced integrations.\n *\n * If you need access to the underlying Pino instance (e.g. for OTel\n * instrumentation), check `transport instanceof PinoTransport` and\n * access `.pino` on it.\n */\n static getTransport(): LogTransport {\n return Logger.transport;\n }\n\n /**\n * @deprecated Since 0.7 — will be removed in 1.0. Use `Logger.getTransport()`\n * instead. If you need the raw Pino instance, cast the transport:\n * `(Logger.getTransport() as PinoTransport).pino`.\n */\n static getPinoInstance(): import('pino').Logger {\n if (Logger.transport instanceof PinoTransport) {\n return Logger.transport.pino;\n }\n throw new Error(\n 'getPinoInstance() requires PinoTransport. Use Logger.getTransport() for the active transport.',\n );\n }\n\n // --- Static convenience methods ---\n\n /**\n * Normalize arguments for static log methods.\n * Supports both `Logger.info('msg')` and `Logger.info({ namespace: 'x' }, 'msg')`.\n */\n private static normalizeArgs(\n optionsOrMsg: LoggerOptions | string,\n rest: unknown[],\n ): [LoggerOptions, unknown[]] {\n if (typeof optionsOrMsg === 'string') {\n return [{}, [optionsOrMsg, ...rest]];\n }\n return [optionsOrMsg, rest];\n }\n\n private static isDevelopment(): boolean {\n return getEnv('NODE_ENV') === 'development';\n }\n\n private static formatError(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}\\nStack: ${error.stack}`;\n }\n return String(error);\n }\n\n private static buildLogData(options: LoggerOptions): Record<string, unknown> {\n return {\n ...(options.namespace && { namespace: options.namespace }),\n ...(options.correlationId && { correlationId: options.correlationId }),\n ...options.context,\n };\n }\n\n private static staticLog(\n level: LogLevel,\n options: LoggerOptions,\n ...msg: unknown[]\n ): void {\n if (options.developerOnly && !Logger.isDevelopment()) return;\n const data = Logger.buildLogData(options);\n Logger.transport.log(level, { ...data, msg });\n }\n\n private static staticErrorLog(\n level: LogLevel,\n options: ErrorLoggerOptions,\n ...msg: unknown[]\n ): void {\n if (options.developerOnly && !Logger.isDevelopment()) return;\n const errorInfo = options.error ? { error: Logger.formatError(options.error) } : {};\n const data = { ...Logger.buildLogData(options), ...errorInfo };\n Logger.transport.log(level, { ...data, msg });\n }\n\n /** Log at info level with options and message(s). */\n static info(options: LoggerOptions, ...msg: unknown[]): void;\n /** Log at info level with just a message string. */\n static info(msg: string, ...args: unknown[]): void;\n static info(optionsOrMsg: LoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticLog('info', options, ...messages);\n }\n\n /** Log at debug level with options and message(s). */\n static debug(options: LoggerOptions, ...msg: unknown[]): void;\n /** Log at debug level with just a message string. */\n static debug(msg: string, ...args: unknown[]): void;\n static debug(optionsOrMsg: LoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticLog('debug', options, ...messages);\n }\n\n /** Log at trace level with options and message(s). */\n static trace(options: LoggerOptions, ...msg: unknown[]): void;\n /** Log at trace level with just a message string. */\n static trace(msg: string, ...args: unknown[]): void;\n static trace(optionsOrMsg: LoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticLog('trace', options, ...messages);\n }\n\n /** Log at warn level with options and message(s). */\n static warn(options: LoggerOptions, ...msg: unknown[]): void;\n /** Log at warn level with just a message string. */\n static warn(msg: string, ...args: unknown[]): void;\n static warn(optionsOrMsg: LoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticLog('warn', options, ...messages);\n }\n\n /** Log at error level with options and message(s). */\n static error(options: ErrorLoggerOptions, ...msg: unknown[]): void;\n /** Log at error level with just a message string. */\n static error(msg: string, ...args: unknown[]): void;\n static error(optionsOrMsg: ErrorLoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticErrorLog('error', options, ...messages);\n }\n\n /** Log at fatal level with options and message(s). */\n static fatal(options: ErrorLoggerOptions, ...msg: unknown[]): void;\n /** Log at fatal level with just a message string. */\n static fatal(msg: string, ...args: unknown[]): void;\n static fatal(optionsOrMsg: ErrorLoggerOptions | string, ...msg: unknown[]): void {\n const [options, messages] = Logger.normalizeArgs(optionsOrMsg, msg);\n Logger.staticErrorLog('fatal', options, ...messages);\n }\n\n /**\n * Create a scoped logger instance with predefined options.\n *\n * @example\n * const logger = Logger.create({ namespace: 'CollectionEditor' });\n * logger.info('Something happened');\n *\n * @example\n * const logger = Logger.create({\n * namespace: 'API',\n * context: { userId: 123 },\n * correlationId: req.headers['x-correlation-id'],\n * });\n * logger.info({ eventId: 789 }, 'Event added');\n */\n static create(options: LoggerOptions = {}): Logger {\n return new Logger(options);\n }\n\n // --- Instance methods ---\n\n private logInstance(level: LogLevel, data?: Record<string, unknown> | string, msg?: string): void {\n const transport = this.childTransport ?? Logger.transport;\n if (typeof data === 'string') {\n transport.log(level, {}, data);\n } else if (msg) {\n transport.log(level, data ?? {}, msg);\n } else {\n transport.log(level, data ?? {});\n }\n }\n\n /** Log at `trace` level. Pass a string or `{ data }` with an optional message. */\n trace(data?: Record<string, unknown> | string, msg?: string): void {\n this.logInstance('trace', data, msg);\n }\n\n /** Log at `debug` level. */\n debug(data?: Record<string, unknown> | string, msg?: string): void {\n this.logInstance('debug', data, msg);\n }\n\n /** Log at `info` level. */\n info(data?: Record<string, unknown> | string, msg?: string): void {\n this.logInstance('info', data, msg);\n }\n\n /** Log at `warn` level. */\n warn(data?: Record<string, unknown> | string, msg?: string): void {\n this.logInstance('warn', data, msg);\n }\n\n /**\n * Log at `error` level. Accepts an `Error` instance, a data object, or a plain string.\n * Error instances are serialized automatically.\n */\n error(errorOrData?: unknown, msg?: string): void {\n const transport = this.childTransport ?? Logger.transport;\n if (typeof errorOrData === 'string') {\n transport.log('error', {}, errorOrData);\n } else if (errorOrData instanceof Error) {\n transport.log('error', { error: errorOrData }, msg);\n } else if (msg) {\n transport.log('error', (errorOrData as Record<string, unknown>) ?? {}, msg);\n } else {\n transport.log('error', (errorOrData as Record<string, unknown>) ?? {});\n }\n }\n\n /**\n * Log at `fatal` level. Same signature as `error()` but signals a critical/shutdown failure.\n */\n fatal(errorOrData?: unknown, msg?: string): void {\n const transport = this.childTransport ?? Logger.transport;\n if (typeof errorOrData === 'string') {\n transport.log('fatal', {}, errorOrData);\n } else if (errorOrData instanceof Error) {\n transport.log('fatal', { error: errorOrData }, msg);\n } else if (msg) {\n transport.log('fatal', (errorOrData as Record<string, unknown>) ?? {}, msg);\n } else {\n transport.log('fatal', (errorOrData as Record<string, unknown>) ?? {});\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { n as ConsoleTransport, r as PinoTransport, t as Logger } from "./Logger-B22dX10w.js";
|
|
2
2
|
//#region src/httpLogger.ts
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* HTTP logging utilities for API clients.
|
|
5
|
+
* Provides header redaction for structured logging.
|
|
6
|
+
*/
|
|
7
|
+
var SENSITIVE_HEADERS = [
|
|
4
8
|
"authorization",
|
|
5
9
|
"cookie",
|
|
6
10
|
"set-cookie",
|
|
@@ -8,15 +12,32 @@ var r = [
|
|
|
8
12
|
"x-auth-token",
|
|
9
13
|
"proxy-authorization"
|
|
10
14
|
];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Redact sensitive headers for logging.
|
|
17
|
+
* Use with logger.debug/info/error to safely log HTTP headers.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* logger.debug({
|
|
21
|
+
* request: {
|
|
22
|
+
* url: '/api/users',
|
|
23
|
+
* headers: redactHeaders(headers)
|
|
24
|
+
* }
|
|
25
|
+
* }, 'HTTP request');
|
|
26
|
+
*/
|
|
27
|
+
function redactHeaders(headers) {
|
|
28
|
+
const result = {};
|
|
29
|
+
if (headers instanceof Headers) headers.forEach((value, key) => {
|
|
30
|
+
result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "[REDACTED]" : value;
|
|
31
|
+
});
|
|
32
|
+
else if (Array.isArray(headers)) headers.forEach(([key, value]) => {
|
|
33
|
+
result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "[REDACTED]" : String(value);
|
|
34
|
+
});
|
|
35
|
+
else Object.entries(headers).forEach(([key, value]) => {
|
|
36
|
+
result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "[REDACTED]" : String(value);
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
20
39
|
}
|
|
21
40
|
//#endregion
|
|
22
|
-
export {
|
|
41
|
+
export { ConsoleTransport, Logger, PinoTransport, redactHeaders };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/httpLogger.ts"],"sourcesContent":["/**\n * HTTP logging utilities for API clients.\n * Provides header redaction for structured logging.\n */\n\nconst SENSITIVE_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'x-auth-token',\n 'proxy-authorization',\n];\n\n/**\n * Redact sensitive headers for logging.\n * Use with logger.debug/info/error to safely log HTTP headers.\n *\n * @example\n * logger.debug({\n * request: {\n * url: '/api/users',\n * headers: redactHeaders(headers)\n * }\n * }, 'HTTP request');\n */\nexport function redactHeaders(\n headers: Headers | Record<string, unknown> | [string, string][],\n): Record<string, string> {\n const result: Record<string, string> = {};\n\n if (headers instanceof Headers) {\n headers.forEach((value: string, key: string) => {\n result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n } else if (Array.isArray(headers)) {\n headers.forEach(([key, value]) => {\n result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? '[REDACTED]' : String(value);\n });\n } else {\n Object.entries(headers).forEach(([key, value]) => {\n result[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? '[REDACTED]' : String(value);\n });\n }\n\n return result;\n}\n"],"mappings":";;;;;;AAKA,IAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;AAcA,SAAgB,cACd,SACwB;CACxB,MAAM,SAAiC,CAAC;CAExC,IAAI,mBAAmB,SACrB,QAAQ,SAAS,OAAe,QAAgB;EAC9C,OAAO,OAAO,kBAAkB,SAAS,IAAI,YAAY,CAAC,IAAI,eAAe;CAC/E,CAAC;MACI,IAAI,MAAM,QAAQ,OAAO,GAC9B,QAAQ,SAAS,CAAC,KAAK,WAAW;EAChC,OAAO,OAAO,kBAAkB,SAAS,IAAI,YAAY,CAAC,IAAI,eAAe,OAAO,KAAK;CAC3F,CAAC;MAED,OAAO,QAAQ,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAChD,OAAO,OAAO,kBAAkB,SAAS,IAAI,YAAY,CAAC,IAAI,eAAe,OAAO,KAAK;CAC3F,CAAC;CAGH,OAAO;AACT"}
|