# OpenTelemetry setup

Loaded on demand from `../SKILL.md`.

## Instrument with OpenTelemetry

Use the OpenTelemetry SDK for the language and export OTLP to a Collector,
never straight to a vendor. The Collector is where batching, redaction,
sampling and routing happen, and swapping backends becomes a config change.

Order of work:

1. **Resource attributes** on every signal: `service.name`, `service.version`,
   `deployment.environment.name`, plus `k8s.*` or `cloud.*` from detectors.
2. **Auto-instrumentation** for HTTP servers and clients, gRPC, database
   drivers and message clients. It gives correct span kinds and semantic
   convention attributes for free.
3. **Manual spans** only around business steps that auto-instrumentation cannot
   see, such as pricing an order or rendering a report.
4. **Propagation**: W3C `traceparent` over HTTP, and the same context injected
   into message headers so consumers continue the trace (use span links when a
   consumer batches many messages).

Node.js example of a manual span:

```ts
import { trace, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("orders-service");

export async function priceOrder(order: Order) {
  return tracer.startActiveSpan("price_order", async (span) => {
    span.setAttribute("order.line_count", order.lines.length);
    try {
      const total = await pricing.compute(order);
      span.setAttribute("order.currency", total.currency);
      return total;
    } catch (err) {
      span.recordException(err as Error);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw err;
    } finally {
      span.end();
    }
  });
}
```

Semantic conventions to follow rather than invent: `http.request.method`,
`http.route`, `http.response.status_code`, `url.path`, `server.address`,
`db.system`, `db.operation.name`, `messaging.system`, `error.type`. Custom
attributes get a namespace (`order.*`, `product.*`).

## Collector pipeline sketch

```yaml
receivers:
  otlp:
    protocols: { grpc: {}, http: {} }
processors:
  memory_limiter: { check_interval: 1s, limit_percentage: 80 }
  attributes/redact:
    actions:
      - { key: user.email, action: delete }
  batch: {}
exporters:
  otlphttp: { endpoint: https://telemetry.example.com }
service:
  pipelines:
    traces:  { receivers: [otlp], processors: [memory_limiter, attributes/redact, batch], exporters: [otlphttp] }
    metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [otlphttp] }
    logs:    { receivers: [otlp], processors: [memory_limiter, attributes/redact, batch], exporters: [otlphttp] }
```

`memory_limiter` goes first and `batch` last.
