opentelemetry
Skill5 starsChanged 6 days agoMITView on GitHub
OpenTelemetry SDK for .NET — tracing, custom spans, metrics, .NET Aspire integration, and OTLP exporter configuration.
4 other projects use a version of this.
Adopted by
Most recently taken up by
- codewithmukesh/dotnet-claude-kittheir own version, first seen Nov 8, 2026
- grafana/skillstheir own version, first seen Oct 27, 2026
- Resgrid/Coretheir own version, first seen Oct 8, 2026
- mizchi/skillstheir own version, first seen Oct 2, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- codewithmukesh/dotnet-claude-kit★ 808same name
- mizchi/skills★ 369same name
- grafana/skills★ 302same name
- Resgrid/Core★ 238same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository15 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- aspnet-api-patternsSkill
- authenticationSkill
- cachingSkill
- clean-architectureSkill
- convention-learnerSkill
- dddSkill
- dotnet-project-initSkill
- ef-core-patternsSkill
- error-handlingSkill
- loggingSkill
- modern-csharpSkill
- resilienceSkill
- testing-dotnetSkill
- vertical-sliceSkill
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 1 is distinctive, and 3 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 96
Use OpenTelemetry semantic conventions where they exist.
- Distinctive: line 105
Do not tag spans with PII (email, name, card number, SSN).
- Very specific: line 117
Use IMeterFactory for DI-friendly meter creation.
- Very specific: line 152
When using .NET Aspire, OTEL is pre-configured via AddServiceDefaults() in the ServiceDefaults project.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
OpenTelemetry for .NET
Reference material for adding distributed tracing and metrics to ASP.NET Core services.
NuGet Packages
<!-- Core -->
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.*" />
<!-- Tracing instrumentation -->
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.*" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.*" />
<!-- Prerelease only (no stable 1.x yet) — a plain "1.*" range never resolves a beta -->
<PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.*-*" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.*" />
<!-- Exporter -->
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.*" />Setup — Program.cs
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation(options =>
{
options.RecordException = true;
options.Filter = ctx => ctx.Request.Path != "/health"; // exclude health checks
})
.AddEntityFrameworkCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("MyApp.Orders") // register custom ActivitySource names
.AddOtlpExporter())
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation()
.AddMeter("MyApp.Orders") // register custom Meter names
.AddOtlpExporter());The EF instrumentation no longer has SetDbStatementForText (removed in 1.13.0-beta.1): SQL text is
always captured and sanitized for SQL-like providers. Parameter values are opt-in via
OTEL_DOTNET_EXPERIMENTAL_EFCORE_ENABLE_TRACE_DB_QUERY_PARAMETERS=true — keep that off in production,
parameters carry PII.
Custom Spans (Activity)
ActivitySource is .NET's API for creating custom spans. One source per logical module.
// Register as singleton in DI:
builder.Services.AddSingleton(new ActivitySource("MyApp.Orders"));// Usage in a handler or service:
public class ProcessOrderHandler(IOrderRepository orders, ActivitySource activitySource)
{
public async Task<Order> HandleAsync(ProcessOrderCommand cmd, CancellationToken ct)
{
using var activity = activitySource.StartActivity("ProcessOrder");
activity?.SetTag("order.id", cmd.OrderId.ToString());
activity?.SetTag("order.customer_id", cmd.CustomerId.ToString());
try
{
var order = await orders.GetByIdAsync(cmd.OrderId, ct)
?? throw new NotFoundException($"Order {cmd.OrderId} not found.");
activity?.SetTag("order.status", order.Status.ToString());
// business logic...
return order;
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
activity?.AddException(ex); // .NET 9+; RecordException is obsolete
throw;
}
}
}Span Tag Conventions
Use OpenTelemetry semantic conventions where they exist. For business attributes, use <domain>.<attribute> format:
| Attribute | Example value |
order.id | "ord_abc123" |
order.status | "Pending" |
customer.tier | "Premium" |
payment.method | "CreditCard" |
Do not tag spans with PII (email, name, card number, SSN).
Child Spans
using var parent = activitySource.StartActivity("CreateOrder");
using var validationSpan = activitySource.StartActivity("ValidateOrder", ActivityKind.Internal);
// validationSpan is automatically a child of parent due to Activity.CurrentCustom Metrics
Use IMeterFactory for DI-friendly meter creation. One Meter per logical module.
// Register metrics class as singleton:
builder.Services.AddSingleton<OrderMetrics>();public sealed class OrderMetrics
{
private readonly Counter<long> _ordersCreated;
private readonly Histogram<double> _orderProcessingDuration;
private readonly UpDownCounter<long> _pendingOrders;
public OrderMetrics(IMeterFactory factory)
{
var meter = factory.Create(new MeterOptions("MyApp.Orders") { Version = "1.0" });
_ordersCreated = meter.CreateCounter<long>("orders.created", "orders", "Total orders created");
_orderProcessingDuration = meter.CreateHistogram<double>("orders.processing_duration", "ms", "Order processing time");
_pendingOrders = meter.CreateUpDownCounter<long>("orders.pending", "orders", "Current pending orders");
}
public void RecordOrderCreated(string tier)
=> _ordersCreated.Add(1, new TagList { { "customer.tier", tier } });
public void RecordProcessingDuration(double ms, string status)
=> _orderProcessingDuration.Record(ms, new TagList { { "order.status", status } });
public void IncrementPending() => _pendingOrders.Add(1);
public void DecrementPending() => _pendingOrders.Add(-1);
}.NET Aspire Integration
When using .NET Aspire, OTEL is pre-configured via AddServiceDefaults() in the ServiceDefaults project. Do not manually call AddOpenTelemetry() in projects that call AddServiceDefaults() — it's already wired.
// AppHost project — resource registration
var api = builder.AddProject<Projects.MyApp_Api>("myapp-api");
// API project — ServiceDefaults handles OTEL, health checks, service discovery
builder.AddServiceDefaults(); // replaces manual AddOpenTelemetry() setup
app.MapDefaultEndpoints(); // maps /health and /aliveServiceDefaults reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_SERVICE_NAME from the environment. Aspire injects these automatically when running via the AppHost.
You still need to register custom ActivitySource and Meter names:
// In ServiceDefaults ConfigureOpenTelemetry or in the service itself:
.AddSource("MyApp.Orders")
.AddMeter("MyApp.Orders")OTLP Exporter Configuration
Via environment variables (preferred — works with Aspire and Docker):
{
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
"OTEL_SERVICE_NAME": "myapp-api",
"OTEL_SERVICE_VERSION": "1.0.0"
}Via code (for explicit control):
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://localhost:4317");
options.Protocol = OtlpExportProtocol.Grpc; // or HttpProtobuf
})Correlation with Serilog
Serilog 3.1+ stamps TraceId and SpanId from Activity.Current onto every log event natively —
Serilog.Enrichers.Span is deprecated and no longer needed. Text sinks show them only when the
output template includes {TraceId} / {SpanId}; structured formatters and the OTLP sink carry
them automatically.
Do / Don't
| Do | Don't |
Register ActivitySource as singleton | Create new ActivitySource(...) per request |
| Tag spans with business-meaningful IDs (order ID, user tier) | Tag spans with PII |
Use SetStatus(Error) + AddException in catch blocks | Swallow exceptions without marking the span failed |
| Exclude health check endpoints from tracing | Trace /health and /alive — creates noise |
| Keep EF query-parameter capture off in production | Set OTEL_DOTNET_EXPERIMENTAL_EFCORE_ENABLE_TRACE_DB_QUERY_PARAMETERS outside dev |
Use ActivityKind.Internal for internal spans | Leave ActivityKind unset on child spans |
See Also
skills/logging/SKILL.md— Serilog setup; this file owns log↔trace correlation- .NET Aspire docs:
AddServiceDefaults()source inServiceDefaults/Extensions.cs
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

