opentelemetry-net-instrumentation
Provides guidance for implementing OpenTelemetry instrumentation in .NET codebases, covering tracing (Activities/Spans), metrics, logs, naming conventions, error handling, performance, SDK setup, resources, context propagation, and API design best practices.
Install / Use
npx skills add Aaronontheweb/dotnet-skills --skill opentelementry-dotnet-instrumentationInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Our assessment of opentelemetry-net-instrumentation
opentelemetry-net-instrumentation scores 93/100 on our quality scale, 780th of 4,610 Development & Engineering skills we index (top 17%).
Its SKILL.md is 27 KB long, well organised into 36 sections with 16 code examples: a thorough specification that gives an agent plenty to work with.
With 1,183 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 15 days ago, so opentelemetry-net-instrumentation is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
opentelemetry-net-instrumentation compared with similar skills
All 4 of these similar skills score higher than opentelemetry-net-instrumentation; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| opentelemetry-net-instrumentation (this skill)by Aaronontheweb | 93 | 1.2k | 15d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 89.0k | 17d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 74.3k | today | CLAUDE.md |
| ai-job-searchby MadsLorentzen | 100 | 44.8k | today | CLAUDE.md |
| claude-howtoby luongnv89 | 100 | 41.7k | 3d ago | CLAUDE.md |
Frequently asked questions
- How do I install opentelemetry-net-instrumentation?
- Run
npx skills add Aaronontheweb/dotnet-skills --skill opentelemetry-net-instrumentation. The install tabs above show the steps for each supported agent. - Which AI agents does opentelemetry-net-instrumentation work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is opentelemetry-net-instrumentation safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is opentelemetry-net-instrumentation still maintained?
- The repository was last updated 15 days ago, so opentelemetry-net-instrumentation is actively maintained.
Skill content
View source on GitHubname: opentelemetry-net-instrumentation description: Provides guidance for implementing OpenTelemetry instrumentation in .NET codebases, covering tracing (Activities/Spans), metrics, logs, naming conventions, error handling, performance, SDK setup, resources, context propagation, and API design best practices. version: 2.0.0 tags:
- opentelemetry
- dotnet
- observability
- tracing
- metrics
- logs
- performance
OpenTelemetry .NET Instrumentation Skill
When to Use
- Adding OpenTelemetry instrumentation to .NET code (traces, metrics, logs)
- Creating or modifying ActivitySources, Meters, or ILogger usage
- Setting up the OpenTelemetry SDK, resources, exporters, or sampling
- Reviewing telemetry implementations for spec compliance
- Optimizing instrumentation performance
- Designing telemetry APIs that become part of the public surface
- Implementing context propagation across service boundaries
Architecture: .NET Is Different
CRITICAL: The .NET OpenTelemetry implementation is fundamentally different from other platforms. .NET provides tracing, metrics, and logging APIs in the framework itself. That means OTel does not provide a separate instrumentation API — it uses the built-in .NET APIs and acts as the collection/export layer.
The Three Built-in .NET APIs (Primary — Zero Dependencies)
| Signal | .NET Framework API | Namespace |
|--------|-------------------|-----------|
| Tracing | ActivitySource / Activity | System.Diagnostics |
| Metrics | Meter / Counter<T> / Histogram<T> / etc. | System.Diagnostics.Metrics |
| Logging | ILogger<T> | Microsoft.Extensions.Logging |
These are the primary and only APIs library authors should use for instrumentation. They ship with the .NET runtime — no NuGet packages required.
The OTel Collection/Export Layer (Secondary — Application Root Only)
OTel NuGet packages are the collection and export layer, added only at the application composition root (not in libraries):
| Package | Purpose | When to add |
|---------|---------|-------------|
| OpenTelemetry.Extensions.Hosting | DI integration for ASP.NET Core / generic host | Application only |
| OpenTelemetry.Exporter.Console | Console exporter (dev/testing) | Application only |
| OpenTelemetry.Exporter.OpenTelemetryProtocol | OTLP exporter (production) | Application only |
| OpenTelemetry.Exporter.Prometheus* | Prometheus metrics endpoint | Application only |
| OpenTelemetry.Instrumentation.AspNetCore | Auto-instrument ASP.NET Core requests | Application only |
| OpenTelemetry.Instrumentation.Http | Auto-instrument HttpClient calls | Application only |
| OpenTelemetry.Instrumentation.SqlClient | Auto-instrument SQL calls | Application only |
Package Decision Guide
Before adding ANY OpenTelemetry NuGet package, discuss the trade-off with the user:
"You're about to add an OTel NuGet package. Is this an application where you need to export telemetry to an observability backend (Jaeger, Prometheus, OTLP collector)? If you're writing a library, you likely need zero OTel packages — just use
System.Diagnostics.ActivitySource/System.Diagnostics.Metrics.Meterand let the consuming application configure the export pipeline. Do you want to proceed?"
Library authors: Add nothing. Use only System.Diagnostics.* and ILogger.
The consuming application wires up the SDK and exporters.
Application authors: Add OpenTelemetry.Extensions.Hosting + the exporters and instrumentation libraries you need. See sdk-resources-and-logs-reference.md for full setup patterns.
Never add OpenTelemetry.Api to a library — System.Diagnostics.* IS the API.
For SDK setup, resource configuration, exporters, sampling, and logs integration, see sdk-resources-and-logs-reference.md.
Core Principles
Resiliency First
CRITICAL: Exceptions in diagnostic/tracing/metrics logic MUST NEVER impact application processing.
- Assume Activity instances can be null. Always protect against null Activity references except in Activity extension methods (use
activity?.ExtensionMethod()) - Guard all instrumentation code with appropriate null checks
API Surface Awareness
- Any telemetry emitted becomes part of the public API surface
- Changes are subject to breaking changes guidelines
- Telemetry should be emitted by default (users opt-in to collection via OpenTelemetry extensions)
- Exception: High-cardinality metric dimensions may require explicit opt-in
Standards Compliance
- Follow Microsoft best practices for distributed tracing instrumentation
- Follow OpenTelemetry semantic conventions
- Attribute values support: string, boolean, double (IEEE 754), int64, byte arrays, and homogeneous arrays of these primitive types. Null/empty values are valid and meaningful per the OTel AnyValue spec — they MUST be stored and passed to exporters.
- Attribute keys must be non-null, non-empty strings
Traces / Spans (Activities)
ActivitySource Setup
// ✅ CORRECT: Use ActivitySource, not DiagnosticSource
public class MyFeature
{
// Primary ActivitySource - name typically matches the component or NuGet package name
private static readonly ActivitySource ActivitySource = new("MyApp.MyComponent", "1.0.0");
// Specialized ActivitySource for opt-in scenarios
private static readonly ActivitySource DetailedActivitySource = new("MyApp.MyComponent.Detailed", "1.0.0");
}
Rules:
- Every component defines a primary
ActivitySourcefor mainstream activities - Name typically matches the component or NuGet package (e.g.,
"MyCompany.MyLibrary") - Version the ActivitySource using SemVer
- Create separate
ActivitySources for specialized or opt-in scenarios. Use hierarchical source names, e.g.MyCompany.MyLibraryandMyCompany.MyLibrary.Detailed, so consuming applications can subscribe only to the sources they want viaAddSource(...)and backends can filter by instrumentation scope.
Creating Activities
// ✅ Check HasListeners, null-check, then guard expensive work behind IsAllDataRequested
if (ActivitySource.HasListeners())
{
using var activity = ActivitySource.StartActivity("ProcessItem", ActivityKind.Internal);
if (activity != null && activity.IsAllDataRequested)
{
activity.DisplayName = "Processing order #12345";
activity.SetTag("app.item_id", itemId);
activity.SetTag("app.item_type", itemType);
}
}
// ❌ WRONG: Don't start activities in fire-and-forget tasks where the
// using scope ends before the async work completes (AsyncLocal context is lost)
async Task HelperAsync()
{
using var activity = ActivitySource.StartActivity("Helper");
_ = Task.Run(() => DoWorkAsync()); // ❌ activity disposed before task completes
}
Rules:
- Check
ActivitySource.HasListeners()before creating (zero-allocation fast path) - Always null-check Activity after creation (listener may filter or sample it out)
- Never start activities in async helper methods (
Activity.CurrentusesAsyncLocal) - Guard expensive tag computation behind
activity.IsAllDataRequested - Use W3C TraceContext. .NET Core 3.0+ / .NET 5+ uses it by default; older TFMs or .NET Framework apps may set
Activity.DefaultIdFormat = ActivityIdFormat.W3Cat startup and useActivity.ForceDefaultIdFormat = trueto override hierarchical parents.
Activity Naming
// ✅ Unique operation name, friendly display name (null-check before accessing)
using var activity = ActivitySource.StartActivity(
name: "ProcessItem", // Unique, identifies class of spans
kind: ActivityKind.Internal
);
if (activity != null)
activity.DisplayName = "Processing order #12345"; // User-friendly, can be specific
// ❌ WRONG: Don't include runtime data in operation name
using var badActivity = ActivitySource.StartActivity($"Process_{itemId}"); // ❌
Rules:
- Each span type has unique
OperationName(identifies statistically interesting class of spans) - Operation name should NOT contain runtime data (only compile/config-time info)
- Use human-readable
DisplayNamefor specifics - Follow OpenTelemetry span naming conventions
SpanKind Selection
Choose the correct ActivityKind to clarify the span's role in distributed tracing:
| ActivityKind | OTel SpanKind | When to use |
|----------------|---------------|-------------|
| Internal | INTERNAL | Default — in-process operations not crossing a remote boundary |
| Server | SERVER | Processing an incoming request/response call (HTTP server, gRPC server, RPC server) |
| Client | CLIENT | Making an outgoing request/response call (HTTP client, database client, RPC call) |
| Producer | PRODUCER | Enqueuing/publishing deferred work (message queue publish, event emit, job enqueue) |
| Consumer | CONSUMER | Dequeuing/processing deferred work (message queue receive, event handle, job dequeue) |
Rules:
- A single span SHOULD NOT serve more than one purpose
- Create the outgoing span before injecting its
SpanContextinto the request. If you inject first, the parent's context propagates instead and the outgoing span ends up dangling (no connection to the downstream call). - See traces-and-propagation-reference.md for detailed SpanKind guidance with examples
Span Attributes (Tags)
// ✅ Application code: use your own namespace
activity?.SetTag("myapp.order_id", orderId);
activity?.SetTag("myapp.payment.status", "confirmed");
// ✅ Manual infrastructure instrumentation: use semantic conventions
// activity?.SetTag("db.system.name", "postgresql"); // custom database client
// activity?.SetTag("http.request.method", "GET"); // custom HTTP transport
// Values can be strings, numbers, booleans, or homogeneous arrays
activity?.SetTag("app.item_count", 42);
activity?.SetTag("app.related_ids", new int[] { 1, 2, 3 });
// ❌ WRONG: PascalCase, hyphen delimiter, plural, or unrelated namespace
activity?.SetTag("MyApp.OrderId", orderId); // ❌ Wrong case
activity?.SetTag("myapp.order-id", orderId); // ❌ Wrong delimiter
Rules:
- Namespace prefix matching your component:
myapp.*,myapp.db.* - All lowercase, underscore (
_) delimiters, singular form - Attribute values: string, boolean, double, int64, byte arrays, homogeneous arrays (null/empty valid per AnyValue spec)
- Business/domain attributes: use your own namespace (
myapp.*). - HTTP, database, messaging, or RPC concepts you manually instrument: use semantic conventions. Do not duplicate attributes already emitted by auto-instrumentation. Do not use OTel namespaces as prefixes for custom attributes.
Activity Status and Errors
try
{
await ProcessItemAsync(); // ✅ success: leave status Unset, do not call SetStatus(Ok) — see rules below
}
catch (Exception ex)
{
if (activity != null)
{
activity.SetStatus(ActivityStatusCode.Error, ex.Message); // modern API
activity.SetTag("error.type", ex.GetType().FullName);
}
throw;
}
Rules (per Recording Errors and the Set Status API spec):
- Leave span status Unset on success — do not call
SetStatus(ActivityStatusCode.Ok). Okis for application code, not instrumentation libraries. The trace API sp
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
89.0kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
74.3kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
44.8kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
claude-howto
41.7kA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
