Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Observability and SRE expert. Use when setting up monitoring, logging, tracing, defining SLOs, or managing incidents. Covers Prometheus, Grafana, OpenTelemetry, and incident response best practices.
.claude/skills/majiayu000-observability-sre/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 139% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 145% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 99% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 89% | 0% |
> These rules are mandatory. Violating them means the skill is not working correctly.
Alert on user-facing symptoms, not internal infrastructure metrics.
yaml# ❌ FORBIDDEN: Alerting on internal metrics - alert: CPUHigh expr: cpu_usage > 70% # Users don't care about CPU, they care about latency - alert: MemoryHigh expr: memory_usage > 80% # Internal metric, may not affect users # ✅ REQUIRED: Alert on user experience - alert: APILatencyHigh expr: slo:api_latency:p95 > 0.200 annotations: summary: "Users experiencing slow response times" - alert: ErrorRateHigh expr: slo:api_errors:rate5m > 0.001 annotations: summary: "Users encountering errors"
Loki/Prometheus labels must have low cardinality (<10 unique labels).
yaml# ❌ FORBIDDEN: High cardinality labels labels: user_id: "usr_123" # Millions of values! order_id: "ord_456" # Millions of values! request_id: "req_789" # Every request is unique! # ✅ REQUIRED: Low cardinality only labels: namespace: "production" # Few values app: "api-server" # Few values level: "error" # 5-6 values method: "GET" # ~10 values # High cardinality data goes in log body: logger.info({ user_id: "usr_123", # In JSON body, not label order_id: "ord_456", }, "Order processed");
Every service must have defined SLOs with error budget tracking.
yaml# ❌ FORBIDDEN: No SLO definition # Just monitoring without targets # ✅ REQUIRED: Explicit SLO with budget # SLO: 99.9% availability # Error Budget: 0.1% = 43.2 minutes/month downtime groups: - name: slo_tracking rules: - record: slo:api_availability:ratio expr: sum(rate(http_requests_total{status!~"5.."}[5m])) / sum(rate(http_requests_total[5m])) - alert: ErrorBudgetBurnRate expr: slo:api_availability:ratio < 0.999 for: 5m annotations: summary: "Burning error budget too fast"
All logs must include trace_id for correlation with distributed traces.
typescript// ❌ FORBIDDEN: Logs without trace context logger.info("Payment processed"); // ✅ REQUIRED: Include trace_id in every log const span = trace.getActiveSpan(); logger.info({ trace_id: span?.spanContext().traceId, span_id: span?.spanContext().spanId, order_id: "ord_123", }, "Payment processed"); // Output includes correlation: // {"trace_id":"abc123","span_id":"def456","order_id":"ord_123","msg":"Payment processed"}
| Scenario | Tool/Pattern | Reason | |----------|--------------|--------| | Metrics collection | Prometheus + Grafana | Industry standard, powerful query language | | Distributed tracing | OpenTelemetry + Tempo/Jaeger | Vendor-neutral, CNCF standard | | Log aggregation (cost-sensitive) | Grafana Loki | Indexes only labels, 10x cheaper | | Log aggregation (search-heavy) | ELK Stack | Full-text search, advanced analytics | | Unified observability | Elastic/Datadog/Dynatrace | Single pane of glass for all telemetry | | Incident management | PagerDuty/Opsgenie | Alert routing, on-call scheduling | | Chaos engineering | Gremlin/Chaos Mesh | Controlled failure injection | | AIOps/Anomaly detection | Dynatrace/Datadog | AI-driven root cause analysis |
| Pillar | What | When | Tools | |--------|------|------|-------| | Metrics | Numerical time-series data | Real-time monitoring, alerting | Prometheus, StatsD, CloudWatch | | Logs | Event records with context | Debugging, audit trails | Loki, ELK, Splunk | | Traces | Request journey across services | Performance analysis, dependencies | OpenTelemetry, Jaeger, Zipkin |
Fourth Pillar (Emerging): Continuous Profiling — Code-level performance data (CPU, memory usage at function level)
yaml# 2025 Best Practice: Federated architecture # Prevents metric chaos while enabling drill-down # Layer 1: Application Prometheus # - Detailed business logic metrics # - High cardinality acceptable # - Short retention (7 days) # Layer 2: Cluster Prometheus # - Per-environment/cluster metrics # - Medium retention (30 days) # - Aggregates from application level # Layer 3: Global Prometheus # - Cross-cluster critical metrics # - Long retention (1 year) # - Federation from cluster level # Global Prometheus config scrape_configs: - job_name: 'federate' scrape_interval: 15s honor_labels: true metrics_path: '/federate' params: 'match[]': - '{job="kubernetes-nodes"}' - '{__name__=~"job:.*"}' # Recording rules only static_configs: - targets: - 'cluster-prom-us-east.internal:9090' - 'cluster-prom-eu-west.internal:9090'
yaml# Precompute expensive queries groups: - name: api_performance interval: 30s rules: # Request rate (requests per second) - record: job:api_requests:rate5m expr: sum(rate(http_requests_total[5m])) by (job, method, status) # Error rate - record: job:api_errors:rate5m expr: | sum(rate(http_requests_total{status=~"5.."}[5m])) by (job) / sum(rate(http_requests_total[5m])) by (job) # P95 latency - record: job:api_latency:p95 expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))
yaml# Increase scrape interval for high-target deployments scrape_interval: 30s # Default: 15s reduces load by 50% # Use relabeling to drop unnecessary metrics metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*|process_.*' # Drop Go runtime metrics action: drop # Limit sample retention storage: tsdb: retention.time: 15d # Keep only 15 days locally retention.size: 50GB # Or max 50GB
typescript// Node.js auto-instrumentation 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({ traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4318/v1/traces', }), instrumentations: [ getNodeAutoInstrumentations({ // Auto-instruments HTTP, Express, PostgreSQL, Redis, etc. '@opentelemetry/instrumentation-fs': { enabled: false }, // Too noisy }), ], }); sdk.start();
typescriptimport { trace, SpanStatusCode } from '@opentelemetry/api'; const tracer = trace.getTracer('payment-service', '1.0.0'); async function processPayment(orderId: string, amount: number) { // Create custom span for business operation return tracer.startActiveSpan('processPayment', async (span) => { try { // Add business context span.setAttributes({ 'order.id': orderId, 'payment.amount': amount, 'payment.currency': 'USD', }); // Child span for external API call const paymentResult = await tracer.startActiveSpan('stripe.charge', async (childSpan) => { const result = await stripe.charges.create({ amount, currency: 'usd' }); childSpan.setAttribute('stripe.charge_id', result.id); childSpan.setStatus({ code: SpanStatusCode.OK }); childSpan.end(); return result; }); span.setStatus({ code: SpanStatusCode.OK }); return paymentResult; } catch (error) { span.recordException(error); span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }); throw error; } finally { span.end(); } }); }
yaml# OpenTelemetry Collector config processors: # Probabilistic sampling: Keep 10% of traces probabilistic_sampler: sampling_percentage: 10 # Tail sampling: Make decisions after seeing full trace tail_sampling: policies: # Always sample errors - name: error-traces type: status_code status_code: {status_codes: [ERROR]} # Always sample slow requests - name: slow-traces type: latency latency: {threshold_ms: 1000} # Sample 5% of normal traffic - name: normal-traces type: probabilistic probabilistic: {sampling_percentage: 5}
typescript// Ensure trace context flows across services import { propagation, context } from '@opentelemetry/api'; // Outgoing HTTP request (automatic with auto-instrumentation) fetch('https://api.example.com/data', { headers: { // W3C Trace Context headers injected automatically: // traceparent: 00-<trace-id>-<span-id>-01 // tracestate: vendor=value }, }); // Manual propagation for non-HTTP (e.g., message queues) const carrier = {}; propagation.inject(context.active(), carrier); await publishMessage(queue, { data: payload, headers: carrier });
typescript// Use structured logging library import pino from 'pino'; const logger = pino({ level: process.env.LOG_LEVEL || 'info', formatters: { level: (label) => ({ level: label }), }, timestamp: pino.stdTimeFunctions.isoTime, // Include trace context in logs mixin() { const span = trace.getActiveSpan(); if (!span) return {}; const { traceId, spanId } = span.spanContext(); return { trace_id: traceId, span_id: spanId, }; }, }); // Structured logging with context logger.info( { user_id: '123', order_id: 'ord_456', amount: 99.99, payment_method: 'card', }, 'Payment processed successfully' ); // Output: // {"level":"info","time":"2025-01-15T10:30:00.000Z","trace_id":"abc123","span_id":"def456","user_id":"123","order_id":"ord_456","amount":99.99,"payment_method":"card","msg":"Payment processed successfully"}
typescript// Follow standard severity levels logger.trace({ details }, 'Low-level debugging'); // Very verbose logger.debug({ state }, 'Debug information'); // Development logger.info({ event }, 'Normal operation'); // Production default logger.warn({ issue }, 'Warning condition'); // Potential issues logger.error({ error, context }, 'Error occurred'); // Errors logger.fatal({ critical }, 'Fatal error'); // Process crash
yaml# Promtail config - ships logs to Loki server: http_listen_port: 9080 positions: filename: /tmp/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: kubernetes kubernetes_sd_configs: - role: pod relabel_configs: # Add pod labels as Loki labels (LOW cardinality only!) - source_labels: [__meta_kubernetes_namespace] target_label: namespace - source_labels: [__meta_kubernetes_pod_name] target_label: pod - source_labels: [__meta_kubernetes_pod_label_app] target_label: app pipeline_stages: # Parse JSON logs - json: expressions: level: level trace_id: trace_id # Extract fields as labels - labels: level: trace_id:
{app="api"} | json | user_id="123"promql# LogQL query examples {namespace="production", app="api"} |= "error" # Text search {app="api"} | json | level="error" | line_format "{{.msg}}" # JSON parsing rate({app="api"}[5m]) # Log rate per second sum by (level) (count_over_time({namespace="production"}[1h])) # Count by level
Detailed material starting at ## SLO/SLI/SLA Management has been moved to reference/extended.md to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 13,822 | 17,843 | +29% | 1 | 1 | 0% | 2,641 | 5,991 | +127% | 0 | 0 | — |
case-02 | fail→pass | 13,041 | 12,273 | -6% | 1 | 1 | 0% | 2,555 | 6,100 | +139% | 0 | 0 | — |
case-03 | fail→pass | 13,180 | 11,630 | -12% | 1 | 1 | 0% | 2,452 | 6,003 | +145% | 0 | 0 | — |
case-04 | pass→pass | 13,829 | 6,701 | -52% | 1 | 1 | 0% | 2,467 | 4,910 | +99% | 0 | 0 | — |
case-05 | pass→pass | 15,578 | 8,590 | -45% | 1 | 1 | 0% | 2,736 | 5,178 | +89% | 0 | 0 | — |
case-06 | pass→pass | 9,303 | 7,724 | -17% | 1 | 1 | 0% | 1,667 | 5,128 | +208% | 0 | 0 | — |
case-07 | pass→pass | 6,775 | 4,092 | -40% | 1 | 1 | 0% | 1,245 | 4,356 | +250% | 0 | 0 | — |
case-08 | pass→pass | 10,512 | 10,608 | +1% | 1 | 1 | 0% | 2,224 | 5,884 | +165% | 0 | 0 | — |
case-09 | pass→pass | 16,164 | 6,020 | -63% | 1 | 1 | 0% | 3,042 | 4,805 | +58% | 0 | 0 | — |
case-10 | pass→pass | 15,158 | 13,765 | -9% | 1 | 1 | 0% | 3,010 | 6,383 | +112% | 0 | 0 | — |
case-11 | pass→pass | 10,412 | 6,710 | -36% | 1 | 1 | 0% | 1,841 | 4,917 | +167% | 0 | 0 | — |
case-12 | fail→fail | 15,470 | 13,336 | -14% | 1 | 1 | 0% | 3,140 | 6,426 | +105% | 0 | 0 | — |
case-13 | pass→pass | 13,633 | 10,584 | -22% | 1 | 1 | 0% | 2,299 | 5,557 | +142% | 0 | 0 | — |
case-14 | pass→pass | 9,674 | 10,371 | +7% | 1 | 1 | 0% | 1,825 | 5,694 | +212% | 0 | 0 | — |
case-15 | pass→pass | 14,211 | 11,954 | -16% | 1 | 1 | 0% | 2,520 | 5,792 | +130% | 0 | 0 | — |
case-16 | pass→pass | 9,926 | 9,335 | -6% | 1 | 1 | 0% | 1,805 | 5,597 | +210% | 0 | 0 | — |
case-17 | pass→pass | 4,776 | 6,232 | +30% | 1 | 1 | 0% | 736 | 4,616 | +527% | 0 | 0 | — |
case-18 | pass→pass | 9,650 | 6,740 | -30% | 1 | 1 | 0% | 1,691 | 4,885 | +189% | 0 | 0 | — |
case-19 | pass→pass | 9,739 | 7,367 | -24% | 1 | 1 | 0% | 1,968 | 5,131 | +161% | 0 | 0 | — |
case-20 | pass→pass | 6,279 | 8,509 | +36% | 1 | 1 | 0% | 1,350 | 5,439 | +303% | 0 | 0 | — |
case-21 | pass→pass | 7,485 | 6,895 | -8% | 1 | 1 | 0% | 1,575 | 5,179 | +229% | 0 | 0 | — |
case-22 | pass→pass | 7,694 | 6,591 | -14% | 1 | 1 | 0% | 1,480 | 5,013 | +239% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.