CtrlK
BlogDocsLog inGet started
Tessl Logo

kopai/otel-instrumentation

Instrument applications with the OpenTelemetry SDK and prove the telemetry is good by validating it against a local Kopai backend. Use when setting up observability, adding tracing/logging/metrics, deciding what to instrument or which attributes to add, retrofitting OTel into an existing codebase, threading context through call chains, configuring sampling, or when traces/logs/metrics aren't appearing after setup. Also use when users say things like "my traces aren't showing up", "I don't see any data", or "how do I add observability to my app". Do NOT use to investigate existing telemetry for a root cause (use root-cause-analysis), to build dashboards (use create-dashboard), or to instrument LLM and agent calls (use otel-genai-instrumentation).

Quality

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

SKILL.md

name:
otel-instrumentation
description:
Instrument applications with the OpenTelemetry SDK and prove the telemetry is good by validating it against a local Kopai backend. Use when setting up observability, adding tracing/logging/metrics, deciding what to instrument or which attributes to add, retrofitting OTel into an existing codebase, threading context through call chains, configuring sampling, or when traces/logs/metrics aren't appearing after setup. Also use when users say things like "my traces aren't showing up", "I don't see any data", or "how do I add observability to my app". Do NOT use to investigate existing telemetry for a root cause (use root-cause-analysis), to build dashboards (use create-dashboard), or to instrument LLM and agent calls (use otel-genai-instrumentation).
license:
Apache-2.0
metadata:
{"author":"kopai","version":"2.1.0"}

OpenTelemetry Instrumentation with Kopai

Emit wide events, then prove they are good.

A span is a wide event: one flat record carrying the full context of a unit of work. Kopai runs on localhost, so you never ship instrumentation on faith — you drive traffic through the app yourself and assert on what arrived. The loop is green when every assertion in validate-traces passes. Nothing here is finished while an assertion is red.

Branches

You are…Start at
Setting up a service that has no telemetryWorkflow step 1 below
Retrofitting OTel into an existing codebasecontext-propagation first — it is ~60% of the work — then the workflow
Chasing data that isn't arrivingtroubleshoot-no-data, then step 4 of the workflow

Workflow

#StepDone when
1Start the backend — npx @kopai/app startcurl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:4318/v1/traces -H 'Content-Type: application/json' -d '{"resourceSpans":[]}' returns 2xx
2Configure the environment — setup-environmentOTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, and a validation.run_id run tag are all exported in the shell that launches the app
3Instrument — the matching lang-<language> rule under Language SDKs below, then the judgment rulesThe app boots with the SDK loaded and prints no OTel export errors
4Drive traffic yourself — drive-trafficEvery discovered entry point hit at least once and at least one deliberate failure driven
5Assertvalidate-traces, then validate-logs, validate-metrics, validate-shutdownEvery assertion passes. The loop is green
6Fix — the matching troubleshoot-* rule under Fix it belowReturn to step 4 and re-drive. Never stop on a red assertion

Steps 4–6 are a loop, not a sequence. Report the instrumentation complete only when step 5 is green against traffic you drove in step 4 of the same run.

What earns a span

Two questions decide it:

  1. Interesting? — does this work meaningfully move latency or failure for the request?
  2. Aggregable? — grouped by name and attributes, does it produce a useful trend?

Both yes → create a span. Otherwise → put an attribute on the span you already have.

When in doubt, prefer attributes over child spans. An attribute on the parent needs no JOIN, costs nothing extra to query, and is immediately groupable. Full decision table, plus the three ways this goes wrong, in instrument-spans.

The core pattern

One span, rich attributes, explicit error status. Node.js shown; every language follows the same shape — see your language's rule under Language SDKs below:

import { trace, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("checkout");

export async function processPayment(order: Order) {
  return tracer.startActiveSpan("process-payment", async (span) => {
    try {
      span.setAttributes({
        "order.id": order.id, // IDs are attributes, never span names
        "order.total": order.total,
        "payment.provider": order.provider,
      });
      const receipt = await charge(order);
      span.setAttribute("payment.status", receipt.status);
      return receipt;
    } catch (err) {
      span.recordException(err as Error);
      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: (err as Error).message,
      });
      throw err;
    } finally {
      span.end();
    }
  });
}

Full code for every pattern — nested spans, async fan-out, queues, manual context — in custom-instrumentation.

Naming

  • Span names describe the operation: GET /api/users, db.query SELECT, process-payment
  • Attribute names are dot-namespaced: user.id, order.total, cache.hit
  • Follow OTel semantic conventions for anything standard: http.route, db.system, rpc.service
  • Namespace your own additions: app., checkout., <company>.

Span names must be low-cardinality. Never interpolate an ID into a span name — that belongs in an attribute.

Picking instrumentation packages

Detect the project's package manager first: the packageManager field in package.json is authoritative when present; otherwise the lockfile decides — pnpm-lock.yaml → pnpm, yarn.lock → yarn, bun.lock → bun, package-lock.json → npm. Run every install with that manager — a second manager writes a second lockfile and diverges silently. The npx @kopai/cli validation commands are manager-neutral; npx ships with Node and never touches the project's dependency tree.

Check deprecation against the registry, not install output — only npm prints full deprecation warnings at install time; pnpm truncates them and Yarn 4 prints none at all. When npm does print them, read them: never filter an install through tail, grep -v, or --quiet.

npm view @opentelemetry/instrumentation-<lib> deprecated   # empty output = not deprecated

npm view is a pure registry read that works inside pnpm/yarn projects too (npm ships with Node); native equivalents are pnpm view <pkg> deprecated and yarn npm info <pkg> --fields deprecated --json (Yarn 4).

A deprecation message usually names its replacement — use it, never the deprecated package; when none is named, follow the package's own migration guidance instead of guessing. When a framework team ships its own plugin (Fastify → @fastify/otel), prefer it over the contrib package: it registers through the framework's plugin system instead of intercepting require/import — interception that native-ESM imports bypass unless an experimental loader hook is added.

Rules

Task-scoped rule files. Load one only when the workflow points at it.

Setupsetup-backend, setup-environment

Language SDKslang-nodejs, lang-fastify, lang-nextjs, lang-python, lang-go, lang-java, lang-dotnet, lang-ruby, lang-php, lang-rust, lang-erlang, lang-cpp

What to instrumentinstrument-spans (what earns a span), instrument-attributes (which attributes, timing attributes, async summaries), instrument-errors (error, span status, slug), context-propagation (threading context; framework accessor gotchas), layered-telemetry (traces vs metrics vs logs), sampling

Prove itdrive-traffic (generate the traffic yourself), validate-traces, validate-logs, validate-metrics, validate-shutdown

Fix ittroubleshoot-no-data, troubleshoot-missing-spans, troubleshoot-missing-attrs, troubleshoot-wrong-port

References

Deep dives — load only when the task needs them:

Related skills

  • otel-genai-instrumentation — LLM, agent, and tool-call instrumentation
  • root-cause-analysis — investigating telemetry once it is flowing. Instrument for the questions that skill asks: who was affected, what changed, where the time went
  • create-dashboard — visualising the signals you emit here

CHANGELOG.md

SKILL.md

tile.json