agentleFS
LeanMeanCleanAppsLeanMeanCleanApps/CleanModelContextProtocol/.agents/skills/telemetry/SKILL.md

telemetry

Skill1 starsChanged 6 days agoMITView on GitHub

How OpenTelemetry traces, metrics and logs work - the standard OTEL_* settings that switch exporting on, the span LoggingBehaviour gives every use case, EF Core's built-in metrics, what each presentation layer instruments, the Aspire dashboard in Docker Compose, and the signals we're waiting on. Use when adding a span, metric or tag, changing what's exported or where, reading telemetry locally, or adding a dependency that should be traced.

11 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use dashboard, create-skill and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • GoogleSkill

    dashboard

    Configures and launches the GKE Capacity & Triage Dashboard scoped to a GCP project ID, GKE cluster name, or target ML workload ID. Uses the configure-and-launch-dashboard action and launch-workload-dashboard workflow to verify local dashboard health, discover cluster capacity, trigger live scans, and generate deep-linked URLs. Use when the user types "/dashboard", says "launch dashboard", "open dashboard", "show capacity", or "view triage dashboard" with a project, cluster, or workload. Don't use for general workload log querying or automated failure attribution (use the troubleshoot skill).

    Also in 3 projects that use this

  • ElasticSkill

    create-skill

    Interactively create a new Claude Code skill and add it to the elastic-docs-skills catalog. Use when the user wants to generate a new skill, scaffold a slash command, or build automation for docs tasks.

    Also in 3 projects that use this

  • VercelSkill

    setup

    Set up Vercel CLI and project configuration. Use when the user says "set up Vercel", "configure Vercel", "link to Vercel", or "vercel init".

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

7 rules. 0 are common, 5 are distinctive, and 2 are very specific to this project.

  • 0

    Common

    Most of the 9 edited versions we read keep these.

  • 5

    Distinctive

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before writing any telemetry code, check what the instrumentation already records.

    line 48
  2. Distinctive:

    Use stable packages only. When the only instrumentation is a prerelease package, go without that signal and add it to the watch list below.

    line 51
  3. Very specific:

    Test spans with an ActivityListener that keeps only the test's own trace (unit-tests).

    line 52
  4. Distinctive:

    Add a prerelease package, or hand-write instrumentation, to get a signal a package will give us later.

    line 102
  5. Distinctive:

    Add a custom metric that repeats one the instrumentation already records.

    line 103
  6. Distinctive:

    Put ids, tokens or personal data in span names, tags or metric tags.

    line 104
  7. Very specific:

    Start a span with new Activity(...).

    line 107

“Common” is measured: kept by at least 6 in 10 of the 9 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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Telemetry

The rule

Telemetry is OpenTelemetry, and it leaves the app over OTLP and nothing else. No vendor SDK, no Aspire package, nothing that ties the code to one backend. Signals come from stable Microsoft and OpenTelemetry instrumentation; we write our own only where nothing covers it, and that's one span per use case. Traces and metrics are always collected; they're exported, with the logs, only when OTEL_EXPORTER_OTLP_ENDPOINT is set. Every standard OTEL_* setting works as the OpenTelemetry SDK documents it.

SignalWhat we sendWhere it comes from
TracesA span for each incoming request and each use case, plus outgoing HTTP callsPresentation instrumentation, LoggingBehaviour
MetricsEF Core's own meter, ASP.NET Core, HttpClient and runtime metrics, with exemplars linking a measurement to a traceMicrosoft.EntityFrameworkCore, Presentation instrumentation
LogsEvery Serilog event, carrying the trace and span IDs of whatever was running when it was written. Validation failures and missing entities are logged inside the use case span, so they sit on its traceSerilog's OpenTelemetry sink (serilog)
BaggagePropagated with the trace context by the ASP.NET Core and HttpClient instrumentationW3C baggage header
EventsAn exception event on the use case span for unexpected errorsLoggingBehaviour

Each layer publishes under its own name, and only the presentation layer's startup knows about the OpenTelemetry SDK:

LayerPublishesHow
ApplicationApplicationTelemetry.Name activity source: a span per use case, named after the request, tagged with application.request.name and application.request.outcomeLoggingBehaviour, with System.Diagnostics only, so Application stays free of telemetry packages
InfrastructureMicrosoft.EntityFrameworkCore meter: queries, saves, active contexts, compiled query cacheBuilt into EF Core; startup subscribes to it by name
Presentationservice.name, service.version and deployment.environment.name on everything, plus its own instrumentationTelemetrySettings and the startup's Telemetry region

What each presentation layer adds:

TemplateInstrumentation
Minimal API, Web APIASP.NET Core and HttpClient traces and metrics, runtime metrics and, with authentication, its meters. Health endpoints aren't traced
GraphQLThe same, plus Hot Chocolate's resolver and operation spans from HotChocolate.Diagnostics
MCPThe same, plus the MCP SDK's own Experimental.ModelContextProtocol spans and mcp.*.operation.duration metrics
LambdaA span per invocation from OpenTelemetry.Instrumentation.AWSLambda, and runtime metrics. Traces and metrics are flushed before the function returns, because Lambda can freeze the environment as soon as it does

