> Structured logging, metrics, distributed tracing, and alerting strategy. The three pillars of observability, assessed and designed for production readiness.
Installs into .claude/skills of the current project.
Are you the author of Harness Observability?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/intense-visions-harness-observability)
# Harness Observability
> Structured logging, metrics, distributed tracing, and alerting strategy. The three pillars of observability, assessed and designed for production readiness.
## When to Use
- When designing or reviewing observability instrumentation for a service
- When auditing logging, metrics, or tracing coverage gaps
- When defining SLIs, SLOs, and alerting strategies for a new feature
- NOT for application performance benchmarking (use harness-perf)
- NOT for security-focused log analysis (use harness-security-review)
- NOT for incident response procedures (use harness-incident-response)
> See also: `harness-pulse` is the read-side companion to this skill. Pulse READS observability data (and external analytics/error signals) to produce daily product-pulse reports; this skill DESIGNS the instrumentation that produces those signals in the first place.
## Process
### Phase 1: DETECT -- Identify Existing Instrumentation
1. **Scan for observability libraries.** Check package manifests for instrumentation dependencies:
- **Logging:** winston, pino, bunyan, log4js, structlog, serilog, zap, logrus
- **Metrics:** prom-client, micrometer, statsd-client, @opentelemetry/sdk-metrics
- **Tracing:** @opentelemetry/sdk-trace-node, dd-trace, jaeger-client, zipkin
- **Platforms:** @datadog/browser-rum, newrelic, @sentry/node, elastic-apm-node
2. **Locate instrumentation code.** Search for logger, metrics, and tracing initialization:
- Logger configuration files (log levels, formatters, transports)
- Metrics registry and custom metric definitions
- Tracer initialization and span creation patterns
- Middleware for automatic HTTP instrumentation
3. **Detect collector and exporter configuration.** Look for:
- OpenTelemetry Collector config (`otel-collector-config.yaml`)
- Prometheus scrape configuration (`prometheus.yml`)
- Grafana dashboards (`grafana/dashboards/`)
- Datadog agent configuration (`datadog.yaml`)
- Fluentd or Fluent Bit configuration for log forwarding
4. **Identify alerting configuration.** Search for:
- Prometheus alerting rules (`alert.rules.yml`)
- Grafana alert definitions
- PagerDuty, Opsgenie, or Slack integration configs
- SLO definitions in monitoring-as-code format
5. **Present detection summary:**
```
Observability Detection:
Logging: pino (structured JSON) -- 12 logger instances found
Metrics: prom-client -- 8 custom metrics defined
Tracing: @opentelemetry/sdk-trace-node -- initialized in src/tracing.ts
Collector: OpenTelemetry Collector -> Grafana Cloud
Alerting: 3 Prometheus alert rules, Slack integration
```
---
### Phase 2: AUDIT -- Evaluate Coverage and Quality
1. **Audit logging quality.** Evaluate each logger usage for:
- **Structured format:** JSON output with consistent field names, not string concatenation
- **Log levels:** Appropriate use of error, warn, info, debug (not everything at info)
- **Context fields:** Request ID, user ID, operation name included in log entries
- **Correlation IDs:** Trace ID propagated through log entries for cross-service correlation
- **Sensitive data:** No PII, credentials, or tokens logged
- **Error logging:** Stack traces included for errors, not just error messages
2. **Audit metrics coverage.** Check for standard metrics:
- **RED metrics:** Request rate, error rate, duration for every HTTP endpoint
- **USE metrics:** Utilization, saturation, errors for system resources
- **Custom business metrics:** Domain-specific counters and gauges
- **Histogram buckets:** Appropriate bucket boundaries for latency histograms
- **Label cardinality:** No high-cardinality labels (user ID, request ID as metric labels)
3. **Audit tracing implementation.** Verify:
- **Span creation:** Entry points create root spans, downstream calls create child spans
- **Context propagation:** Trace context flows across HTTP boundaries (W3C or B3 headers)
- **Span attributes:** Meaningful attributes set on spans (not empty spans)
- **Error recording:** Exceptions recorded on spans with status codes
- **Sampling strategy:** Configured and appropriate (not sampling 100% in production)
4. **Audit alerting effectiveness.** Check each alert for:
- **Actionability:** Does the alert tell the on-call engineer what to do?
- **Signal quality:** Based on symptoms (error rate) not causes (CPU usage)
- **Thresholds:** Based on SLO burn rate, not arbitrary values
- **Runbook link:** Each alert has a link to a runbook or troubleshooting guide
- **Routing:** Alerts route to the correct team and escalation path
5. **Score each pillar and identify gaps:**
```
Observability Audit:
Logging: 7/10 -- structured, but missing correlation IDs in 4 services
Metrics: 5/10 -- RED metrics partial, no business metrics
Tracing: 8/10 -- good coverage, sampling needs tuning
Alerting: 3/10 -- only 3 rules, no SLO-based alerts, no runbooks
```
---
### Phase 3: DESIGN -- Recommend Instrumentation Strategy
1. **Design logging strategy.** Recommend:
- Standard log format with required fields (timestamp, level, service, traceId, message)
- Logger factory that injects correlation context automatically
- Log level configuration per environment (debug in dev, info in production)
- Log aggregation pipeline (application -> collector -> storage -> query)
- Retention policy per environment
2. **Design metrics strategy.** Recommend:
- Standard RED metrics for every HTTP endpoint using middleware
- Custom business metrics aligned with product KPIs
- Histogram bucket configuration based on expected latency distribution
- Metric naming convention following Prometheus best practices (`http_request_duration_seconds`)
- Dashboard templates for standard service views
3. **Design tracing strategy.** Recommend:
- Automatic instrumentation for HTTP, database, and message queue operations
- Manual span creation for business-critical code paths
- Sampling strategy (head-based for development, tail-based for production)
- Baggage propagation for cross-cutting concerns (tenant ID, feature flags)
- Trace-to-log correlation via trace ID injection
4. **Define SLIs and SLOs.** For each service endpoint:
- **Availability SLI:** Proportion of successful requests (non-5xx)
- **Latency SLI:** Proportion of requests faster than threshold (p99 < 500ms)
- **SLO target:** 99.9% availability, 99% latency within budget
- **Error budget:** Calculation and burn rate alerting
- **SLO dashboard:** Visual budget remaining over rolling window
5. **Design alerting strategy.** Recommend:
- Multi-window, multi-burn-rate alerts based on SLOs
- Page-worthy alerts (high burn rate) vs. ticket-worthy alerts (slow burn)
- Alert grouping to reduce noise (one alert per service, not per instance)
- Escalation paths with timeouts
- Regular alert review cadence (monthly) to retire noisy alerts
---
### Phase 4: VALIDATE -- Verify Instrumentation Correctness
1. **Validate log output.** Check that logs from a test run:
- Parse as valid JSON (if structured logging is configured)
- Contain required fields (timestamp, level, service name)
- Include trace IDs when tracing is active
- Do not contain sensitive data (grep for common patterns: password, token, secret)
- Use appropriate log levels (no error-level logs for normal operations)
2. **Validate metric exposition.** Verify:
- Metrics endpoint (`/metrics`) is accessible and returns Prometheus format
- All declared custom metrics appear in the output
- Histogram buckets are populated with reasonable values
- No high-cardinality label combinations (check label count)
- Counter values increment correctly (not resetting unexpectedly)
3. **Validate trace propagation.** Verify end-to-end:
- A request to the entry service generates a trace visible in the tracing backend
- Child spans appear for downstream service calls
- Span attributes are populated with expected values
- Error spans have appropriate status and exception recording
- Sampling is working as configured (not dropping all traces or keeping all)
4. **Validate alerting rules.** Check:
- Alert expressions are syntactically valid (PromQL, LogQL)
- Alert thresholds fire on historical data that represents real incidents
- Alert routing sends to the correct channel or pager
- Runbook links resolve to actual documentation
5. **Generate observability report:**
```
Observability Validation: [PASS/WARN/FAIL]
Logging: PASS (structured, correlated, no PII detected)
Metrics: WARN (RED metrics present, missing business metrics)
Tracing: PASS (propagation verified, sampling at 10%)
Alerting: FAIL (no SLO-based alerts, 2 of 3 rules missing runbooks)
Priority actions:
1. Define SLOs for /api/orders and /api/payments endpoints
2. Add multi-burn-rate alerts based on SLO error budget
3. Write runbooks for existing alerts
4. Add order_total and payment_success_rate business metrics
```
---
## Harness Integration
- **`harness skill run harness-observability`** -- Primary invocation for observability audit.
- **`harness validate`** -- Run after instrumentation changes to verify project health.
- **`harness check-deps`** -- Verify observability library dependencies are installed.
- **`emit_interaction`** -- Present audit results and SLO design recommendations.
## Success Criteria
- All three observability pillars (logging, metrics, tracing) are assessed
- Coverage gaps are identified with specific remediation recommendations
- SLI/SLO definitions are provided for critical service endpoints
- Alerting strategy is evaluated for actionability and signal quality
- No sensitive data detected in log output
- Correlation between logs and traces is verified
## Examples
### Example: Express.js API with OpenTelemetry
```
Phase 1: DETECT
Logging: pino with pino-http middleware
Metrics: @opentelemetry/sdk-metrics -> Prometheus
Tracing: @opentelemetry/sdk-trace-node -> Jaeger
Collector: OTel Collector (otel-collector-config.yaml)
Alerting: 5 Prometheus rules in monitoring/alerts.yml
Phase 2: AUDIT
Logging: 8/10 -- structured JSON, trace IDs present, missing request body size
Metrics: 6/10 -- http_request_duration_seconds present, missing queue depth
and business metrics (orders_created_total)
Tracing: 9/10 -- auto-instrumented HTTP + pg + Redis, manual spans on
checkout flow
Alerting: 4/10 -- static thresholds, no SLO burn rate, 2 missing runbooks
Phase 3: DESIGN
SLOs recommended:
- POST /api/orders: 99.9% availability, p99 < 800ms
- GET /api/products: 99.95% availability, p99 < 200ms
Alerting: Replace static "error rate > 5%" with multi-window burn rate
Metrics: Add orders_created_total, cart_abandonment_rate gauges
Logging: Add request/response body size for capacity planning
Phase 4: VALIDATE
Log output: PASS (valid JSON, no PII)
Metrics endpoint: PASS (all custom metrics present)
Trace propagation: PASS (end-to-end verified)
Alert rules: WARN (valid PromQL, but thresholds not SLO-based)
Result: WARN -- alerting strategy needs SLO alignment
```
### Example: Go Microservices with Datadog
```
Phase 1: DETECT
Logging: zap (structured) across 4 services
Metrics: Datadog dogstatsd client
Tracing: dd-trace-go with automatic HTTP/gRPC instrumentation
Collector: Datadog Agent (datadog.yaml in k8s/)
Alerting: 12 monitors in Datadog (Terraform-managed)
Phase 2: AUDIT
Logging: 9/10 -- consistent structured format, correlation IDs, no PII
Metrics: 7/10 -- RED metrics present, custom counters for business events,
but missing histogram for gRPC call duration
Tracing: 8/10 -- HTTP and gRPC instrumented, database spans present,
Redis spans missing
Alerting: 6/10 -- good coverage but static thresholds, no error budgets
Phase 3: DESIGN
1. Add dd-trace-go Redis integration for complete trace picture
2. Add grpc_server_handling_seconds histogram
3. Define SLOs in Datadog for top 5 endpoints
4. Convert 4 highest-priority monitors to SLO burn rate alerts
5. Add Datadog SLO dashboard for team visibility
Phase 4: VALIDATE
Log output: PASS
Metrics: WARN (missing gRPC histogram)
Traces: WARN (Redis spans missing)
Alerts: WARN (no SLO-based alerts)
Result: WARN -- 3 instrumentation gaps, alerting needs SLO alignment
```
## Rationalizations to Reject
| Rationalization | Reality |
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| "We can see what's happening in CloudWatch logs — we don't need structured logging" | Unstructured log lines cannot be queried, aggregated, or correlated across services. When an incident spans three services, searching for a request ID across unstructured logs is manual forensics. Structured logging is not a nicety — it is the foundation for incident response. |
| "We'll add alerting once we've seen a few incidents and know what to alert on" | The first incident is the worst time to define alerting. SLO-based burn rate alerts can be defined from traffic patterns before any incidents occur. Waiting for incidents to define thresholds means every early failure goes undetected. |
| "User ID is a useful label for the latency metric — it helps us debug per-user issues" | User ID as a metric label creates one time series per user, which at 100,000 users means 100,000 label combinations. High-cardinality labels exhaust metric storage, cause query timeouts, and make the entire metrics system unstable. Use logs for per-user debugging; use metrics for aggregate signals. |
| "The tracing library is initialized, so we have distributed tracing" | Initializing the library creates root spans but does not propagate context across HTTP boundaries, instrument database calls, or connect traces to logs. Trace initialization without verified end-to-end propagation produces disconnected, useless traces. |
| "We have alerts — they're just not linked to runbooks yet" | An alert that fires at 3am without a runbook link requires the on-call engineer to start debugging from scratch. The absence of a runbook is not a documentation gap; it is a mean-time-to-recover multiplier. |
## Gates
- **No sensitive data in logs.** If PII, credentials, or tokens are detected in log output, it is a blocking finding. The logging configuration must sanitize or redact sensitive fields before any other improvements are made.
- **No high-cardinality metric labels.** Metric labels with unbounded values (user IDs, request IDs, timestamps) cause storage explosion and query timeouts. This is a blocking finding.
- **No alerting without runbooks.** Production alerts that lack a runbook link are incomplete. Every page-worthy alert must have actionable documentation.
- **No tracing without context propagation.** Traces that do not propagate context across service boundaries provide incomplete pictures and mislead investigations. Broken propagation is a blocking finding.
## Escalation
- **When observability libraries conflict:** Some combinations (e.g., dd-trace and OTel auto-instrumentation) cause duplicate spans or metric conflicts. Recommend choosing one provider and removing the other. If migration is needed, present a phased approach.
- **When sampling drops critical traces:** If tail-based sampling is misconfigured, critical error traces may be dropped. Recommend always-sample rules for error responses and critical code paths, with probabilistic sampling for normal traffic.
- **When metric cardinality is already out of control:** Do not attempt to fix retroactively in the metrics backend. Recommend adding new metrics with correct labels and deprecating the high-cardinality ones. Set a timeline for removal.
- **When no observability infrastructure exists:** This is a design-from-scratch scenario. Start with structured logging (lowest barrier), then add metrics (RED for HTTP endpoints), then tracing. Do not attempt all three at once.