11import { DiagConsoleLogger , DiagLogLevel , TracerProvider , diag } from "@opentelemetry/api" ;
2+ import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base" ;
23import { logs } from "@opentelemetry/api-logs" ;
34import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http" ;
45import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http" ;
@@ -16,6 +17,7 @@ import {
1617 BatchLogRecordProcessor ,
1718 LoggerProvider ,
1819 LogRecordExporter ,
20+ ReadableLogRecord ,
1921 SimpleLogRecordProcessor ,
2022} from "@opentelemetry/sdk-logs" ;
2123import {
@@ -92,6 +94,8 @@ export type TracingSDKConfig = {
9294 diagLogLevel ?: TracingDiagnosticLogLevel ;
9395} ;
9496
97+ const idGenerator = new RandomIdGenerator ( ) ;
98+
9599export class TracingSDK {
96100 public readonly asyncResourceDetector = new AsyncResourceDetector ( ) ;
97101 private readonly _logProvider : LoggerProvider ;
@@ -160,7 +164,7 @@ export class TracingSDK {
160164 )
161165 ) ;
162166
163- const externalTraceId = crypto . randomUUID ( ) ;
167+ const externalTraceId = idGenerator . generateTraceId ( ) ;
164168
165169 for ( const exporter of config . exporters ?? [ ] ) {
166170 traceProvider . addSpanProcessor (
@@ -215,13 +219,22 @@ export class TracingSDK {
215219 for ( const externalLogExporter of config . logExporters ?? [ ] ) {
216220 loggerProvider . addLogRecordProcessor (
217221 getEnvVar ( "OTEL_BATCH_PROCESSING_ENABLED" ) === "1"
218- ? new BatchLogRecordProcessor ( externalLogExporter , {
219- maxExportBatchSize : parseInt ( getEnvVar ( "OTEL_LOG_MAX_EXPORT_BATCH_SIZE" ) ?? "64" ) ,
220- scheduledDelayMillis : parseInt ( getEnvVar ( "OTEL_LOG_SCHEDULED_DELAY_MILLIS" ) ?? "200" ) ,
221- exportTimeoutMillis : parseInt ( getEnvVar ( "OTEL_LOG_EXPORT_TIMEOUT_MILLIS" ) ?? "30000" ) ,
222- maxQueueSize : parseInt ( getEnvVar ( "OTEL_LOG_MAX_QUEUE_SIZE" ) ?? "512" ) ,
223- } )
224- : new SimpleLogRecordProcessor ( externalLogExporter )
222+ ? new BatchLogRecordProcessor (
223+ new ExternalLogRecordExporterWrapper ( externalLogExporter , externalTraceId ) ,
224+ {
225+ maxExportBatchSize : parseInt ( getEnvVar ( "OTEL_LOG_MAX_EXPORT_BATCH_SIZE" ) ?? "64" ) ,
226+ scheduledDelayMillis : parseInt (
227+ getEnvVar ( "OTEL_LOG_SCHEDULED_DELAY_MILLIS" ) ?? "200"
228+ ) ,
229+ exportTimeoutMillis : parseInt (
230+ getEnvVar ( "OTEL_LOG_EXPORT_TIMEOUT_MILLIS" ) ?? "30000"
231+ ) ,
232+ maxQueueSize : parseInt ( getEnvVar ( "OTEL_LOG_MAX_QUEUE_SIZE" ) ?? "512" ) ,
233+ }
234+ )
235+ : new SimpleLogRecordProcessor (
236+ new ExternalLogRecordExporterWrapper ( externalLogExporter , externalTraceId )
237+ )
225238 ) ;
226239 }
227240
@@ -321,3 +334,50 @@ class ExternalSpanExporterWrapper {
321334 : Promise . resolve ( ) ;
322335 }
323336}
337+
338+ class ExternalLogRecordExporterWrapper {
339+ constructor (
340+ private underlyingExporter : LogRecordExporter ,
341+ private externalTraceId : string
342+ ) { }
343+
344+ export ( logs : any [ ] , resultCallback : ( result : any ) => void ) : void {
345+ const modifiedLogs = logs . map ( this . transformLogRecord . bind ( this ) ) ;
346+
347+ this . underlyingExporter . export ( modifiedLogs , resultCallback ) ;
348+ }
349+
350+ shutdown ( ) : Promise < void > {
351+ return this . underlyingExporter . shutdown ( ) ;
352+ }
353+
354+ transformLogRecord ( logRecord : ReadableLogRecord ) : ReadableLogRecord {
355+ // If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
356+ if ( ! logRecord . spanContext || ! this . externalTraceId ) {
357+ return logRecord ;
358+ }
359+
360+ // Capture externalTraceId for use within the proxy's scope.
361+ const { externalTraceId } = this ;
362+
363+ return new Proxy ( logRecord , {
364+ get ( target , prop , receiver ) {
365+ if ( prop === "spanContext" ) {
366+ // Intercept access to spanContext.
367+ const originalSpanContext = target . spanContext ;
368+ // Ensure originalSpanContext exists (it should, due to the check above, but good for safety).
369+ if ( originalSpanContext ) {
370+ return {
371+ ...originalSpanContext ,
372+ traceId : externalTraceId , // Override traceId.
373+ } ;
374+ }
375+ // Fallback if, for some reason, originalSpanContext is undefined here.
376+ return undefined ;
377+ }
378+ // For all other properties, defer to the original object.
379+ return Reflect . get ( target , prop , receiver ) ;
380+ } ,
381+ } ) ;
382+ }
383+ }
0 commit comments