Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
88 changes: 88 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/otel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"dependencies": {
"@opentelemetry/api": "1.9.0",
"@opentelemetry/core": "1.30.1",
"@opentelemetry/instrumentation": "^0.203.0",
"@opentelemetry/otlp-transformer": "0.57.2",
"@opentelemetry/resources": "1.30.1",
"@opentelemetry/sdk-trace-node": "1.30.1"
Expand Down
29 changes: 25 additions & 4 deletions packages/otel/src/bootstrap/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { type SpanProcessor } from '@opentelemetry/sdk-trace-node'
import type { Instrumentation } from '@opentelemetry/instrumentation'
import { GET_TRACER, SHUTDOWN_TRACERS } from '../constants.js'

export const createTracerProvider = async (options: {
export interface TracerProviderOptions {
/**
* The request headers (checked for presence of the `x-nf-enable-tracing` header)
*/
headers: Headers
serviceName: string
serviceVersion: string
deploymentEnvironment: string
siteUrl: string
siteId: string
siteName: string
}) => {
instrumentations?: (Instrumentation | Promise<Instrumentation>)[]
extraSpanProcessors?: (SpanProcessor | Promise<SpanProcessor>)[]
}

export const createTracerProvider = async (options: TracerProviderOptions) => {
if (!options.headers.has('x-nf-enable-tracing')) {
return
}
Expand All @@ -17,7 +26,9 @@ export const createTracerProvider = async (options: {
const runtimeVersion = nodeVersion.slice(1)

const { Resource } = await import('@opentelemetry/resources')
const { NodeTracerProvider, BatchSpanProcessor } = await import('@opentelemetry/sdk-trace-node')
const { NodeTracerProvider, SimpleSpanProcessor } = await import('@opentelemetry/sdk-trace-node')

const { registerInstrumentations } = await import('@opentelemetry/instrumentation')

const { NetlifySpanExporter } = await import('./netlify_span_exporter.js')

Expand All @@ -34,11 +45,21 @@ export const createTracerProvider = async (options: {

const nodeTracerProvider = new NodeTracerProvider({
resource,
spanProcessors: [new BatchSpanProcessor(new NetlifySpanExporter())],
spanProcessors: [
new SimpleSpanProcessor(new NetlifySpanExporter()),
...(await Promise.all(options.extraSpanProcessors ?? [])),
],
})

nodeTracerProvider.register()

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 Down
38 changes: 37 additions & 1 deletion packages/otel/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type SugaredTracer } from '@opentelemetry/api/experimental'
import { type SugaredSpanOptions, type SugaredTracer } from '@opentelemetry/api/experimental'
import { GET_TRACER, SHUTDOWN_TRACERS } from './constants.js'
import type { Context, Span } from '@opentelemetry/api'

type GlobalThisExtended = typeof globalThis & {
[GET_TRACER]?: (name?: string, version?: string) => SugaredTracer | undefined
Expand All @@ -13,3 +14,38 @@ export const getTracer = (name?: string, version?: string): SugaredTracer | unde
export const shutdownTracers = async (): Promise<void> => {
return (globalThis as GlobalThisExtended)[SHUTDOWN_TRACERS]?.()
}

export function withActiveSpan<F extends (span?: Span) => ReturnType<F>>(
tracer: SugaredTracer | undefined,
name: string,
fn: F,
): ReturnType<F>
export function withActiveSpan<F extends (span?: Span) => ReturnType<F>>(
tracer: SugaredTracer | undefined,
name: string,
options: SugaredSpanOptions,
fn: F,
): ReturnType<F>
export function withActiveSpan<F extends (span?: Span) => ReturnType<F>>(
tracer: SugaredTracer | undefined,
name: string,
options: SugaredSpanOptions,
context: Context,
fn: F,
): ReturnType<F>
export function withActiveSpan<F extends (span?: Span) => ReturnType<F>>(
tracer: SugaredTracer | undefined,
name: string,
optionsOrFn: SugaredSpanOptions | F,
contextOrFn?: Context | F,
fn?: F,
): ReturnType<F> {
const func = typeof contextOrFn === 'function' ? contextOrFn : typeof optionsOrFn === 'function' ? optionsOrFn : fn
if (!func) {
throw new Error('function to execute with active span is missing')
}
if (!tracer) {
return func()
}
return tracer.withActiveSpan(name, optionsOrFn as SugaredSpanOptions, contextOrFn as Context, func)
}
Loading