evanderkoogh / otel-cf-workers

An OpenTelemetry compatible library for instrumenting and exporting traces for Cloudflare Workers
BSD 3-Clause "New" or "Revised" License
210 stars 40 forks source link

Support multiple span processors #43

Closed plantfansam closed 10 months ago

plantfansam commented 11 months ago

This PR attempts to close https://github.com/evanderkoogh/otel-cf-workers/issues/42. Not sure if you want this upstream @evanderkoogh, but I wanted to prove to myself it could be done, so here it is! Some demo code:

import { instrument, ResolveConfigFn } from '@microlabs/otel-cf-workers'
import { trace, Span, Context } from '@opentelemetry/api'
import { ConsoleSpanExporter, ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base'

class LoggingSpanProcessor implements SpanProcessor {
    onStart(span: Span, _parentContext: Context): void {
        console.log(`startspan:${span.spanContext().spanId}`)
    }

    onEnd(span: ReadableSpan): void {
        console.log(`endspan:${span.spanContext().spanId}`)
    }

    forceFlush(): Promise<void> {
        return Promise.resolve()
    }

    shutdown(): Promise<void> {
        return Promise.resolve()
    }
}

const ResolveTraceConfigFn: ResolveConfigFn = () => {
    return {
        exporter: new ConsoleSpanExporter,
        service: { name: 'greetings' },
        spanProcessors: [new LoggingSpanProcessor]
    }
}

const handler = {
    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        await fetch('https://cloudflare.com')
        const greeting = "G'day World"
        trace.getActiveSpan()?.setAttribute('greeting', greeting)
        ctx.waitUntil(fetch('https://workers.dev'))
        return new Response(`${greeting}!`)
    },
}

export default instrument(handler, ResolveTraceConfigFn)
evanderkoogh commented 10 months ago

As we talked about on the call, I have certainly no objections to being able to use different SpanProcessors, but do you think there is a reason to have multiple SpanProcessors? Would you not either use the default one, or a different one?

plantfansam commented 10 months ago

That's such a good question, and it sent me back down the spec rabbit hole. Re-reading the spec, I think that the key line is:

Each processor registered on TracerProvider is a start of pipeline that consist of span processor and optional exporter. SDK MUST allow to end each pipeline with individual exporter.

I think this means that a single OpenTelemetry SDK can e.g. export to different endpoints by registering multiple processor -> exporter couplets.

evanderkoogh commented 10 months ago

I fail to see the reason why you would want to have a different SpanProcessor for every Exporter?

plantfansam commented 10 months ago

Yeah, to be honest, I have a hard time understanding the spec's perspective on this, and there seems to have been a lot of discussion on the topic (e.g. https://github.com/open-telemetry/opentelemetry-specification/pull/338/files) . I (very weakly) believe that multiple processors for each exporter would provide a more on-spec way of achieving this. See, e.g., having a zPages processor and a Batch Span processor that gets to an exporter in this example.

My specific use case for this library doesn't involve two export paths, just monkeying with the spans a bit when they start and end.