otel-instrumentation · dash0hq — Claude Code skill: OpenTelemetry tracing setup
Gives your agent a rulebook for OpenTelemetry spans, metrics and logs, so telemetry answers questions instead of burning budget.
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a claude-codeWhat otel-instrumentation does
The skill hands the agent a set of OpenTelemetry rules: how to name spans, which resource attributes to set, how to pick metric instruments and units, how to log errors, and how to scrub personal data before export. Language-specific files cover Node.js, Go, Python, Java, Scala, .NET, Ruby, PHP, the browser and Next.js App Router. There are also sections on Kubernetes, capturing prepared-statement parameters, and a post-deployment validation checklist.
One rule stands out. Sampling belongs in the collector, not the SDK. The skill tells you to set OTEL_TRACES_SAMPLER=always_on explicitly in every SDK, because the OpenTelemetry default ParentBased(root=AlwaysOn) lets a caller outside your trust boundary switch off a service's tracing through the traceparent header. Ratio-based SDK samplers get the same treatment: they decide before the request outcome is known, and that decision cannot be undone.
Before adding instrumentation packages, the skill verifies the versions actually exist in your project. That matters when you are wiring observability into a running service and would rather not discover a version mismatch at runtime.
What it helps with
- add spans to a new service with proper status codes
- move sampling policy out of the SDK into the collector
- pick metric instrument types and units
- correlate logs with traces via trace_id
- strip PII from telemetry before export
Example prompts for the agent
- Instrument this Node.js service with OpenTelemetry: traces, metrics and logs.
- Check that we are not sampling in the SDK and move the policy to the collector.
- Add trace correlation to our logs and remove personal data from span attributes.
How to install otel-instrumentation
1Skills CLI (Vercel)
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a claude-code2Manually: put the files in place
~/.claude/skills/otel-instrumentation/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/otel-instrumentation/
1Skills CLI (Vercel)
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a codex2Manually: put the files in place
.agents/skills/otel-instrumentation/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a cursor2Manually: put the files in place
.cursor/skills/otel-instrumentation/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install dash0hq/agent-skills otel-instrumentation2Manually: put the files in place
.github/skills/otel-instrumentation/SKILL.md1Skills CLI (Vercel)
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a gemini-cli2Platform CLI
gemini skills install https://github.com/dash0hq/agent-skills --path skills/otel-instrumentationManually: put the files in place
.devin/skills/otel-instrumentation/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add dash0hq/agent-skills --skill otel-instrumentation -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about otel-instrumentation
How do I install otel-instrumentation in Claude?
Run npx skills add dash0hq/agent-skills --skill otel-instrumentation -a claude-code. The skill lands at ~/.claude/skills/otel-instrumentation/SKILL.md.
What does otel-instrumentation do?
It guides the agent through instrumenting an application with OpenTelemetry traces, metrics and logs: naming rules, resource attributes, sensitive-data handling and post-deployment validation.
Where should sampling happen?
In the collector, per the skill, with OTEL_TRACES_SAMPLER=always_on set explicitly in each SDK. Otherwise an outside caller can disable a service's tracing through the traceparent header.
Source text
SKILL.md · Apache-2.0
---
name: 'otel-instrumentation'
description: Configures trace spans, defines custom metrics, sets up log exporters, and optimizes sampling strategies for OpenTelemetry instrumentation. Use when instrumenting applications with traces, metrics, or logs. Triggers on requests for observability, telemetry, tracing, metrics collection, logging integration, or OTel setup.
license: Apache-2.0
metadata:
author: dash0
version: '2.0.0'
workflow_type: 'advisory'
supports-traces: "true"
supports-metrics: "true"
supports-logs: "true"
---
# OpenTelemetry Instrumentation Guide
Expert guidance for implementing high-quality, cost-efficient OpenTelemetry telemetry.
## Rules & Quick Reference
| Use Case / Rule | Description |
|-----------------|-------------|
| [telemetry](./rules/telemetry.md) | **Entrypoint** — signal types, correlation, and navigation |
| [resolve-values](./rules/resolve-values.md) | Resolving configuration values from the codebase |
| [verify-dependencies](./rules/verify-dependencies.md) | Verifying instrumentation packages and versions exist before adding them |
| [resources](./rules/resources.md) | Resource attributes — service identity and environment |
| [k8s](./rules/platforms/k8s.md) | Kubernetes deployment — downward API, pod spec |
| [spans](./rules/spans.md) | Spans — naming, kind, status, and hygiene |
| [logs](./rules/logs.md) | Logs — structured logging, severity, trace correlation |
| [metrics](./rules/metrics.md) | Metrics — instrument types, naming, units, cardinality |
| [sensitive-data](./rules/sensitive-data.md) | Sensitive data — PII prevention, sanitization, redaction |
| [capture-database-query-parameters](./rules/capture-database-query-parameters.md) | Prepared-statement parameter capture per language (Java, .NET, Python, Node.js, Go) |
| [validation](./rules/validation.md) | Telemetry validation — post-deployment verification checklist |
| [nodejs](./rules/sdks/nodejs.md) | Node.js instrumentation setup |
| [go](./rules/sdks/go.md) | Go instrumentation setup |
| [python](./rules/sdks/python.md) | Python instrumentation setup |
| [java](./rules/sdks/java.md) | Java instrumentation setup |
| [scala](./rules/sdks/scala.md) | Scala instrumentation setup |
| [dotnet](./rules/sdks/dotnet.md) | .NET instrumentation setup |
| [ruby](./rules/sdks/ruby.md) | Ruby instrumentation setup |
| [php](./rules/sdks/php.md) | PHP instrumentation setup |
| [browser](./rules/sdks/browser.md) | Browser instrumentation setup |
| [nextjs](./rules/sdks/nextjs.md) | Next.js full-stack instrumentation (App Router) |
## Official documentation
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
- [Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)
- [Dash0 Integration Hub](https://www.dash0.com/hub/integrations)
## Getting started
Follow these steps when instrumenting an application from scratch:
1. **Pick your SDK rule** — choose the language-specific rule from the table above (e.g., [nodejs](./rules/sdks/nodejs.md), [python](./rules/sdks/python.md)).
2. **Set up resource attributes** — define service identity and environment per [resources](./rules/resources.md).
3. **Add spans, metrics, and logs** — instrument your code following [spans](./rules/spans.md), [metrics](./rules/metrics.md), and [logs](./rules/logs.md).
4. **Guard sensitive data** — scrub PII before export per [sensitive-data](./rules/sensitive-data.md).
5. **Validate** — confirm telemetry reaches the backend using the checklist in [validation](./rules/validation.md).
The snippet below shows a complete span with attributes and status for Node.js — see [nodejs](./rules/sdks/nodejs.md) for full setup including SDK initialisation, exporter configuration, and auto-instrumentation:
```js
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('my-service', '1.0.0');
tracer.startActiveSpan('operation-name', async (span) => {
try {
span.setAttribute('user.id', userId);
span.setAttribute('order.id', orderId);
const result = await processOrder(orderId);
span.setAttribute('order.status', result.status);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (err) {
// Record the exception as a structured log record, not span.recordException — see rules/spans.md
span.setStatus({ code: SpanStatusCode.ERROR, message: `${err.name}: ${err.message}` });
const spanContext = span.spanContext();
logger.error('operation-name.failed', {
'trace_id': spanContext.traceId,
'span_id': spanContext.spanId,
'exception.type': err.name,
'exception.message': err.message,
'exception.stacktrace': err.stack,
});
throw err;
} finally {
span.end();
}
});
```
## Key principles
### Signal density over volume
Every telemetry item should serve one of three purposes:
- **Detect** - Help identify that something is wrong
- **Localize** - Help pinpoint where the problem is
- **Explain** - Help understand why it happened
If it doesn't serve one of these purposes, don't emit it.
### Sample in the pipeline, not the SDK
Set the `AlwaysOn` sampler explicitly in every SDK (`OTEL_TRACES_SAMPLER=always_on`).
This overrides the OpenTelemetry default, `ParentBased(root=AlwaysOn)`, under which callers outside the trust boundary can silently switch off a service's tracing through the `traceparent` header.
Do not configure ratio-based SDK samplers either — they make irreversible decisions before the outcome of a request is known.
Defer all sampling to the [Collector](../otel-collector/rules/sampling.md), where policies can be changed centrally without redeploying applications.
See [SDK sampling](./rules/spans.md#sampling) for the full rationale and the conditions under which `ParentBased` is acceptable.
<!-- eval:skip -->
```
SDK (AlwaysOn, set explicitly) → Collector (sampling) → Backend (retention)
↓ ↓ ↓
All spans Head or tail Storage policies
exported sampling applied
```
© dash0 · Apache-2.0 · dash0hq/agent-skills
Similar skills
Everything for DevOps and infrastructure →otel-instrumentation by other authors comes first.