OpenTelemetry

    OpenTelemetry LLM tracing: bring your own SDK

    init() never displaces a TracerProvider you configured: your exporters keep every span, and Syntropylabs additionally receives them.

    Set up Any OpenTelemetry instrumentor

    The snippet is copied from the SDK README, not paraphrased. Keys shown as tk_live_... are the environment key from Settings › Environments.

    TypeScript
    import { NodeSDK } from "@opentelemetry/sdk-node";
    
    const sdk = new NodeSDK({ /* your exporters, sampler, resource */ });
    sdk.start();
    
    evalkit.init({ subscriptionKey: process.env.EVALKIT_SUBSCRIPTION_KEY!, serviceName: "my-service" });

    Copied from sdk-ts/README.md § Coexisting with an OpenTelemetry setup you already have

    Install → first trace

    1. 01

      Start your OpenTelemetry SDK

      Register your NodeSDK or TracerProvider first, with your exporters, sampler and resource.

    2. 02

      Call evalkit.init()

      It finds the registered provider and attaches one span processor to it (tracingMode attached); or pass tracerProvider explicitly to touch no global state.

    3. 03

      Produce one span

      Your own instrumentation’s spans and the auto-instrumented model calls now reach both your exporters and Syntropylabs.

    4. 04

      Open the trace

      Bridged spans keep their kind, events and token counts; evalkit.* attributes make them filterable by session and user.

    What a turn looks like

    Sample data in the product’s own table. Hover, focus or tap a claim to see the columns it points at.

    Sample run

    Operation Service StatusModel LatencyTokens≈CostScore
    support_agent.turn8f3a1c0d94e2session sess_4b1esupport-apiokgpt-4o-mini1.84 s3,412$0.000692%auto
    refund_agent.turnc21d7e5a30b8session sess_9a02tool looprefund-workerokclaude-sonnet-44.31 s7,905$0.038968%auto
    claude-code.turn5be04f7d1a96session sess_f77cClaude Codeunsetclaude-sonnet-448.20 s61,208$0.19
    rag.answere9a2b6c4d015errordocs-boterrorgpt-4o6.02 s9,880$0.031241%auto
    voice.turn17c8d3f2a4e0session sess_20d1ivr-agentokgpt-4o-realtime0.92 s1,104$0.0071

    fig. 1 · the traces table this integration fills · sample data

    What you see

    Taken from the SDK READMEs’ coverage tables. with flag means the signal leaves your machine only when you opt in.

    What Syntropylabs shows for Any OpenTelemetry instrumentor
    SignalCapturedDetail
    Attaches to a TracerProvider you already runyestracingMode owned | attached; your OTLP, Jaeger or Datadog exporters keep receiving every span
    Third-party spans bridged with kind, events, token and cache countsyes
    evalkit.* attributes fill prompt, completion, session, user and device columnsyesevalkit.prompt, evalkit.session_id and so on
    LLM spans from another instrumentorpartialfiled as function_call and not double-counted unless EVALKIT_COUNT_FOREIGN_LLM=1
    OTLP/HTTP receiveryesprotobuf or JSON at /v1/otlp, Bearer environment key
    Forward traces on to another OpenTelemetry backendno

    Limitations

    • Start your OpenTelemetry SDK before init(): OpenTelemetry JS refuses a duplicate global registration and silently orphans whichever side registers second.
    • Do not also add an EvalKit exporter to your provider: ingest is append-only, so a span sent twice is counted twice in token and cost aggregates.
    • A gen_ai.* span without evalkit.span_type or evalkit.sdk_version is treated as a second copy of a call EvalKit already reported; set EVALKIT_COUNT_FOREIGN_LLM=1 when that instrumentor is the only reporter.
    • Forwarding from Syntropylabs to another backend is not available; coexistence happens on the SDK side, where your exporters keep every span.

    Next steps

    Trace Any OpenTelemetry instrumentor today

    Tracing is free for one project on every plan, coding-agent traces included. Evaluations, datasets and simulation are Pro and up.