@logtape/otel 1.3.0-dev.379 → 1.3.0-dev.381
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/deno.json +9 -7
- package/dist/deno.cjs +9 -7
- package/dist/deno.js +9 -7
- package/dist/deno.js.map +1 -1
- package/dist/mod.cjs +146 -26
- package/dist/mod.d.cts +81 -16
- package/dist/mod.d.cts.map +1 -1
- package/dist/mod.d.ts +81 -16
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +147 -27
- package/dist/mod.js.map +1 -1
- package/package.json +10 -8
- package/src/mod.test.ts +625 -2
- package/src/mod.ts +353 -66
package/src/mod.ts
CHANGED
|
@@ -7,11 +7,12 @@ import {
|
|
|
7
7
|
import { diag, type DiagLogger, DiagLogLevel } from "@opentelemetry/api";
|
|
8
8
|
import {
|
|
9
9
|
type AnyValue,
|
|
10
|
+
type Logger as OTLogger,
|
|
10
11
|
type LoggerProvider as LoggerProviderBase,
|
|
11
12
|
type LogRecord as OTLogRecord,
|
|
13
|
+
NOOP_LOGGER,
|
|
12
14
|
SeverityNumber,
|
|
13
15
|
} from "@opentelemetry/api-logs";
|
|
14
|
-
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
15
16
|
import type { OTLPExporterNodeConfigBase } from "@opentelemetry/otlp-exporter-base";
|
|
16
17
|
import {
|
|
17
18
|
defaultResource,
|
|
@@ -19,7 +20,6 @@ import {
|
|
|
19
20
|
} from "@opentelemetry/resources";
|
|
20
21
|
import {
|
|
21
22
|
LoggerProvider,
|
|
22
|
-
type LogRecordProcessor,
|
|
23
23
|
SimpleLogRecordProcessor,
|
|
24
24
|
} from "@opentelemetry/sdk-logs";
|
|
25
25
|
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
@@ -59,16 +59,109 @@ function getEnvironmentVariable(name: string): string | undefined {
|
|
|
59
59
|
return undefined;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Checks if an OTLP endpoint is configured via environment variables or options.
|
|
64
|
+
* Checks the following environment variables:
|
|
65
|
+
* - `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` (logs-specific endpoint)
|
|
66
|
+
* - `OTEL_EXPORTER_OTLP_ENDPOINT` (general OTLP endpoint)
|
|
67
|
+
*
|
|
68
|
+
* @param config Optional exporter configuration that may contain a URL.
|
|
69
|
+
* @returns `true` if an endpoint is configured, `false` otherwise.
|
|
70
|
+
*/
|
|
71
|
+
function hasOtlpEndpoint(config?: OTLPExporterNodeConfigBase): boolean {
|
|
72
|
+
// Check if URL is provided in config
|
|
73
|
+
if (config?.url) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check environment variables
|
|
78
|
+
const logsEndpoint = getEnvironmentVariable(
|
|
79
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
|
|
80
|
+
);
|
|
81
|
+
if (logsEndpoint) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const endpoint = getEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT");
|
|
86
|
+
if (endpoint) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Detects the OTLP protocol from environment variables.
|
|
95
|
+
* Priority:
|
|
96
|
+
* 1. `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL`
|
|
97
|
+
* 2. `OTEL_EXPORTER_OTLP_PROTOCOL`
|
|
98
|
+
* 3. Default: `"http/json"` (for backward compatibility)
|
|
99
|
+
*
|
|
100
|
+
* @returns The detected OTLP protocol.
|
|
101
|
+
*/
|
|
102
|
+
function detectOtlpProtocol(): OtlpProtocol {
|
|
103
|
+
const logsProtocol = getEnvironmentVariable(
|
|
104
|
+
"OTEL_EXPORTER_OTLP_LOGS_PROTOCOL",
|
|
105
|
+
);
|
|
106
|
+
if (
|
|
107
|
+
logsProtocol === "grpc" ||
|
|
108
|
+
logsProtocol === "http/protobuf" ||
|
|
109
|
+
logsProtocol === "http/json"
|
|
110
|
+
) {
|
|
111
|
+
return logsProtocol;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const protocol = getEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL");
|
|
115
|
+
if (
|
|
116
|
+
protocol === "grpc" ||
|
|
117
|
+
protocol === "http/protobuf" ||
|
|
118
|
+
protocol === "http/json"
|
|
119
|
+
) {
|
|
120
|
+
return protocol;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return "http/json";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates an OTLP log exporter based on the detected protocol.
|
|
128
|
+
* Uses dynamic imports to maintain browser compatibility when gRPC is not used.
|
|
129
|
+
* @param config Optional exporter configuration.
|
|
130
|
+
* @returns A promise that resolves to the appropriate OTLP log exporter.
|
|
131
|
+
*/
|
|
132
|
+
async function createOtlpExporter(
|
|
133
|
+
config?: OTLPExporterNodeConfigBase,
|
|
134
|
+
// deno-lint-ignore no-explicit-any
|
|
135
|
+
): Promise<any> {
|
|
136
|
+
const protocol = detectOtlpProtocol();
|
|
137
|
+
|
|
138
|
+
switch (protocol) {
|
|
139
|
+
case "grpc": {
|
|
140
|
+
const { OTLPLogExporter } = await import(
|
|
141
|
+
"@opentelemetry/exporter-logs-otlp-grpc"
|
|
142
|
+
);
|
|
143
|
+
return new OTLPLogExporter(config);
|
|
144
|
+
}
|
|
145
|
+
case "http/protobuf": {
|
|
146
|
+
const { OTLPLogExporter } = await import(
|
|
147
|
+
"@opentelemetry/exporter-logs-otlp-proto"
|
|
148
|
+
);
|
|
149
|
+
return new OTLPLogExporter(config);
|
|
150
|
+
}
|
|
151
|
+
case "http/json":
|
|
152
|
+
default: {
|
|
153
|
+
const { OTLPLogExporter } = await import(
|
|
154
|
+
"@opentelemetry/exporter-logs-otlp-http"
|
|
155
|
+
);
|
|
156
|
+
return new OTLPLogExporter(config);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
62
161
|
/**
|
|
63
162
|
* The OpenTelemetry logger provider.
|
|
64
163
|
*/
|
|
65
164
|
type ILoggerProvider = LoggerProviderBase & {
|
|
66
|
-
/**
|
|
67
|
-
* Adds a new {@link LogRecordProcessor} to this logger.
|
|
68
|
-
* @param processor the new LogRecordProcessor to be added.
|
|
69
|
-
*/
|
|
70
|
-
addLogRecordProcessor(processor: LogRecordProcessor): void;
|
|
71
|
-
|
|
72
165
|
/**
|
|
73
166
|
* Flush all buffered data and shut down the LoggerProvider and all registered
|
|
74
167
|
* LogRecordProcessor.
|
|
@@ -95,14 +188,15 @@ type Message = (string | null | undefined)[];
|
|
|
95
188
|
export type BodyFormatter = (message: Message) => AnyValue;
|
|
96
189
|
|
|
97
190
|
/**
|
|
98
|
-
*
|
|
191
|
+
* The OTLP protocol to use for exporting logs.
|
|
192
|
+
* @since 0.9.0
|
|
99
193
|
*/
|
|
100
|
-
export
|
|
101
|
-
/**
|
|
102
|
-
* The OpenTelemetry logger provider to use.
|
|
103
|
-
*/
|
|
104
|
-
loggerProvider?: ILoggerProvider;
|
|
194
|
+
export type OtlpProtocol = "grpc" | "http/protobuf" | "http/json";
|
|
105
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Base options shared by all OpenTelemetry sink configurations.
|
|
198
|
+
*/
|
|
199
|
+
interface OpenTelemetrySinkOptionsBase {
|
|
106
200
|
/**
|
|
107
201
|
* The way to render the message in the log record. If `"string"`,
|
|
108
202
|
* the message is rendered as a single string with the values are
|
|
@@ -128,85 +222,278 @@ export interface OpenTelemetrySinkOptions {
|
|
|
128
222
|
* Turned off by default.
|
|
129
223
|
*/
|
|
130
224
|
diagnostics?: boolean;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Options for creating an OpenTelemetry sink with a custom logger provider.
|
|
229
|
+
* When using this configuration, you are responsible for setting up the
|
|
230
|
+
* logger provider with appropriate exporters and processors.
|
|
231
|
+
*
|
|
232
|
+
* This is the recommended approach for production use as it gives you
|
|
233
|
+
* full control over the OpenTelemetry configuration.
|
|
234
|
+
* @since 0.9.0
|
|
235
|
+
*/
|
|
236
|
+
export interface OpenTelemetrySinkProviderOptions
|
|
237
|
+
extends OpenTelemetrySinkOptionsBase {
|
|
238
|
+
/**
|
|
239
|
+
* The OpenTelemetry logger provider to use.
|
|
240
|
+
*/
|
|
241
|
+
loggerProvider: ILoggerProvider;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Options for creating an OpenTelemetry sink with automatic exporter creation.
|
|
246
|
+
* The protocol is determined by environment variables:
|
|
247
|
+
* - `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` (highest priority)
|
|
248
|
+
* - `OTEL_EXPORTER_OTLP_PROTOCOL` (fallback)
|
|
249
|
+
* - Default: `"http/json"`
|
|
250
|
+
*
|
|
251
|
+
* For production use, consider providing your own {@link ILoggerProvider}
|
|
252
|
+
* via {@link OpenTelemetrySinkProviderOptions} for more control.
|
|
253
|
+
* @since 0.9.0
|
|
254
|
+
*/
|
|
255
|
+
export interface OpenTelemetrySinkExporterOptions
|
|
256
|
+
extends OpenTelemetrySinkOptionsBase {
|
|
257
|
+
/**
|
|
258
|
+
* The OpenTelemetry logger provider to use.
|
|
259
|
+
* Must be undefined or omitted when using exporter options.
|
|
260
|
+
*/
|
|
261
|
+
loggerProvider?: undefined;
|
|
131
262
|
|
|
132
263
|
/**
|
|
133
264
|
* The OpenTelemetry OTLP exporter configuration to use.
|
|
134
|
-
* Ignored if `loggerProvider` is provided.
|
|
135
265
|
*/
|
|
136
266
|
otlpExporterConfig?: OTLPExporterNodeConfigBase;
|
|
137
267
|
|
|
138
268
|
/**
|
|
139
269
|
* The service name to use. If not provided, the service name is
|
|
140
270
|
* taken from the `OTEL_SERVICE_NAME` environment variable.
|
|
141
|
-
* Ignored if `loggerProvider` is provided.
|
|
142
271
|
*/
|
|
143
272
|
serviceName?: string;
|
|
144
273
|
}
|
|
145
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Options for creating an OpenTelemetry sink.
|
|
277
|
+
*
|
|
278
|
+
* This is a union type that accepts either:
|
|
279
|
+
* - {@link OpenTelemetrySinkProviderOptions}: Provide your own `loggerProvider`
|
|
280
|
+
* (recommended for production)
|
|
281
|
+
* - {@link OpenTelemetrySinkExporterOptions}: Let the sink create an exporter
|
|
282
|
+
* automatically based on environment variables
|
|
283
|
+
*
|
|
284
|
+
* When no `loggerProvider` is provided, the protocol is determined by:
|
|
285
|
+
* 1. `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` environment variable
|
|
286
|
+
* 2. `OTEL_EXPORTER_OTLP_PROTOCOL` environment variable
|
|
287
|
+
* 3. Default: `"http/json"`
|
|
288
|
+
*
|
|
289
|
+
* @example Using a custom logger provider (recommended)
|
|
290
|
+
* ```typescript
|
|
291
|
+
* import { LoggerProvider, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
292
|
+
* import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
|
|
293
|
+
*
|
|
294
|
+
* const provider = new LoggerProvider();
|
|
295
|
+
* provider.addLogRecordProcessor(new SimpleLogRecordProcessor(new OTLPLogExporter()));
|
|
296
|
+
*
|
|
297
|
+
* const sink = getOpenTelemetrySink({ loggerProvider: provider });
|
|
298
|
+
* ```
|
|
299
|
+
*
|
|
300
|
+
* @example Using automatic exporter creation
|
|
301
|
+
* ```typescript
|
|
302
|
+
* // Protocol determined by OTEL_EXPORTER_OTLP_PROTOCOL env var
|
|
303
|
+
* const sink = getOpenTelemetrySink({
|
|
304
|
+
* serviceName: "my-service",
|
|
305
|
+
* });
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
export type OpenTelemetrySinkOptions =
|
|
309
|
+
| OpenTelemetrySinkProviderOptions
|
|
310
|
+
| OpenTelemetrySinkExporterOptions;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A no-op logger provider that returns NOOP_LOGGER for all requests.
|
|
314
|
+
* Used when no OTLP endpoint is configured to avoid repeated connection errors.
|
|
315
|
+
*/
|
|
316
|
+
const noopLoggerProvider: ILoggerProvider = {
|
|
317
|
+
getLogger: () => NOOP_LOGGER,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Initializes the logger provider asynchronously.
|
|
322
|
+
* This is used when the user doesn't provide a custom logger provider.
|
|
323
|
+
*
|
|
324
|
+
* If no OTLP endpoint is configured (via options or environment variables),
|
|
325
|
+
* returns a noop logger provider to avoid repeated connection errors.
|
|
326
|
+
*
|
|
327
|
+
* @param options The exporter options.
|
|
328
|
+
* @returns A promise that resolves to the initialized logger provider.
|
|
329
|
+
*/
|
|
330
|
+
async function initializeLoggerProvider(
|
|
331
|
+
options: OpenTelemetrySinkExporterOptions,
|
|
332
|
+
): Promise<ILoggerProvider> {
|
|
333
|
+
// If no endpoint is configured, use noop logger provider to avoid
|
|
334
|
+
// repeated transport errors
|
|
335
|
+
if (!hasOtlpEndpoint(options.otlpExporterConfig)) {
|
|
336
|
+
return noopLoggerProvider;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const resource = defaultResource().merge(
|
|
340
|
+
resourceFromAttributes({
|
|
341
|
+
[ATTR_SERVICE_NAME]: options.serviceName ??
|
|
342
|
+
getEnvironmentVariable("OTEL_SERVICE_NAME"),
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
const otlpExporter = await createOtlpExporter(options.otlpExporterConfig);
|
|
346
|
+
const loggerProvider = new LoggerProvider({
|
|
347
|
+
resource,
|
|
348
|
+
processors: [
|
|
349
|
+
// @ts-ignore: it works anyway...
|
|
350
|
+
new SimpleLogRecordProcessor(otlpExporter),
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
return loggerProvider;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Emits a log record to the OpenTelemetry logger.
|
|
358
|
+
* @param logger The OpenTelemetry logger.
|
|
359
|
+
* @param record The LogTape log record.
|
|
360
|
+
* @param options The sink options.
|
|
361
|
+
*/
|
|
362
|
+
function emitLogRecord(
|
|
363
|
+
logger: OTLogger,
|
|
364
|
+
record: LogRecord,
|
|
365
|
+
options: OpenTelemetrySinkOptions,
|
|
366
|
+
): void {
|
|
367
|
+
const objectRenderer = options.objectRenderer ?? "inspect";
|
|
368
|
+
const { category, level, message, timestamp, properties } = record;
|
|
369
|
+
const severityNumber = mapLevelToSeverityNumber(level);
|
|
370
|
+
const attributes = convertToAttributes(properties, objectRenderer);
|
|
371
|
+
attributes["category"] = [...category];
|
|
372
|
+
logger.emit(
|
|
373
|
+
{
|
|
374
|
+
severityNumber,
|
|
375
|
+
severityText: level,
|
|
376
|
+
body: typeof options.messageType === "function"
|
|
377
|
+
? convertMessageToCustomBodyFormat(
|
|
378
|
+
message,
|
|
379
|
+
objectRenderer,
|
|
380
|
+
options.messageType,
|
|
381
|
+
)
|
|
382
|
+
: options.messageType === "array"
|
|
383
|
+
? convertMessageToArray(message, objectRenderer)
|
|
384
|
+
: convertMessageToString(message, objectRenderer),
|
|
385
|
+
attributes,
|
|
386
|
+
timestamp: new Date(timestamp),
|
|
387
|
+
} satisfies OTLogRecord,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
146
391
|
/**
|
|
147
392
|
* Creates a sink that forwards log records to OpenTelemetry.
|
|
393
|
+
*
|
|
394
|
+
* When a custom `loggerProvider` is provided, it is used directly.
|
|
395
|
+
* Otherwise, the sink will lazily initialize a logger provider on the first
|
|
396
|
+
* log record, using the protocol determined by environment variables.
|
|
397
|
+
*
|
|
148
398
|
* @param options Options for creating the sink.
|
|
149
399
|
* @returns The sink.
|
|
150
400
|
*/
|
|
151
401
|
export function getOpenTelemetrySink(
|
|
152
402
|
options: OpenTelemetrySinkOptions = {},
|
|
153
|
-
): Sink {
|
|
403
|
+
): Sink & AsyncDisposable {
|
|
154
404
|
if (options.diagnostics) {
|
|
155
405
|
diag.setLogger(new DiagLoggerAdaptor(), DiagLogLevel.DEBUG);
|
|
156
406
|
}
|
|
157
407
|
|
|
158
|
-
|
|
159
|
-
if (options.loggerProvider
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
const objectRenderer = options.objectRenderer ?? "inspect";
|
|
176
|
-
const logger = loggerProvider.getLogger(metadata.name, metadata.version);
|
|
177
|
-
const sink = (record: LogRecord) => {
|
|
178
|
-
const { category, level, message, timestamp, properties } = record;
|
|
179
|
-
if (
|
|
180
|
-
category[0] === "logtape" && category[1] === "meta" &&
|
|
181
|
-
category[2] === "otel"
|
|
182
|
-
) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const severityNumber = mapLevelToSeverityNumber(level);
|
|
186
|
-
const attributes = convertToAttributes(properties, objectRenderer);
|
|
187
|
-
attributes["category"] = [...category];
|
|
188
|
-
logger.emit(
|
|
408
|
+
// If loggerProvider is provided, use the synchronous path
|
|
409
|
+
if (options.loggerProvider != null) {
|
|
410
|
+
const loggerProvider = options.loggerProvider;
|
|
411
|
+
const logger = loggerProvider.getLogger(metadata.name, metadata.version);
|
|
412
|
+
const shutdown = loggerProvider.shutdown?.bind(loggerProvider);
|
|
413
|
+
const sink: Sink & AsyncDisposable = Object.assign(
|
|
414
|
+
(record: LogRecord) => {
|
|
415
|
+
const { category } = record;
|
|
416
|
+
if (
|
|
417
|
+
category[0] === "logtape" && category[1] === "meta" &&
|
|
418
|
+
category[2] === "otel"
|
|
419
|
+
) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
emitLogRecord(logger, record, options);
|
|
423
|
+
},
|
|
189
424
|
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
message,
|
|
195
|
-
objectRenderer,
|
|
196
|
-
options.messageType,
|
|
197
|
-
)
|
|
198
|
-
: options.messageType === "array"
|
|
199
|
-
? convertMessageToArray(message, objectRenderer)
|
|
200
|
-
: convertMessageToString(message, objectRenderer),
|
|
201
|
-
attributes,
|
|
202
|
-
timestamp: new Date(timestamp),
|
|
203
|
-
} satisfies OTLogRecord,
|
|
425
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
426
|
+
if (shutdown != null) await shutdown();
|
|
427
|
+
},
|
|
428
|
+
},
|
|
204
429
|
);
|
|
205
|
-
|
|
206
|
-
if (loggerProvider.shutdown != null) {
|
|
207
|
-
const shutdown = loggerProvider.shutdown.bind(loggerProvider);
|
|
208
|
-
sink[Symbol.asyncDispose] = shutdown;
|
|
430
|
+
return sink;
|
|
209
431
|
}
|
|
432
|
+
|
|
433
|
+
// Lazy initialization for automatic exporter creation
|
|
434
|
+
let loggerProvider: ILoggerProvider | null = null;
|
|
435
|
+
let logger: OTLogger | null = null;
|
|
436
|
+
let initPromise: Promise<void> | null = null;
|
|
437
|
+
let initError: Error | null = null;
|
|
438
|
+
|
|
439
|
+
const sink: Sink & AsyncDisposable = Object.assign(
|
|
440
|
+
(record: LogRecord) => {
|
|
441
|
+
const { category } = record;
|
|
442
|
+
if (
|
|
443
|
+
category[0] === "logtape" && category[1] === "meta" &&
|
|
444
|
+
category[2] === "otel"
|
|
445
|
+
) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// If already initialized, emit the log
|
|
450
|
+
if (logger != null) {
|
|
451
|
+
emitLogRecord(logger, record, options);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// If initialization failed, skip silently
|
|
456
|
+
if (initError != null) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Start initialization if not already started
|
|
461
|
+
if (initPromise == null) {
|
|
462
|
+
initPromise = initializeLoggerProvider(options)
|
|
463
|
+
.then((provider) => {
|
|
464
|
+
loggerProvider = provider;
|
|
465
|
+
logger = provider.getLogger(metadata.name, metadata.version);
|
|
466
|
+
// Emit the current record that triggered initialization
|
|
467
|
+
emitLogRecord(logger, record, options);
|
|
468
|
+
})
|
|
469
|
+
.catch((error) => {
|
|
470
|
+
initError = error;
|
|
471
|
+
// Log initialization error to console as a fallback
|
|
472
|
+
// deno-lint-ignore no-console
|
|
473
|
+
console.error("Failed to initialize OpenTelemetry logger:", error);
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
// Records during initialization are dropped
|
|
477
|
+
// (the triggering record is emitted in the then() callback above)
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
481
|
+
// Wait for initialization to complete if in progress
|
|
482
|
+
if (initPromise != null) {
|
|
483
|
+
try {
|
|
484
|
+
await initPromise;
|
|
485
|
+
} catch {
|
|
486
|
+
// Initialization failed, nothing to shut down
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (loggerProvider?.shutdown != null) {
|
|
491
|
+
await loggerProvider.shutdown();
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
);
|
|
496
|
+
|
|
210
497
|
return sink;
|
|
211
498
|
}
|
|
212
499
|
|