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

troubleshoot-missing-attrs.mdreferences/

titleimpacttags
Missing AttributesHIGHtroubleshoot, attributes

Missing Attributes

Spans arrive, but they're too narrow to answer anything. First, see what you actually have:

npx @kopai/cli traces search --resource-attr "validation.run_id=$RUN_ID" --json \
  | jq -r '.[].SpanAttributes // {} | keys[]' | sort -u

Diff that against references/attributes.md.

Symptom: nothing but auto-instrumented HTTP fields

Expected — no SDK can guess your user model or business domain. This isn't a bug, it's the work: instrument-attributes.md, starting with who was affected, what changed, and where the time went.

Symptom: an attribute is set in code but never arrives

Set on the wrong span. SetAttributes applies to the span you're holding. Inside a handler you usually want the current span from context, not a new one:

span := trace.SpanFromContext(ctx)          // the active span
span.SetAttributes(attribute.String("user.id", userID))
span = trace.get_current_span()
span.set_attribute("user.id", user_id)
const span = trace.getActiveSpan();
span?.setAttribute("user.id", userId);

Set after End(). Attributes added to an ended span are silently discarded. Set them before the span closes — in Go, before the defer span.End() fires.

No active span. getActiveSpan() returning a non-recording span means context didn't reach this code — context-propagation.md. In Node.js the ?. above hides this failure entirely; check the span is recording if you suspect it.

Wrong type. Attribute values must be primitives or arrays of primitives. Passing a struct, dict, or object is dropped by some SDKs and stringified by others. Serialise deliberately, or pick out the fields you need as separate attributes.

Symptom: resource attributes missing

Resource attributes are set once at SDK init, not per span. Set them via the environment so they can't drift:

export OTEL_RESOURCE_ATTRIBUTES="service.version=$(git rev-parse --short HEAD),deployment.environment=local"

If they're set programmatically, confirm the resource is merged with the default rather than replacing it — replacing it drops service.name and the SDK-detected fields.

Symptom: http.route is a concrete URL

/api/users/8823 instead of /api/users/{id} means the route pattern wasn't captured — usually middleware registered outside the router, so it never sees the match. Every ID becomes its own group and aggregation stops working. context-propagation.md.

Symptom: attributes present but useless for grouping

High-cardinality values where you wanted categories — a raw message instead of a category, a timestamp instead of a bucket. Add a low-cardinality companion rather than replacing the detail: keep exception.message for reading, add exception.slug for grouping (instrument-errors.md).

Verify

npx @kopai/cli traces search --resource-attr "validation.run_id=$RUN_ID" \
  --span-attr "user.id=<a value you drove>" --json | jq 'length'

Non-zero means the attribute is queryable, which is the only thing that counts.

Reference

https://opentelemetry.io/docs/concepts/semantic-conventions/

references

_sections.md

architectural-patterns.md

attributes.md

cli-reference.md

context-propagation.md

custom-instrumentation.md

drive-traffic.md

instrument-attributes.md

instrument-errors.md

instrument-spans.md

lang-cpp.md

lang-dotnet.md

lang-erlang.md

lang-fastify.md

lang-go.md

lang-java.md

lang-nextjs.md

lang-nodejs.md

lang-php.md

lang-python.md

lang-ruby.md

lang-rust.md

layered-telemetry.md

nextjs-examples.md

otel-docs.md

sampling.md

setup-backend.md

setup-environment.md

troubleshoot-missing-attrs.md

troubleshoot-missing-spans.md

troubleshoot-no-data.md

troubleshoot-wrong-port.md

validate-logs.md

validate-metrics.md

validate-shutdown.md

validate-traces.md

CHANGELOG.md

SKILL.md

tile.json