@eventuras/logger 0.8.1 → 0.9.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 +11 -10
- package/dist/{Logger-n-HsscG5.js → Logger-B22dX10w.js} +26 -2
- package/dist/Logger-B22dX10w.js.map +1 -0
- package/dist/Logger.d.ts +29 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/node.js.map +1 -1
- package/dist/opentelemetry.d.ts +63 -44
- package/dist/opentelemetry.d.ts.map +1 -1
- package/dist/opentelemetry.js +127 -76
- package/dist/opentelemetry.js.map +1 -1
- package/dist/sink.d.ts +17 -0
- package/dist/sink.d.ts.map +1 -0
- package/dist/transports/console.d.ts +7 -1
- package/dist/transports/pino.d.ts +12 -2
- package/dist/transports/pino.d.ts.map +1 -1
- package/package.json +14 -24
- package/LICENSE +0 -674
- package/dist/Logger-n-HsscG5.js.map +0 -1
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
|
|
@@ -1,4 +1,18 @@
|
|
|
1
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
|
|
2
16
|
//#region src/transports/pino.ts
|
|
3
17
|
/**
|
|
4
18
|
* Pino-based transport — the default production backend.
|
|
@@ -24,6 +38,15 @@ var PinoTransport = class {
|
|
|
24
38
|
} },
|
|
25
39
|
...options.pinoOptions
|
|
26
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
|
+
};
|
|
27
50
|
if (options.destinationStream) this.pino = pino(pinoOpts, options.destinationStream);
|
|
28
51
|
else if (options.destination) this.pino = pino(pinoOpts, pino.destination(options.destination));
|
|
29
52
|
else this.pino = pino(pinoOpts);
|
|
@@ -44,6 +67,7 @@ var PinoTransport = class {
|
|
|
44
67
|
* Created by `PinoTransport.child()` — not intended for direct use.
|
|
45
68
|
*/
|
|
46
69
|
var PinoChildTransport = class PinoChildTransport {
|
|
70
|
+
pinoChild;
|
|
47
71
|
constructor(pinoChild) {
|
|
48
72
|
this.pinoChild = pinoChild;
|
|
49
73
|
}
|
|
@@ -309,6 +333,6 @@ var Logger = class Logger {
|
|
|
309
333
|
}
|
|
310
334
|
};
|
|
311
335
|
//#endregion
|
|
312
|
-
export { ConsoleTransport as n, PinoTransport as r, Logger as t };
|
|
336
|
+
export { setLogLineSink as i, ConsoleTransport as n, PinoTransport as r, Logger as t };
|
|
313
337
|
|
|
314
|
-
//# sourceMappingURL=Logger-
|
|
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/Logger.d.ts
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger with pluggable transports.
|
|
3
|
+
*
|
|
4
|
+
* Uses a `LogTransport` abstraction so the logging backend can be swapped
|
|
5
|
+
* without changing application code. Ships with PinoTransport (default,
|
|
6
|
+
* production-grade) and ConsoleTransport (browser/testing).
|
|
7
|
+
*
|
|
8
|
+
* Standard log levels:
|
|
9
|
+
* fatal: 60 | error: 50 | warn: 40 | info: 30 | debug: 20 | trace: 10
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Scoped logger (recommended pattern)
|
|
13
|
+
* const logger = Logger.create({
|
|
14
|
+
* namespace: 'CollectionEditor',
|
|
15
|
+
* context: { collectionId: 123 },
|
|
16
|
+
* });
|
|
17
|
+
* logger.info('Event added', { eventId: 456 });
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Static one-off logs
|
|
21
|
+
* Logger.info('Simple message');
|
|
22
|
+
* Logger.error({ error: err }, 'Something failed');
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Custom transport
|
|
26
|
+
* import { ConsoleTransport } from '@eventuras/logger';
|
|
27
|
+
* Logger.configure({ transport: new ConsoleTransport() });
|
|
28
|
+
*/
|
|
29
|
+
import type { ErrorLoggerOptions, LoggerConfig, LoggerOptions, LogTransport } from './types';
|
|
2
30
|
export declare class Logger {
|
|
3
31
|
private static transport;
|
|
4
32
|
private static config;
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +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;
|
|
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"}
|
package/dist/node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PinoTransportOptions } from './transports/pino';
|
|
2
|
-
import { LoggerConfig } from './types';
|
|
1
|
+
import { type PinoTransportOptions } from './transports/pino';
|
|
2
|
+
import type { LoggerConfig } from './types';
|
|
3
3
|
export { formatLogLine, createPrettyStream } from './transports/pretty';
|
|
4
4
|
/** Options for `configureNodeLogger`. */
|
|
5
5
|
export type NodeLoggerOptions = Omit<LoggerConfig, 'transport'> & {
|
package/dist/node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as PinoTransport, t as Logger } from "./Logger-
|
|
1
|
+
import { r as PinoTransport, t as Logger } from "./Logger-B22dX10w.js";
|
|
2
2
|
import { Writable } from "node:stream";
|
|
3
3
|
//#region src/transports/pretty.ts
|
|
4
4
|
/**
|
|
@@ -73,7 +73,7 @@ var LEVEL_BY_NAME = {
|
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
/** Keys excluded from the "extra data" output. */
|
|
76
|
-
var INTERNAL_KEYS = new Set([
|
|
76
|
+
var INTERNAL_KEYS = /* @__PURE__ */ new Set([
|
|
77
77
|
"level",
|
|
78
78
|
"time",
|
|
79
79
|
"pid",
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","names":[],"sources":["../src/transports/pretty.ts","../src/node.ts"],"sourcesContent":["/**\n * Lightweight pretty-print formatter for development.\n * Zero external dependencies — uses ANSI colors and simple formatting.\n *\n * Produces output like:\n * 12:34:56 INFO (web:auth) → User logged in\n * 12:34:56 ERROR (web:auth) → Failed to authenticate { error: \"invalid token\" }\n */\n\nimport { Writable } from 'node:stream';\n\nconst ANSI = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n green: '\\x1b[32m',\n cyan: '\\x1b[36m',\n magenta: '\\x1b[35m',\n gray: '\\x1b[90m',\n} as const;\n\nconst LEVEL_BY_NUMBER: Record<number, { label: string; color: string; }> = {\n 10: { label: 'TRACE', color: ANSI.gray },\n 20: { label: 'DEBUG', color: ANSI.cyan },\n 30: { label: 'INFO ', color: ANSI.green },\n 40: { label: 'WARN ', color: ANSI.yellow },\n 50: { label: 'ERROR', color: ANSI.red },\n 60: { label: 'FATAL', color: `${ANSI.bold}${ANSI.red}` },\n};\n\nconst LEVEL_BY_NAME: Record<string, { label: string; color: string; }> = {\n trace: { label: 'TRACE', color: ANSI.gray },\n debug: { label: 'DEBUG', color: ANSI.cyan },\n info: { label: 'INFO ', color: ANSI.green },\n warn: { label: 'WARN ', color: ANSI.yellow },\n error: { label: 'ERROR', color: ANSI.red },\n fatal: { label: 'FATAL', color: `${ANSI.bold}${ANSI.red}` },\n};\n\n/** Keys excluded from the \"extra data\" output. */\nconst INTERNAL_KEYS = new Set([\n 'level', 'time', 'pid', 'hostname', 'msg', 'name', 'ns', 'namespace',\n]);\n\nfunction formatTime(time: unknown): string {\n if (typeof time === 'string') {\n // ISO string — extract time portion\n const d = new Date(time);\n return Number.isNaN(d.getTime()) ? '' : d.toLocaleTimeString('en-GB', { hour12: false });\n }\n if (typeof time === 'number') {\n return new Date(time).toLocaleTimeString('en-GB', { hour12: false });\n }\n return new Date().toLocaleTimeString('en-GB', { hour12: false });\n}\n\nfunction formatData(obj: Record<string, unknown>): string {\n const filtered: Record<string, unknown> = {};\n let hasKeys = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (!INTERNAL_KEYS.has(key)) {\n filtered[key] = value;\n hasKeys = true;\n }\n }\n\n if (!hasKeys) return '';\n return ` ${ANSI.dim}${JSON.stringify(filtered)}${ANSI.reset}`;\n}\n\n/**\n * Format a Pino JSON log line into a human-readable string.\n * @param line Raw JSON string from Pino\n * @returns Formatted string, or the original line if parsing fails\n */\nexport function formatLogLine(line: string): string {\n const trimmed = line.trim();\n if (!trimmed) return '';\n\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(trimmed);\n } catch {\n return trimmed;\n }\n\n const level = obj.level;\n const config = typeof level === 'string'\n ? LEVEL_BY_NAME[level] ?? { label: level.toUpperCase().padEnd(5), color: ANSI.gray }\n : LEVEL_BY_NUMBER[level as number] ?? { label: `L${level}`, color: ANSI.gray };\n const time = formatTime(obj.time);\n const msg = (obj.msg as string) ?? '';\n const ns = (obj.namespace as string) || (obj.ns as string) || (obj.name as string) || '';\n\n const nsTag = ns ? ` ${ANSI.magenta}(${ns})${ANSI.reset}` : '';\n const arrow = `${ANSI.dim}→${ANSI.reset}`;\n const data = formatData(obj);\n\n return `${ANSI.dim}${time}${ANSI.reset} ${config.color}${config.label}${ANSI.reset}${nsTag} ${arrow} ${msg}${data}`;\n}\n\n/**\n * Creates a Node.js writable stream that formats Pino JSON output.\n * Used as the destination for PinoTransport when prettyPrint is enabled.\n */\nexport function createPrettyStream(): NodeJS.WritableStream {\n return new Writable({\n write(chunk: Buffer, _encoding: string, callback: () => void) {\n const lines = chunk.toString().split('\\n');\n for (const line of lines) {\n const formatted = formatLogLine(line);\n if (formatted) {\n process.stdout.write(formatted + '\\n');\n }\n }\n callback();\n },\n });\n}\n","/**\n * Node.js-only exports for @eventuras/logger.\n *\n * These utilities depend on `node:stream` and must not be imported in browser\n * or edge runtime environments. Import from the root entry point\n * (`@eventuras/logger`) for the browser-safe API.\n *\n * @example\n * // Pretty dev output\n * import { configureNodeLogger } from '@eventuras/logger/node';\n * configureNodeLogger({ prettyPrint: process.env.NODE_ENV === 'development' });\n *\n * @example\n * // Lower-level: just the pretty helpers\n * import { createPrettyStream, formatLogLine } from '@eventuras/logger/node';\n */\nimport { Logger } from './Logger';\nimport { PinoTransport, type PinoTransportOptions } from './transports/pino';\nimport { createPrettyStream } from './transports/pretty';\nimport type { LoggerConfig } from './types';\n\nexport { formatLogLine, createPrettyStream } from './transports/pretty';\n\n/** Options for `configureNodeLogger`. */\nexport type NodeLoggerOptions = Omit<LoggerConfig, 'transport'> & {\n /** Enable human-readable, ANSI-colored output. Off by default. */\n prettyPrint?: boolean;\n /** Raw PinoTransport options for advanced tuning. */\n pinoOptions?: PinoTransportOptions['pinoOptions'];\n};\n\n/**\n * Configure the global Logger with a Node-side PinoTransport, optionally\n * wired to a pretty-print stream for development. Keeps the browser/edge\n * main entry free of `node:stream` imports — call this from a Node-only\n * bootstrap (e.g. `instrumentation.ts`, `server.ts`).\n *\n * @example\n * // In your server entry point\n * import { configureNodeLogger } from '@eventuras/logger/node';\n * configureNodeLogger({\n * level: 'debug',\n * prettyPrint: process.env.NODE_ENV === 'development',\n * });\n */\nexport function configureNodeLogger(options: NodeLoggerOptions = {}): void {\n const { prettyPrint, pinoOptions, ...rest } = options;\n Logger.configure({\n ...rest,\n transport: new PinoTransport({\n level: rest.level,\n redact: rest.redact,\n destination: rest.destination,\n destinationStream: prettyPrint ? createPrettyStream() : undefined,\n pinoOptions,\n }),\n });\n}\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,OAAO;CACX,OAAO;CACP,KAAK;CACL,MAAM;CACN,KAAK;CACL,QAAQ;CACR,OAAO;CACP,MAAM;CACN,SAAS;CACT,MAAM;
|
|
1
|
+
{"version":3,"file":"node.js","names":[],"sources":["../src/transports/pretty.ts","../src/node.ts"],"sourcesContent":["/**\n * Lightweight pretty-print formatter for development.\n * Zero external dependencies — uses ANSI colors and simple formatting.\n *\n * Produces output like:\n * 12:34:56 INFO (web:auth) → User logged in\n * 12:34:56 ERROR (web:auth) → Failed to authenticate { error: \"invalid token\" }\n */\n\nimport { Writable } from 'node:stream';\n\nconst ANSI = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n green: '\\x1b[32m',\n cyan: '\\x1b[36m',\n magenta: '\\x1b[35m',\n gray: '\\x1b[90m',\n} as const;\n\nconst LEVEL_BY_NUMBER: Record<number, { label: string; color: string; }> = {\n 10: { label: 'TRACE', color: ANSI.gray },\n 20: { label: 'DEBUG', color: ANSI.cyan },\n 30: { label: 'INFO ', color: ANSI.green },\n 40: { label: 'WARN ', color: ANSI.yellow },\n 50: { label: 'ERROR', color: ANSI.red },\n 60: { label: 'FATAL', color: `${ANSI.bold}${ANSI.red}` },\n};\n\nconst LEVEL_BY_NAME: Record<string, { label: string; color: string; }> = {\n trace: { label: 'TRACE', color: ANSI.gray },\n debug: { label: 'DEBUG', color: ANSI.cyan },\n info: { label: 'INFO ', color: ANSI.green },\n warn: { label: 'WARN ', color: ANSI.yellow },\n error: { label: 'ERROR', color: ANSI.red },\n fatal: { label: 'FATAL', color: `${ANSI.bold}${ANSI.red}` },\n};\n\n/** Keys excluded from the \"extra data\" output. */\nconst INTERNAL_KEYS = new Set([\n 'level', 'time', 'pid', 'hostname', 'msg', 'name', 'ns', 'namespace',\n]);\n\nfunction formatTime(time: unknown): string {\n if (typeof time === 'string') {\n // ISO string — extract time portion\n const d = new Date(time);\n return Number.isNaN(d.getTime()) ? '' : d.toLocaleTimeString('en-GB', { hour12: false });\n }\n if (typeof time === 'number') {\n return new Date(time).toLocaleTimeString('en-GB', { hour12: false });\n }\n return new Date().toLocaleTimeString('en-GB', { hour12: false });\n}\n\nfunction formatData(obj: Record<string, unknown>): string {\n const filtered: Record<string, unknown> = {};\n let hasKeys = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (!INTERNAL_KEYS.has(key)) {\n filtered[key] = value;\n hasKeys = true;\n }\n }\n\n if (!hasKeys) return '';\n return ` ${ANSI.dim}${JSON.stringify(filtered)}${ANSI.reset}`;\n}\n\n/**\n * Format a Pino JSON log line into a human-readable string.\n * @param line Raw JSON string from Pino\n * @returns Formatted string, or the original line if parsing fails\n */\nexport function formatLogLine(line: string): string {\n const trimmed = line.trim();\n if (!trimmed) return '';\n\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(trimmed);\n } catch {\n return trimmed;\n }\n\n const level = obj.level;\n const config = typeof level === 'string'\n ? LEVEL_BY_NAME[level] ?? { label: level.toUpperCase().padEnd(5), color: ANSI.gray }\n : LEVEL_BY_NUMBER[level as number] ?? { label: `L${level}`, color: ANSI.gray };\n const time = formatTime(obj.time);\n const msg = (obj.msg as string) ?? '';\n const ns = (obj.namespace as string) || (obj.ns as string) || (obj.name as string) || '';\n\n const nsTag = ns ? ` ${ANSI.magenta}(${ns})${ANSI.reset}` : '';\n const arrow = `${ANSI.dim}→${ANSI.reset}`;\n const data = formatData(obj);\n\n return `${ANSI.dim}${time}${ANSI.reset} ${config.color}${config.label}${ANSI.reset}${nsTag} ${arrow} ${msg}${data}`;\n}\n\n/**\n * Creates a Node.js writable stream that formats Pino JSON output.\n * Used as the destination for PinoTransport when prettyPrint is enabled.\n */\nexport function createPrettyStream(): NodeJS.WritableStream {\n return new Writable({\n write(chunk: Buffer, _encoding: string, callback: () => void) {\n const lines = chunk.toString().split('\\n');\n for (const line of lines) {\n const formatted = formatLogLine(line);\n if (formatted) {\n process.stdout.write(formatted + '\\n');\n }\n }\n callback();\n },\n });\n}\n","/**\n * Node.js-only exports for @eventuras/logger.\n *\n * These utilities depend on `node:stream` and must not be imported in browser\n * or edge runtime environments. Import from the root entry point\n * (`@eventuras/logger`) for the browser-safe API.\n *\n * @example\n * // Pretty dev output\n * import { configureNodeLogger } from '@eventuras/logger/node';\n * configureNodeLogger({ prettyPrint: process.env.NODE_ENV === 'development' });\n *\n * @example\n * // Lower-level: just the pretty helpers\n * import { createPrettyStream, formatLogLine } from '@eventuras/logger/node';\n */\nimport { Logger } from './Logger';\nimport { PinoTransport, type PinoTransportOptions } from './transports/pino';\nimport { createPrettyStream } from './transports/pretty';\nimport type { LoggerConfig } from './types';\n\nexport { formatLogLine, createPrettyStream } from './transports/pretty';\n\n/** Options for `configureNodeLogger`. */\nexport type NodeLoggerOptions = Omit<LoggerConfig, 'transport'> & {\n /** Enable human-readable, ANSI-colored output. Off by default. */\n prettyPrint?: boolean;\n /** Raw PinoTransport options for advanced tuning. */\n pinoOptions?: PinoTransportOptions['pinoOptions'];\n};\n\n/**\n * Configure the global Logger with a Node-side PinoTransport, optionally\n * wired to a pretty-print stream for development. Keeps the browser/edge\n * main entry free of `node:stream` imports — call this from a Node-only\n * bootstrap (e.g. `instrumentation.ts`, `server.ts`).\n *\n * @example\n * // In your server entry point\n * import { configureNodeLogger } from '@eventuras/logger/node';\n * configureNodeLogger({\n * level: 'debug',\n * prettyPrint: process.env.NODE_ENV === 'development',\n * });\n */\nexport function configureNodeLogger(options: NodeLoggerOptions = {}): void {\n const { prettyPrint, pinoOptions, ...rest } = options;\n Logger.configure({\n ...rest,\n transport: new PinoTransport({\n level: rest.level,\n redact: rest.redact,\n destination: rest.destination,\n destinationStream: prettyPrint ? createPrettyStream() : undefined,\n pinoOptions,\n }),\n });\n}\n"],"mappings":"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"}
|
package/dist/opentelemetry.d.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenTelemetry integration for @eventuras/logger
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* without vendor lock-in.
|
|
4
|
+
* Sends logs to any OpenTelemetry-compatible backend (Sentry, Grafana, the
|
|
5
|
+
* Aspire dashboard, etc.) without vendor lock-in.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Every line the Pino transport writes — after redaction, from every logger,
|
|
8
|
+
* including ones created before setup — is also emitted as an OpenTelemetry
|
|
9
|
+
* log record. The logger bridges this itself rather than through
|
|
10
|
+
* `@opentelemetry/instrumentation-pino`, which only patches Pino when it is
|
|
11
|
+
* loaded through an import-in-the-middle loader hook, and so never saw this
|
|
12
|
+
* package's ESM import of Pino.
|
|
13
|
+
*
|
|
14
|
+
* `@opentelemetry/sdk-logs` is an optional peer dependency, needed only when
|
|
15
|
+
* passing `logRecordProcessor` — which comes from that package anyway, so the
|
|
16
|
+
* app already has it and the logger uses the app's copy.
|
|
17
|
+
*
|
|
18
|
+
* Setup never throws or rejects: if it can't start, it logs an error and
|
|
19
|
+
* leaves logging to stdout untouched.
|
|
10
20
|
*
|
|
11
21
|
* @example
|
|
12
22
|
* // In your app's instrumentation.ts or main entry point
|
|
@@ -14,13 +24,10 @@
|
|
|
14
24
|
* import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
15
25
|
* import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
16
26
|
*
|
|
17
|
-
* setupOpenTelemetryLogger({
|
|
18
|
-
* logRecordProcessor: new BatchLogRecordProcessor(
|
|
19
|
-
* new OTLPLogExporter(
|
|
20
|
-
*
|
|
21
|
-
* headers: { 'x-sentry-auth': 'sentry sentry_key=...' }
|
|
22
|
-
* })
|
|
23
|
-
* )
|
|
27
|
+
* await setupOpenTelemetryLogger({
|
|
28
|
+
* logRecordProcessor: new BatchLogRecordProcessor({
|
|
29
|
+
* exporter: new OTLPLogExporter(), // reads OTEL_EXPORTER_OTLP_* env vars
|
|
30
|
+
* }),
|
|
24
31
|
* });
|
|
25
32
|
*
|
|
26
33
|
* @example
|
|
@@ -36,16 +43,36 @@
|
|
|
36
43
|
* Defined locally to avoid requiring OTel types at compile time.
|
|
37
44
|
*/
|
|
38
45
|
export interface LogRecordProcessor {
|
|
46
|
+
onEmit(logRecord: unknown, context?: unknown): void;
|
|
39
47
|
shutdown(): Promise<void>;
|
|
40
48
|
forceFlush(): Promise<void>;
|
|
41
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Minimal shape of an OpenTelemetry log record, as emitted by this package.
|
|
52
|
+
* Loosely typed so the SDK's own `LogRecord` type is assignable to it.
|
|
53
|
+
*/
|
|
54
|
+
export type OTelLogRecord = {
|
|
55
|
+
timestamp?: unknown;
|
|
56
|
+
severityNumber?: number;
|
|
57
|
+
severityText?: string;
|
|
58
|
+
body?: unknown;
|
|
59
|
+
attributes?: Record<string, unknown>;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Minimal interface for an OpenTelemetry Logger.
|
|
63
|
+
* Compatible with `@opentelemetry/api-logs` `Logger`.
|
|
64
|
+
*/
|
|
65
|
+
export interface OTelLogger {
|
|
66
|
+
emit(record: OTelLogRecord): void;
|
|
67
|
+
}
|
|
42
68
|
/**
|
|
43
69
|
* Minimal interface for an OpenTelemetry LoggerProvider.
|
|
44
|
-
* Compatible with `@opentelemetry/sdk-logs` `LoggerProvider
|
|
70
|
+
* Compatible with `@opentelemetry/sdk-logs` `LoggerProvider` and the global
|
|
71
|
+
* provider from `@opentelemetry/api-logs`.
|
|
45
72
|
*/
|
|
46
73
|
export interface OTelLoggerProvider {
|
|
47
|
-
|
|
48
|
-
shutdown(): Promise<void>;
|
|
74
|
+
getLogger(name: string, version?: string): OTelLogger;
|
|
75
|
+
shutdown?(): Promise<void>;
|
|
49
76
|
forceFlush?(): Promise<void>;
|
|
50
77
|
}
|
|
51
78
|
/**
|
|
@@ -54,15 +81,19 @@ export interface OTelLoggerProvider {
|
|
|
54
81
|
export type OpenTelemetryLoggerOptions = {
|
|
55
82
|
/**
|
|
56
83
|
* Log record processor (e.g., BatchLogRecordProcessor with an exporter).
|
|
57
|
-
*
|
|
84
|
+
* A LoggerProvider is created for it. Requires `@opentelemetry/sdk-logs`.
|
|
58
85
|
*/
|
|
59
86
|
logRecordProcessor?: LogRecordProcessor;
|
|
60
87
|
/**
|
|
61
|
-
* Logger provider
|
|
88
|
+
* Logger provider to emit to, e.g. one your app already configured with a
|
|
89
|
+
* resource and processors. Takes precedence over `logRecordProcessor`.
|
|
90
|
+
*
|
|
91
|
+
* With neither option, the globally registered provider is used (as set up
|
|
92
|
+
* by `@opentelemetry/sdk-node`, for instance).
|
|
62
93
|
*/
|
|
63
94
|
loggerProvider?: OTelLoggerProvider;
|
|
64
95
|
/**
|
|
65
|
-
* Service name to
|
|
96
|
+
* Service name attached to every log record as the `service.name` attribute.
|
|
66
97
|
* Defaults to the `OTEL_SERVICE_NAME` environment variable, or `'unknown-service'`.
|
|
67
98
|
*/
|
|
68
99
|
serviceName?: string;
|
|
@@ -72,55 +103,43 @@ export type OpenTelemetryLoggerOptions = {
|
|
|
72
103
|
enabled?: boolean;
|
|
73
104
|
};
|
|
74
105
|
/**
|
|
75
|
-
* Set up OpenTelemetry integration for
|
|
106
|
+
* Set up OpenTelemetry integration for the logger.
|
|
76
107
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* 2. Registers the log record processor (for exporting logs)
|
|
80
|
-
* 3. Enables Pino instrumentation to bridge Pino logs to OTel
|
|
81
|
-
*
|
|
82
|
-
* Call this function once at application startup, before creating any loggers.
|
|
108
|
+
* Call once at application startup. Loggers created before the call are
|
|
109
|
+
* exported too. Calling again replaces the previous setup.
|
|
83
110
|
*
|
|
84
111
|
* @param options - Configuration options
|
|
85
112
|
*
|
|
86
113
|
* @example
|
|
87
114
|
* // Send to Sentry via OTLP
|
|
88
|
-
* import { setupOpenTelemetryLogger } from '@eventuras/logger';
|
|
115
|
+
* import { setupOpenTelemetryLogger } from '@eventuras/logger/opentelemetry';
|
|
89
116
|
* import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
90
117
|
* import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
91
118
|
*
|
|
92
|
-
* setupOpenTelemetryLogger({
|
|
93
|
-
* logRecordProcessor: new BatchLogRecordProcessor(
|
|
94
|
-
* new OTLPLogExporter({
|
|
119
|
+
* await setupOpenTelemetryLogger({
|
|
120
|
+
* logRecordProcessor: new BatchLogRecordProcessor({
|
|
121
|
+
* exporter: new OTLPLogExporter({
|
|
95
122
|
* url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
96
123
|
* headers: {
|
|
97
124
|
* 'x-sentry-auth': `sentry sentry_key=${process.env.SENTRY_KEY}`
|
|
98
125
|
* }
|
|
99
126
|
* })
|
|
100
|
-
* )
|
|
127
|
+
* })
|
|
101
128
|
* });
|
|
102
129
|
*
|
|
103
130
|
* @example
|
|
104
|
-
* // Use
|
|
105
|
-
*
|
|
106
|
-
* // OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://[org].ingest.sentry.io/api/[project]/integration/otlp/v1/logs
|
|
107
|
-
* // OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-sentry-auth=sentry sentry_key=...
|
|
108
|
-
*
|
|
109
|
-
* import { setupOpenTelemetryLogger } from '@eventuras/logger';
|
|
110
|
-
* import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
111
|
-
* import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
112
|
-
*
|
|
113
|
-
* await setupOpenTelemetryLogger({
|
|
114
|
-
* logRecordProcessor: new BatchLogRecordProcessor(
|
|
115
|
-
* new OTLPLogExporter() // Reads from env vars
|
|
116
|
-
* )
|
|
117
|
-
* });
|
|
131
|
+
* // Use a provider your app already registered globally (e.g. via NodeSDK)
|
|
132
|
+
* await setupOpenTelemetryLogger();
|
|
118
133
|
*/
|
|
119
134
|
export declare function setupOpenTelemetryLogger(options?: OpenTelemetryLoggerOptions): Promise<void>;
|
|
120
135
|
/**
|
|
121
136
|
* Shut down the OpenTelemetry logger integration.
|
|
122
137
|
* Call this when your application is shutting down to flush any pending logs.
|
|
123
138
|
*
|
|
139
|
+
* A provider created from `logRecordProcessor` is shut down. One passed as
|
|
140
|
+
* `loggerProvider`, or the global one, belongs to your app — it is flushed,
|
|
141
|
+
* not shut down.
|
|
142
|
+
*
|
|
124
143
|
* @example
|
|
125
144
|
* process.on('SIGTERM', async () => {
|
|
126
145
|
* await shutdownOpenTelemetryLogger();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opentelemetry.d.ts","sourceRoot":"","sources":["../src/opentelemetry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"opentelemetry.d.ts","sourceRoot":"","sources":["../src/opentelemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAOH;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACtD,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IAExC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC;IAEpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAmFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAoDf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAejE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,kBAAkB,GAAG,IAAI,CAO7D"}
|