@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/dist/node.js CHANGED
@@ -1,7 +1,15 @@
1
- import { r as e, t } from "./Logger-CcNEmm6u.js";
2
- import { Writable as n } from "node:stream";
1
+ import { r as PinoTransport, t as Logger } from "./Logger-B22dX10w.js";
2
+ import { Writable } from "node:stream";
3
3
  //#region src/transports/pretty.ts
4
- var r = {
4
+ /**
5
+ * Lightweight pretty-print formatter for development.
6
+ * Zero external dependencies — uses ANSI colors and simple formatting.
7
+ *
8
+ * Produces output like:
9
+ * 12:34:56 INFO (web:auth) → User logged in
10
+ * 12:34:56 ERROR (web:auth) → Failed to authenticate { error: "invalid token" }
11
+ */
12
+ var ANSI = {
5
13
  reset: "\x1B[0m",
6
14
  dim: "\x1B[2m",
7
15
  bold: "\x1B[1m",
@@ -11,57 +19,61 @@ var r = {
11
19
  cyan: "\x1B[36m",
12
20
  magenta: "\x1B[35m",
13
21
  gray: "\x1B[90m"
14
- }, i = {
22
+ };
23
+ var LEVEL_BY_NUMBER = {
15
24
  10: {
16
25
  label: "TRACE",
17
- color: r.gray
26
+ color: ANSI.gray
18
27
  },
19
28
  20: {
20
29
  label: "DEBUG",
21
- color: r.cyan
30
+ color: ANSI.cyan
22
31
  },
23
32
  30: {
24
33
  label: "INFO ",
25
- color: r.green
34
+ color: ANSI.green
26
35
  },
27
36
  40: {
28
37
  label: "WARN ",
29
- color: r.yellow
38
+ color: ANSI.yellow
30
39
  },
31
40
  50: {
32
41
  label: "ERROR",
33
- color: r.red
42
+ color: ANSI.red
34
43
  },
35
44
  60: {
36
45
  label: "FATAL",
37
- color: `${r.bold}${r.red}`
46
+ color: `${ANSI.bold}${ANSI.red}`
38
47
  }
39
- }, a = {
48
+ };
49
+ var LEVEL_BY_NAME = {
40
50
  trace: {
41
51
  label: "TRACE",
42
- color: r.gray
52
+ color: ANSI.gray
43
53
  },
44
54
  debug: {
45
55
  label: "DEBUG",
46
- color: r.cyan
56
+ color: ANSI.cyan
47
57
  },
48
58
  info: {
49
59
  label: "INFO ",
50
- color: r.green
60
+ color: ANSI.green
51
61
  },
52
62
  warn: {
53
63
  label: "WARN ",
54
- color: r.yellow
64
+ color: ANSI.yellow
55
65
  },
56
66
  error: {
57
67
  label: "ERROR",
58
- color: r.red
68
+ color: ANSI.red
59
69
  },
60
70
  fatal: {
61
71
  label: "FATAL",
62
- color: `${r.bold}${r.red}`
72
+ color: `${ANSI.bold}${ANSI.red}`
63
73
  }
64
- }, o = new Set([
74
+ };
75
+ /** Keys excluded from the "extra data" output. */
76
+ var INTERNAL_KEYS = /* @__PURE__ */ new Set([
65
77
  "level",
66
78
  "time",
67
79
  "pid",
@@ -71,60 +83,114 @@ var r = {
71
83
  "ns",
72
84
  "namespace"
73
85
  ]);
74
- function s(e) {
75
- if (typeof e == "string") {
76
- let t = new Date(e);
77
- return Number.isNaN(t.getTime()) ? "" : t.toLocaleTimeString("en-GB", { hour12: !1 });
86
+ function formatTime(time) {
87
+ if (typeof time === "string") {
88
+ const d = new Date(time);
89
+ return Number.isNaN(d.getTime()) ? "" : d.toLocaleTimeString("en-GB", { hour12: false });
78
90
  }
79
- return typeof e == "number" ? new Date(e).toLocaleTimeString("en-GB", { hour12: !1 }) : (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: !1 });
91
+ if (typeof time === "number") return new Date(time).toLocaleTimeString("en-GB", { hour12: false });
92
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false });
80
93
  }
81
- function c(e) {
82
- let t = {}, n = !1;
83
- for (let [r, i] of Object.entries(e)) o.has(r) || (t[r] = i, n = !0);
84
- return n ? ` ${r.dim}${JSON.stringify(t)}${r.reset}` : "";
94
+ function formatData(obj) {
95
+ const filtered = {};
96
+ let hasKeys = false;
97
+ for (const [key, value] of Object.entries(obj)) if (!INTERNAL_KEYS.has(key)) {
98
+ filtered[key] = value;
99
+ hasKeys = true;
100
+ }
101
+ if (!hasKeys) return "";
102
+ return ` ${ANSI.dim}${JSON.stringify(filtered)}${ANSI.reset}`;
85
103
  }
86
- function l(e) {
87
- let t = e.trim();
88
- if (!t) return "";
89
- let n;
104
+ /**
105
+ * Format a Pino JSON log line into a human-readable string.
106
+ * @param line Raw JSON string from Pino
107
+ * @returns Formatted string, or the original line if parsing fails
108
+ */
109
+ function formatLogLine(line) {
110
+ const trimmed = line.trim();
111
+ if (!trimmed) return "";
112
+ let obj;
90
113
  try {
91
- n = JSON.parse(t);
114
+ obj = JSON.parse(trimmed);
92
115
  } catch {
93
- return t;
116
+ return trimmed;
94
117
  }
95
- let o = n.level, l = typeof o == "string" ? a[o] ?? {
96
- label: o.toUpperCase().padEnd(5),
97
- color: r.gray
98
- } : i[o] ?? {
99
- label: `L${o}`,
100
- color: r.gray
101
- }, u = s(n.time), d = n.msg ?? "", f = n.namespace || n.ns || n.name || "", p = f ? ` ${r.magenta}(${f})${r.reset}` : "", m = `${r.dim}→${r.reset}`, h = c(n);
102
- return `${r.dim}${u}${r.reset} ${l.color}${l.label}${r.reset}${p} ${m} ${d}${h}`;
118
+ const level = obj.level;
119
+ const config = typeof level === "string" ? LEVEL_BY_NAME[level] ?? {
120
+ label: level.toUpperCase().padEnd(5),
121
+ color: ANSI.gray
122
+ } : LEVEL_BY_NUMBER[level] ?? {
123
+ label: `L${level}`,
124
+ color: ANSI.gray
125
+ };
126
+ const time = formatTime(obj.time);
127
+ const msg = obj.msg ?? "";
128
+ const ns = obj.namespace || obj.ns || obj.name || "";
129
+ const nsTag = ns ? ` ${ANSI.magenta}(${ns})${ANSI.reset}` : "";
130
+ const arrow = `${ANSI.dim}→${ANSI.reset}`;
131
+ const data = formatData(obj);
132
+ return `${ANSI.dim}${time}${ANSI.reset} ${config.color}${config.label}${ANSI.reset}${nsTag} ${arrow} ${msg}${data}`;
103
133
  }
104
- function u() {
105
- return new n({ write(e, t, n) {
106
- let r = e.toString().split("\n");
107
- for (let e of r) {
108
- let t = l(e);
109
- t && process.stdout.write(t + "\n");
134
+ /**
135
+ * Creates a Node.js writable stream that formats Pino JSON output.
136
+ * Used as the destination for PinoTransport when prettyPrint is enabled.
137
+ */
138
+ function createPrettyStream() {
139
+ return new Writable({ write(chunk, _encoding, callback) {
140
+ const lines = chunk.toString().split("\n");
141
+ for (const line of lines) {
142
+ const formatted = formatLogLine(line);
143
+ if (formatted) process.stdout.write(formatted + "\n");
110
144
  }
111
- n();
145
+ callback();
112
146
  } });
113
147
  }
114
148
  //#endregion
115
149
  //#region src/node.ts
116
- function d(n = {}) {
117
- let { prettyPrint: r, pinoOptions: i, ...a } = n;
118
- t.configure({
119
- ...a,
120
- transport: new e({
121
- level: a.level,
122
- redact: a.redact,
123
- destination: a.destination,
124
- destinationStream: r ? u() : void 0,
125
- pinoOptions: i
150
+ /**
151
+ * Node.js-only exports for @eventuras/logger.
152
+ *
153
+ * These utilities depend on `node:stream` and must not be imported in browser
154
+ * or edge runtime environments. Import from the root entry point
155
+ * (`@eventuras/logger`) for the browser-safe API.
156
+ *
157
+ * @example
158
+ * // Pretty dev output
159
+ * import { configureNodeLogger } from '@eventuras/logger/node';
160
+ * configureNodeLogger({ prettyPrint: process.env.NODE_ENV === 'development' });
161
+ *
162
+ * @example
163
+ * // Lower-level: just the pretty helpers
164
+ * import { createPrettyStream, formatLogLine } from '@eventuras/logger/node';
165
+ */
166
+ /**
167
+ * Configure the global Logger with a Node-side PinoTransport, optionally
168
+ * wired to a pretty-print stream for development. Keeps the browser/edge
169
+ * main entry free of `node:stream` imports — call this from a Node-only
170
+ * bootstrap (e.g. `instrumentation.ts`, `server.ts`).
171
+ *
172
+ * @example
173
+ * // In your server entry point
174
+ * import { configureNodeLogger } from '@eventuras/logger/node';
175
+ * configureNodeLogger({
176
+ * level: 'debug',
177
+ * prettyPrint: process.env.NODE_ENV === 'development',
178
+ * });
179
+ */
180
+ function configureNodeLogger(options = {}) {
181
+ const { prettyPrint, pinoOptions, ...rest } = options;
182
+ Logger.configure({
183
+ ...rest,
184
+ transport: new PinoTransport({
185
+ level: rest.level,
186
+ redact: rest.redact,
187
+ destination: rest.destination,
188
+ destinationStream: prettyPrint ? createPrettyStream() : void 0,
189
+ pinoOptions
126
190
  })
127
191
  });
128
192
  }
129
193
  //#endregion
130
- export { d as configureNodeLogger, u as createPrettyStream, l as formatLogLine };
194
+ export { configureNodeLogger, createPrettyStream, formatLogLine };
195
+
196
+ //# sourceMappingURL=node.js.map
@@ -0,0 +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":"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"}
@@ -1,12 +1,22 @@
1
1
  /**
2
2
  * OpenTelemetry integration for @eventuras/logger
3
3
  *
4
- * This module provides integration between Pino and OpenTelemetry Logs API.
5
- * It allows sending logs to any OpenTelemetry-compatible backend (Sentry, Grafana, Jaeger, etc.)
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
- * NOTE: This module requires OpenTelemetry packages to be installed as peer dependencies.
9
- * If they are not available, the integration will gracefully disable itself.
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
- * url: 'https://[org].ingest.sentry.io/api/[project]/integration/otlp/v1/logs',
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
- addLogRecordProcessor(processor: LogRecordProcessor): void;
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
- * If not provided, logs will only be instrumented but not exported.
84
+ * A LoggerProvider is created for it. Requires `@opentelemetry/sdk-logs`.
58
85
  */
59
86
  logRecordProcessor?: LogRecordProcessor;
60
87
  /**
61
- * Logger provider instance. If not provided, a new one will be created.
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 attach to log records.
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 Pino logger.
106
+ * Set up OpenTelemetry integration for the logger.
76
107
  *
77
- * This function:
78
- * 1. Creates or uses provided LoggerProvider
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 environment variables (recommended)
105
- * // Set these in your environment:
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,qBAAqB,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAwBD;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IAExC;;OAEG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC;IAEpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAiEf;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBjE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,kBAAkB,GAAG,IAAI,CAO7D"}
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"}