The Lambda template builds its tracer and meter providers once in Startup with Sdk.CreateTracerProviderBuilder() and Sdk.CreateMeterProviderBuilder() rather than AddOpenTelemetry(), because the classes Lambda Annotations generates build two service providers from the same services, and each would otherwise export everything again. X-Ray context extraction is off, so a "not sampled" header from Lambda can't switch tracing off; the collector can still send to X-Ray. Lambda container images can't use layers, so in AWS the collector is either built into the image (the ADOT collector extension) or runs as a gateway.

The app sends to an OpenTelemetry Collector, never straight to a backend, because that's how it should run in production: the collector owns batching, retries, sampling, scrubbing and the choice of backend, so none of that lives in the app. otel-collector.yaml holds its configuration: an OTLP receiver on 4317 (gRPC) and 4318 (HTTP), the memory_limiter and batch processors, and one exporter per backend.

Locally, Docker Compose runs the collector (otel/opentelemetry-collector) and the Aspire dashboard (mcr.microsoft.com/dotnet/aspire-dashboard), and the collector forwards everything to the dashboard. The dashboard is only a container that receives OTLP and shows logs, traces and metrics on the template's dashboard port (docker-and-compose); it keeps everything in memory, so it's empty after a restart. In production, run a collector beside the app or as a gateway and swap the dashboard exporter for your backend's.

Steps

  1. A new use case needs nothing: LoggingBehaviour gives it a span named after the request class, with its outcome as a tag.
  2. Before writing any telemetry code, check what the instrumentation already records. Request durations per route, GraphQL operations, MCP tool calls and Lambda invocations are all covered, so we don't count or time them again.
  3. To trace work inside Application, start a child span with using var activity = ApplicationTelemetry.ActivitySource.StartActivity("Name");, and use the semantic convention tag names where one exists.
  4. A new dependency in Infrastructure (a queue, an HTTP API) gets its spans and metrics from its own instrumentation package, if a stable one exists. Subscribe to its source or meter name in the presentation layer's startup.
  5. Use stable packages only. When the only instrumentation is a prerelease package, go without that signal and add it to the watch list below.
  6. Test spans with an ActivityListener that keeps only the test's own trace (unit-tests). The integration tests send a traceparent header and check the request and use case spans join up.
  7. To send telemetry somewhere new, add an exporter to otel-collector.yaml and list it in the pipelines. The app's settings don't change.
  8. To see it locally, run docker compose up -d --build and open the dashboard on the port docs/RUN.md lists. For a dotnet run, start the collector with docker compose up -d otel-collector and set OTEL_EXPORTER_OTLP_ENDPOINT to its gRPC port, http://$(docker compose port otel-collector 4317).

Example

// src/Application/Common/Behaviours/LoggingBehaviour.cs
using var activity = ApplicationTelemetry.ActivitySource.StartActivity(RequestName);
_ = activity?.SetTag(ApplicationTelemetry.RequestNameTag, RequestName);
...
catch (Exception exception)
{
    _ = activity?.SetTag(ApplicationTelemetry.RequestOutcomeTag, "error")
        .SetStatus(ActivityStatusCode.Error, exception.Message)
        .AddException(exception);

    throw;
}
# docker-compose.yml
environment:
  OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
  OTEL_EXPORTER_OTLP_PROTOCOL: grpc
# otel-collector.yaml
exporters:
  otlp_grpc/aspire:
    endpoint: aspire-dashboard:18889
    tls:
      insecure: true

Watch list

These signals and packages aren't stable for .NET yet. Check them when dependencies are updated, and adopt each one once it has a stable release:

  • OpenTelemetry.Instrumentation.EntityFrameworkCore: adds a span per SQL command. Until it's stable there are no database spans, only EF Core's metrics.
  • Profiles: there's no stable .NET SDK for the profiling signal.
  • The OpenTelemetry Events API: until then, events are [LoggerMessage] logs.

Don't

  • Reference an OpenTelemetry, Aspire or vendor package from Application or Infrastructure. They publish with System.Diagnostics, and Presentation subscribes.
  • Export anywhere but OTLP, or hard-code an endpoint. Settings come from the OTEL_* variables.
  • Point the app straight at a backend, the dashboard included. It sends to a collector, and the collector decides where things go.
  • Add a prerelease package, or hand-write instrumentation, to get a signal a package will give us later.
  • Add a custom metric that repeats one the instrumentation already records.
  • Put ids, tokens or personal data in span names, tags or metric tags.
  • Mark a span as an error for a validation failure or a missing entity. The caller got an answer; the outcome tag and the log say what happened.
  • Trace the health endpoints. Probes would bury everything else.
  • Start a span with new Activity(...). Use the layer's ActivitySource, so nothing is recorded when nobody is listening.

Check

dotnet test --filter "FullyQualifiedName~Telemetry|FullyQualifiedName~LoggingBehaviour"   # use case span and end-to-end trace tests
docker compose up -d --build                         # then open the dashboard on the port docs/RUN.md lists and make a request

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.