Ollygarden otel instrumentation planning
Skill ollygarden/skills/skills/ollygarden-otel-instrumentation-planning
OllyGarden's opinionated AI agent skills for OpenTelemetry and observability workflows
npx -y skills add ollygarden/skills --skill ollygarden-otel-instrumentation-planningAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Plan Minimal Viable Instrumentation for a codebase. Use when starting instrumentation from scratch, assessing what to instrument in an existing application, or deciding what boundaries need manual vs auto instrumentation. Identifies application boundaries, classifies them as auto-instrumented or manual, selects signals, plans attributes with cardinality awareness, and produces an actionable instrumentation plan tied to SLOs/SLIs. Triggers on "what should I instrument", "add observability", "instrumentation plan", "MVI", or when scanning a codebase without existing instrumentation.
SKILL.md
19.6 KB, as published. Nobody here has run it
Instrumentation Planning
This skill plans what to instrument and why, producing an actionable instrumentation plan tied to SLOs/SLIs. It does not generate code. Implementation mechanics — semconv lookups, SDK setup, language-specific instrumentation code — are handled by the manual, auto-instrumentation, SDK setup, and language-specific setup skills.
Philosophy
Instrumentation exists to understand runtime behavior when debugging production issues. It is not documentation, not logging-for-the-sake-of-logging, and not a checkbox exercise.
OllyGarden's core beliefs:
- The pipeline begins at instrumentation. Quality at the source means less noise downstream. A bad span cannot be fixed by a clever query.
- Instrument the unexpected, not the expected — except for critical business milestones. A successful checkout matters; a health check returning 200 does not.
- Every signal should answer one question: "Will this help debug a production issue?" If the answer is no, leave it out. You can always add it later.
- Minimal Viable Instrumentation (MVI) is the goal. The minimum set that enables production debugging, not maximum coverage.
Workflow
Follow these six steps in order. Each step builds on the previous one.
Step 1: Assess the codebase
Scan the repository to understand what exists before planning anything new.
- Search for OpenTelemetry imports, SDK initialization code, and tracer/meter/logger provider setup across all source files.
- Search for existing spans (start span calls,
@WithSpanannotations, middleware), existing metrics (counter, histogram, gauge creation), and structured log calls. - Classify the codebase:
- Greenfield: no OTel imports, no SDK initialization, no existing spans or metrics.
- Brownfield: some instrumentation exists. Note exactly what is already covered to avoid duplication.
- Identify the application type (may be a hybrid):
- API service: handles inbound HTTP/gRPC requests, responds synchronously.
- Batch processor: runs on a schedule, processes data in bulk.
- Real-time system: event-driven, streaming, or WebSocket-based.
- Background jobs: async workers, queue consumers, cron tasks.
- Note the languages, frameworks, and libraries in use — these determine which instrumentation libraries are available and what auto-instrumentation covers.
- Identify business workflows from routes, handlers, jobs, consumers, and service calls:
- user-visible outcome
- entry point
- critical dependencies
- degraded states and fallbacks
- business milestones that need positive confirmation
For brownfield codebases, list each existing instrumented boundary so that steps 2-6 can build on what is already in place rather than recreating it.
Step 2: Map and classify application boundaries
List every runtime boundary, decide whether to include or exclude it, then classify
candidate boundaries as auto, manual, auto + manual context, or excluded.
2.1 — Identify all boundaries:
Scan for every boundary type present in the codebase:
- HTTP/gRPC handlers (inbound requests)
- Database calls (queries, transactions)
- Messaging (publish/subscribe, queue send/receive)
- RPC calls to other services (outbound HTTP, gRPC)
- Cache operations (Redis, Memcached, in-memory)
- External API calls (third-party services)
- File system or blob storage operations
- Background job dispatch and execution
2.2 — Include or exclude:
For each boundary, ask: "Will this help debug a production issue?"
- Include: business-critical operations, user-facing APIs, database operations, external service calls, message queue operations, cache interactions — anything where failure has user or business impact.
- Exclude: health checks (
/health,/healthz,/ready,/livez), metrics endpoints (/metrics,/debug/), static asset handlers (/static/,/assets/, favicon), internal Kubernetes probes, admin/debug endpoints.
Read references/boundary-prioritization.md for the full inclusion/exclusion framework.
2.3 — Classify each included boundary as auto, manual, auto + manual context, or excluded:
For each included boundary, determine the instrumentation strategy. Consult the companion skills as knowledge sources:
auto: an instrumentation library or auto-instrumentation package covers this boundary type in the detected language/framework and the generated span explains the production debugging question. The SDK setup/finalization phase configures it. The implement phase writes NO manual boundary span for it.manual: no instrumentation library covers this specific operation. The implement phase must add hand-written span code.auto + manual context: auto-instrumentation covers the technical boundary, but the trace would miss a business or degraded outcome unless code adds bounded context to the current span or emits a bounded event. The implement phase must not duplicate the auto span.excluded: the boundary or service exists but is intentionally omitted, suppressed, filtered, or downsampled because telemetry would be low-value, noisy, sensitive, or unrelated to business workflows.
Rules:
- If an auto-instrumentation library exists for a standard boundary type, prefer
autoorauto + manual context; do not create duplicate manual spans. - Use
manualonly when no auto library covers the specific operation or when the operation is a business milestone/workflow boundary rather than a standard technical boundary. - Use
excludedfor health checks, metrics/debug/static endpoints, low-value platform traffic, DTO mapping, object conversion, validation helpers, utility functions, and other implementation details.
Read references/boundary-prioritization.md for a reference of which boundary types
are commonly auto-instrumented across languages.
Also apply the universal guidance from:
ollygarden-otel-auto-instrumentationfor auto-instrumentation scope, tuning, suppressions, duplication avoidance, sensitive capture risks, and volume/noise.ollygarden-otel-manual-instrumentationfor manual boundary quality, signal choice, propagation, cardinality, and error ownership.
Step 3: Select signals and plan error handling per boundary
For each included boundary, decide which telemetry signal(s) to use and how errors should be handled.
3.1 — Signal selection:
Walk through each boundary and assign one or more signals:
- Span: for request-scoped work with duration, status, and parent-child context.
- Metric: for aggregated measurements (rates, distributions, gauges).
- Log/Event: for discrete occurrences that need full payload capture.
Read references/signal-selection.md for the signal decision tree and the error
handling guidance (the error golden rule).
Common signal combinations:
- HTTP handlers: span + request duration histogram.
- Database calls: span (for trace context) + optional latency histogram.
- Queue consumers: span (for distributed trace) + processing rate counter.
Apply the error golden rule from the reference: record errors on spans using status and events, do not create separate error metrics that duplicate what span-derived metrics already provide.
Also define the overall signal scope for SDK setup:
- traces: in scope or out of scope
- metrics: in scope, standard/SLO-only, or out of scope
- logs: in scope only when a redaction/export policy is stated
- baggage: in scope only when bounded allowlisted keys are stated
Do not leave signal scope implicit. SDK setup must be able to tell which providers, exporters, propagators, and processors are allowed.
3.2 — Error handling (manual boundaries only):
For each manual boundary, specify in the plan:
- Set span status to ERROR on failure: always yes when the operation fails from the caller's perspective. Do not set ERROR for handled errors or retries that succeed.
- What to record on error: prefer the Logs API for new instrumentation, following the
accepted span-events-to-logs migration plan. Span event APIs are not yet deprecated in
released specifications or every language SDK. Set the
error.typeattribute. - Follow the error golden rule from
references/signal-selection.md: either record the error OR return with context, never both.
This prevents the implement agent from guessing about error handling. Every manual boundary in the plan must have an explicit "On error" specification.
Step 4: Plan attributes and assess cardinality
For each instrumented boundary, define the attributes to capture and check cardinality.
- List the proposed attributes for each boundary's signals.
- Check OpenTelemetry semantic conventions first — always prefer standard attribute
names (
http.request.method,db.system.name,messaging.system) over custom ones. Only invent custom attributes when no semantic convention covers the concept. - Read
references/cardinality-guide.mdfor cardinality tier definitions, UCUM unit conventions, and anti-patterns to avoid. - Classify each attribute's cardinality:
- LOW: bounded set, known at deploy time (HTTP method, status code class, db system).
- MEDIUM: bounded but larger set (HTTP route with parameterized paths, operation name).
- HIGH: unbounded or very large set (user ID, request ID, full URL path, query text).
- Flag any attribute in the HIGH cardinality tier. These are acceptable on spans (backends handle high-cardinality span attributes) but dangerous on metrics.
- Ensure every metric has only LOW or MEDIUM cardinality attributes. If a metric needs a HIGH cardinality dimension, move that dimension to a span attribute instead.
- Review PII and sensitive data risks for each signal source. Explicitly forbid raw personal data, credentials, tokens, cookies, authorization headers, request/response bodies, message payloads, raw exception messages, raw IDs, timestamps, and other unbounded values unless the plan states a safe transformation such as redaction, aggregation, or hashing.
Step 5: Define target SLOs/SLIs
Based on the application type from step 1, recommend SLOs and map them to the planned instrumentation.
- Read
references/slo-driven-planning.mdfor the SLO-by-application-type mapping and SLI implementation requirements. - Recommend SLOs appropriate to the application type:
- API services: availability (success rate), latency (p99 response time).
- Batch processors: completion rate, processing duration, freshness.
- Real-time systems: message processing latency, delivery success rate.
- Background jobs: success rate, queue depth / age, execution duration.
- Map each SLO to a concrete SLI definition — the specific metric, threshold, and time window that will measure the objective.
- Map each SLI to the boundary and signal from steps 2-3 that will produce the required data. Verify the planned instrumentation actually covers each SLI.
- Flag any recommended SLO that is not yet covered by the planned instrumentation. These are gaps that must be resolved before the plan is complete.
- Important: recommend the metrics needed for SLIs, but do not add them to the plan unless the user explicitly requests it. SLI metrics may already be derivable from planned spans.
Step 6: Produce the instrumentation plan
Assemble the final output using the template below.
- Fill in every section of the template. Do not leave placeholders or TBDs.
- List auto-instrumented boundaries in the auto table. List manual boundaries as individual sections with all required fields.
- Include the gaps and risks section — be honest about what the plan does not cover, what cardinality risks exist, and where signal choices are uncertain.
- Include SDK setup constraints derived from the plan. The SDK/setup phase needs these constraints to avoid enabling extra services, signals, exporters, propagators, or auto-instrumentation beyond the MVI.
- If the codebase is brownfield, note which boundaries are already instrumented and whether the existing instrumentation is sufficient or needs changes.
- Do not include framework-specific code snippets or language-specific wiring. The plan specifies observability decisions and SDK constraints. Language-specific mechanics are handled by setup skills.
Output Template
Produce the plan in the following structure:
# Instrumentation Plan: {service-name}
## Application Profile
- **Type**: {API service | Batch processor | Real-time system | Background jobs}
- **Existing instrumentation**: {None | Partial — summary}
- **Languages/frameworks**: {e.g., TypeScript with NestJS, Go with net/http}
- **Package manager**: {npm | bun | yarn | pnpm | go | maven | gradle}
## Business Workflows
| Workflow | User outcome | Entry point | Critical dependencies | Degraded states | Trace expectation |
|----------|--------------|-------------|-----------------------|-----------------|-------------------|
| {workflow.name} | {what the user/business expects} | {route/job/consumer} | {services/dependencies} | {fallbacks/retries/partial success} | {what should be understandable from the trace} |
## Target SLOs
| SLO | Target | SLI | Derivable from spans? |
|-----|--------|-----|----------------------|
| Availability | 99.9% success | 5xx error rate at HTTP boundary | Yes |
| Latency | P99 < 500ms | Request duration at HTTP boundary | Yes |
## Services In Scope
| Service | Include? | Role | Reason | SDK strategy | Signals |
|---------|----------|------|--------|--------------|---------|
| {service-name} | {yes/no/low-priority} | {business/control-plane/worker} | {why this service does or does not help production debugging} | {agent/library/manual/none} | {traces/metrics/logs/none} |
## Signal Scope
| Signal | Enabled? | Scope | Reason | Guardrails |
|--------|----------|-------|--------|------------|
| Traces | {yes/no} | {services/boundaries} | {debugging value} | {sampling and suppression constraints} |
| Metrics | {yes/no/standard-only/SLO-only} | {services/boundaries} | {SLI/dashboard value} | {low-cardinality dimensions only} |
| Logs | {yes/no} | {services/events} | {diagnostic value} | {redaction/export policy, or "do not configure logger provider"} |
| Baggage | {yes/no} | {allowlisted keys or "none"} | {why values must propagate} | {bounded, non-sensitive values only} |
## Auto-Instrumented Boundaries
These are handled by instrumentation libraries configured during SDK setup.
The implement phase MUST NOT write manual spans for these. If manual context is needed,
list the target as `auto + manual context` and specify the context separately.
| Service | Boundary | Decision | Library/agent | Covers | Tuning / suppressions | Sensitive data risks | Volume/noise risk |
|---------|----------|----------|---------------|--------|----------------------|----------------------|-------------------|
| {service} | {e.g., HTTP server requests} | {auto/auto + manual context/excluded} | {e.g., Java agent} | {method, route, status} | {health suppression, header/body capture disabled, etc.} | {headers, path IDs, payloads, etc.} | {low/medium/high plus rationale} |
## Manual Boundaries
Each boundary below requires hand-written span code.
### {boundary-name}
- **File**: `{path/to/file}`
- **Function**: `{methodName()}`
- **Strategy**: {manual | auto + manual context}
- **Create new span**: {yes/no}
- **Signal**: {Span | current-span attributes | Metric | Log/Event | Nothing}
- **Attributes**: `{attribute.name}` ({LOW|MEDIUM|HIGH}, PII risk: {none/low/medium/high}, reason: ...)
- **Events/logs**: {none, or exact event name and bounded fields}
- **On error**: {set status behavior, exception/error category behavior, handled-error behavior}
- **Do not record**: {raw IDs, raw messages, payloads, headers, etc.}
- **SLI coverage**: {which SLO this supports, or "Production debugging"}
### {next-boundary-name}
...
## PII And Sensitive Data Policy
| Source | Risk | Decision |
|--------|------|----------|
| {source, e.g., HTTP path params, headers, Kafka payloads, exception messages} | {PII/security/business/cardinality risk} | {remove/redact/hash/aggregate/route-template-only/do not capture} |
## Noise, Volume, And Cost Risks
| Source | Risk | Decision |
|--------|------|----------|
| {source, e.g., health checks, high-volume consumer, control-plane service} | {why it creates cost/noise} | {exclude/downsample/suppress/keep with rationale} |
## Propagation Policy
- **Trace context**: {required/not applicable, boundaries}
- **Baggage**: {disabled, or allowlisted keys with rationale}
- **Messaging propagation**: {required/not applicable, destination headers/properties at a conceptual level}
## SDK Setup Constraints
- **Services to configure**: {list}
- **Services not to configure or lower priority**: {list with rationale}
- **Allowed signals**: {traces/metrics/logs}
- **Disallowed signals**: {logs, baggage, etc. with rationale}
- **Sampling**: {parent-based ratio, collector tail sampling dependency, non-production always_on, or unresolved}
- **Required suppressions**: {health/readiness/metrics/debug/static/control-plane/noisy auto-instrumentations}
- **Sensitive capture restrictions**: {headers, bodies, payloads, raw SQL, raw exception messages}
- **Required env vars**: {endpoint, service version, environment, sampling ratio, etc.}
## Excluded Boundaries
- {boundary} — {one-line reason, e.g., "health check, no diagnostic value"}
## Gaps and Risks
- {Any SLI not covered by the plan}
- {Any cardinality risks flagged}
- {Any boundaries where signal choice is uncertain}
- {Any SDK setup constraint that is unresolved}
- {Any PII/noise/volume risk that implementation must preserve}
## Verification Checklist
- [ ] Business workflows above are understandable from the resulting traces.
- [ ] SDK setup configures only the services and signals allowed by this plan.
- [ ] Logs are not exported unless this plan includes a redaction/export policy.
- [ ] Baggage is not enabled unless this plan includes bounded allowlisted keys.
- [ ] Sampling follows the plan and does not use production `always_on` unless explicitly justified.
- [ ] Health/readiness/metrics/debug/static endpoints are suppressed, filtered, downsampled, or explicitly accepted.
- [ ] Manual spans do not duplicate auto-instrumented HTTP, DB, RPC, messaging, or cache spans.
- [ ] No PII, credentials, payloads, raw IDs, raw exception messages, or unbounded values are recorded.
- [ ] Custom attributes have stated cardinality and investigation value.
What NOT to include in the plan
- No language-specific code snippets or framework wiring. The plan specifies observability decisions and SDK constraints. How to wire those decisions in a language is the job of the setup and implementation skills.
- No recommended implementation order. Everything in the plan gets implemented.
- No phased rollout or tier labels. The plan is the MVI — the complete set of boundaries needed for production debugging. There are no "Phase 2" boundaries.
- No free-form "Details" prose per boundary. Use the structured fields above. If something doesn't fit in the fields, it probably doesn't belong in the plan.