Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions packages/otel/src/bootstrap/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type SpanProcessor } from '@opentelemetry/sdk-trace-node'
import { BatchSpanProcessor, ConsoleSpanExporter, type SpanProcessor } from '@opentelemetry/sdk-trace-node'

Check failure on line 1 in packages/otel/src/bootstrap/main.ts

View workflow job for this annotation

GitHub Actions / Lint

'ConsoleSpanExporter' is defined but never used. Allowed unused vars must match /^_/u

Check failure on line 1 in packages/otel/src/bootstrap/main.ts

View workflow job for this annotation

GitHub Actions / Lint

'BatchSpanProcessor' is defined but never used. Allowed unused vars must match /^_/u
import type { Instrumentation } from '@opentelemetry/instrumentation'
import { GET_TRACER, SHUTDOWN_TRACERS } from '../constants.js'
import { GET_TRACE_CONTEXT_FORWARDER, GET_TRACER, SHUTDOWN_TRACERS } from '../constants.js'
import { Context, context, W3CTraceContextPropagator } from '../opentelemetry.ts'

export interface TracerProviderOptions {
serviceName: string
Expand All @@ -11,6 +12,7 @@
siteName: string
instrumentations?: (Instrumentation | Promise<Instrumentation>)[]
spanProcessors?: (SpanProcessor | Promise<SpanProcessor>)[]
propagationHeaders?: Headers
}

export const createTracerProvider = async (options: TracerProviderOptions) => {
Expand Down Expand Up @@ -43,16 +45,38 @@
})

nodeTracerProvider.register({
propagator: new W3CTraceContextPropagator(),
propagator: new W3CTraceContextPropagator()

})

let traceContextForwarder: (propagator: W3CTraceContextPropagator, requestHeaders: Headers) => void

if (options.propagationHeaders) {
traceContextForwarder = (propagator: W3CTraceContextPropagator, requestHeaders: Headers): Context => {
const getter = {
keys: (carrier: Headers) => [...carrier.keys()],
get: (carrier: Headers, key: string) => carrier.get(key) || undefined,

Check failure on line 58 in packages/otel/src/bootstrap/main.ts

View workflow job for this annotation

GitHub Actions / Lint

Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator
}
const extractedContext = propagator.extract(context.active(), options.propagationHeaders, getter)

propagator?.inject(context.active(), requestHeaders, {

Check failure on line 62 in packages/otel/src/bootstrap/main.ts

View workflow job for this annotation

GitHub Actions / Lint

Unnecessary optional chain on a non-nullish value
set: (carrier, key, value) => {
carrier.set(key, value)

Check failure on line 64 in packages/otel/src/bootstrap/main.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe call of a(n) `any` typed value
},
})

return extractedContext
}
}

const instrumentations = await Promise.all(options.instrumentations ?? [])

registerInstrumentations({
instrumentations,
tracerProvider: nodeTracerProvider,
})


const { trace } = await import('@opentelemetry/api')
const { SugaredTracer } = await import('@opentelemetry/api/experimental')
const { default: pkg } = await import('../../package.json', { with: { type: 'json' } })
Expand All @@ -70,6 +94,15 @@
},
})

Object.defineProperty(globalThis, GET_TRACE_CONTEXT_FORWARDER, {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not totally convinced this is the best way to make this forwarder accessible to the fetch instrumentation so may rework this

enumerable: false,
configurable: true,
writable: false,
value: function() {
return traceContextForwarder
},
})

Object.defineProperty(globalThis, SHUTDOWN_TRACERS, {
enumerable: false,
configurable: true,
Expand Down
1 change: 1 addition & 0 deletions packages/otel/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const GET_TRACER = '__netlify__getTracer'
export const SHUTDOWN_TRACERS = '__netlify__shutdownTracers'
export const GET_TRACE_CONTEXT_FORWARDER = '__netlify__getTraceContextForwarder'
export const TRACE_PREFIX = '__nfOTLPTrace'
24 changes: 21 additions & 3 deletions packages/otel/src/instrumentations/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as api from '@opentelemetry/api'
import { SugaredTracer } from '@opentelemetry/api/experimental'
import { _globalThis } from '@opentelemetry/core'
import { _globalThis, W3CTraceContextPropagator } from '@opentelemetry/core'
import { InstrumentationConfig, type Instrumentation } from '@opentelemetry/instrumentation'
import { getTraceContextForwarder } from '../main.ts'

export interface FetchInstrumentationConfig extends InstrumentationConfig {
getRequestAttributes?(headers: Request): api.Attributes
Expand All @@ -26,9 +27,9 @@ export class FetchInstrumentation implements Instrumentation {
return this.config
}

setConfig(): void {}
setConfig(): void { }

setMeterProvider(): void {}
setMeterProvider(): void { }
setTracerProvider(provider: api.TracerProvider): void {
this.provider = provider
}
Expand Down Expand Up @@ -120,6 +121,23 @@ export class FetchInstrumentation implements Instrumentation {
return await originalFetch(resource, options)
}

const traceContextForwarder = getTraceContextForwarder()
if (options?.headers && traceContextForwarder) {
const headers = new Headers(options.headers)
const extractedContext = traceContextForwarder(new W3CTraceContextPropagator(), headers)

// Replace headers in options with the mutated version
const nextOptions: RequestInit = { ...options, headers }

return tracer.startActiveSpan('fetch', {}, extractedContext, async (span) => {
const request = new Request(resource, nextOptions)
this.annotateFromRequest(span, request)
const response = await originalFetch(request, nextOptions)
this.annotateFromResponse(span, response)
return response
})
}

return tracer.withActiveSpan('fetch', async (span) => {
const request = new Request(resource, options)
this.annotateFromRequest(span, request)
Expand Down
8 changes: 7 additions & 1 deletion packages/otel/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { type SugaredSpanOptions, type SugaredTracer } from '@opentelemetry/api/experimental'
import { GET_TRACER, SHUTDOWN_TRACERS } from './constants.js'
import { GET_TRACER, SHUTDOWN_TRACERS, GET_TRACE_CONTEXT_FORWARDER } from './constants.js'
import type { Context, Span } from '@opentelemetry/api'
import { W3CTraceContextPropagator } from '@opentelemetry/core'

type GlobalThisExtended = typeof globalThis & {
[GET_TRACER]?: (name?: string, version?: string) => SugaredTracer | undefined
[SHUTDOWN_TRACERS]?: () => void
[GET_TRACE_CONTEXT_FORWARDER]?: () => ((propagator: W3CTraceContextPropagator, requestHeaders: Headers) => Context) | undefined
}

export const getTracer = (name?: string, version?: string): SugaredTracer | undefined => {
return (globalThis as GlobalThisExtended)[GET_TRACER]?.(name, version)
}

export const getTraceContextForwarder = (): ((propagator: W3CTraceContextPropagator, requestHeaders: Headers) => Context) | undefined => {
return (globalThis as GlobalThisExtended)[GET_TRACE_CONTEXT_FORWARDER]?.()
}

export const shutdownTracers = async (): Promise<void> => {
return (globalThis as GlobalThisExtended)[SHUTDOWN_TRACERS]?.()
}
Expand Down
Loading