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).
—
—
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
| title | impact | tags |
|---|---|---|
| Validate Shutdown | HIGH | validate, shutdown, flush |
The batch processor buffers. A process that exits without flushing drops everything it was holding — typically the last few seconds, which is exactly the window containing the crash you wanted to debug.
This failure is invisible during normal development: long-running servers flush on their interval and look fine, while short-lived jobs and crashed processes silently lose their tail. Assert on it once, explicitly.
RUN_ID=$(uuidgen); export OTEL_RESOURCE_ATTRIBUTES="validation.run_id=$RUN_ID"
# start the app, drive one request, then:
kill "$APP_PID" # SIGTERM — never kill -9
sleep 5
npx @kopai/cli traces search --resource-attr "validation.run_id=$RUN_ID" --json | jq 'length'Pass: the spans from just before the signal are present. Fail: shutdown isn't flushing. Three usual causes:
exit() before shutdown() resolves — it must be awaitedEach language's setup in references/lang-<language>.md shows the shutdown hook. Verify it
exists in your code rather than assuming the SDK installed one — most SDKs do not.
For CLI tools, jobs, serverless handlers, and test runs, the process may finish before the first scheduled export ever fires.
Pass: a one-shot invocation produces spans in Kopai. Fail: the run ended before the batch interval. Two fixes, in order of preference:
forceFlush() (or the language equivalent) before exiting — correct and explicitOTEL_BSP_SCHEDULE_DELAY=500 — a blunt instrument that shortens the window but
never closes itFor serverless, flushing before returning is mandatory, not an optimisation: the runtime freezes the process the moment your handler returns, and the buffer freezes with it.
Stop Kopai, then start the app:
# with the Kopai backend stopped
<start your app>
curl -s -o /dev/null -w "%{http_code}\n" "$BASE/"Pass: the app starts and serves traffic normally. Export errors may be logged; that is fine. Fail: the app refuses to start, hangs, or 500s because it can't reach the collector. Instrumentation must never be load-bearing. Wrap SDK init so a failure logs and continues — an observability outage that takes production down with it is worse than no observability.
S1–S3 pass. That closes the loop: the telemetry is correct, complete, survives shutdown,
and cannot take the service down. Report what you instrumented, which assertions you ran,
and the $RUN_ID they were green against.
references