Org Skills

OpenTelemetry setup and signals

Supporting material for service-operability. Agents load it on demand; it ships inside the skill folder.

RawSource

Verified 2026-09 against https://opentelemetry.io/docs/ and https://www.w3.org/TR/trace-context/.

Shape

Instrument every service with the OpenTelemetry SDK, export OTLP to a collector, and let the collector route to the backend. Changing vendor, adding sampling or scrubbing a field then happens in collector configuration instead of in a redeploy of every service.

service ──OTLP──▶ collector ──▶ traces backend
                        ├──▶ metrics backend
                        └──▶ logs backend

.NET

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService(serviceName: "orders-api"))
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter())
    .WithMetrics(m => m
        .AddAspNetCoreInstrumentation()
        .AddRuntimeInstrumentation()
        .AddOtlpExporter());

builder.Logging.AddOpenTelemetry(o =>
{
    o.IncludeScopes = true;
    o.AddOtlpExporter();
});

Database and messaging instrumentation come from their own packages; add the ones matching the libraries actually in use.

Node.js

import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

const sdk = new NodeSDK({
  serviceName: 'orders-api',
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
process.on('SIGTERM', () => { sdk.shutdown().finally(() => process.exit(0)); });

Start the SDK before importing instrumented libraries — auto-instrumentation patches modules at require/import time, so anything imported first is not traced. Use a preload (--import / -r) rather than an import at the top of the application file.

Context propagation

  • HTTP: the W3C traceparent header, handled by the instrumentation for both inbound and outbound.
  • Queues: not automatic in the useful direction. Inject the current context into the message headers on publish and extract it on consume, or the consumer's work appears as an unrelated trace and you lose the causal link exactly where async debugging is hardest.
  • Background jobs: start a new root span and link it to the span that scheduled the work.
  • Carry a business correlation id (order id, tenant) as a span attribute and a log field, since that is what support actually searches by.

Logs

  • Structured JSON, one event per line, fields rather than interpolated sentences.
  • Every line carries trace id, span id and correlation id. This is what makes logs and traces one investigation rather than two.
  • Levels that mean something: error is something a human must look at; warn is a degraded but handled condition; info is a state change worth reconstructing; debug is off in production.
  • Redact centrally in the logger — a processor that strips known-sensitive keys and obvious token patterns. Per-call-site discipline fails the first time someone logs a whole request object.
  • Never log credentials, tokens, full card numbers, or personal data beyond the purpose. For regulated data, log an identifier and look it up in a system with its own access control.

Sampling

  • Fixed-rate head sampling drops the traces you need. Prefer tail-based sampling in the collector: keep everything that errored or was slow, sample the rest.
  • Keep 100% of traces for low-traffic but high-value flows (checkout, payment).
  • Metrics are not sampled — they are aggregate and cheap, which is why alerting is built on them.

A starting alert set

Alert Signal Why
Error rate 5xx / total per endpoint over a short window Users are seeing failures now
Latency p99 against the objective Slow is a failure with a longer timeout
Saturation Pool utilisation, queue depth, event loop or thread pool lag Warns before the first two fire
Queue age Oldest unprocessed message Consumers stopped or fell behind
Dead letters Count over a window Messages are failing permanently
Readiness flapping Instances going ready/unready repeatedly A dependency check or capacity problem

Alert on symptoms users feel. CPU and memory belong on dashboards for diagnosis, not on pagers — they page for things nobody noticed and stay quiet for things everybody did.