[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EFP07K6DX7W4QT2X8GZ10F":3,"skill-same:01M6EFP07K6DX7W4QT2X8GZ10F":48},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":28,"repositoryHtmlUrl":29,"repositoryHomepageUrl":30,"ownerHtmlUrl":31,"ownerBlogUrl":6,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M6EFP07K6DX7W4QT2X8GZ10F","observability-setup",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Fobservability-setup\u002FSKILL.md",[],[18,19,20],"setup","observability","observability setup","2026-11-10 10:12:30",false,"# Observability Setup\n\n## Overview\n\nThis skill helps AI agents implement the three pillars of observability — traces, metrics, and structured logs — across microservice architectures. It uses OpenTelemetry as the instrumentation standard and Grafana's LGTM stack (Loki, Grafana, Tempo, Mimir) as the backend, though patterns apply to any OTel-compatible backend.\n\n## Instructions\n\n### OpenTelemetry Instrumentation (Node.js)\n\n1. Install packages:\n   ```bash\n   npm install @opentelemetry\u002Fsdk-node @opentelemetry\u002Fauto-instrumentations-node \\\n     @opentelemetry\u002Fexporter-trace-otlp-http @opentelemetry\u002Fexporter-metrics-otlp-http\n   ```\n\n2. Create `src\u002Finstrumentation.ts` — this file must be loaded BEFORE any other imports:\n   ```ts\n   import { NodeSDK } from '@opentelemetry\u002Fsdk-node';\n   import { getNodeAutoInstrumentations } from '@opentelemetry\u002Fauto-instrumentations-node';\n   import { OTLPTraceExporter } from '@opentelemetry\u002Fexporter-trace-otlp-http';\n   import { OTLPMetricExporter } from '@opentelemetry\u002Fexporter-metrics-otlp-http';\n   import { PeriodicExportingMetricReader } from '@opentelemetry\u002Fsdk-metrics';\n\n   const sdk = new NodeSDK({\n     serviceName: process.env.OTEL_SERVICE_NAME || 'my-service',\n     traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + '\u002Fv1\u002Ftraces' }),\n     metricReader: new PeriodicExportingMetricReader({\n       exporter: new OTLPMetricExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + '\u002Fv1\u002Fmetrics' }),\n       exportIntervalMillis: 15000,\n     }),\n     instrumentations: [getNodeAutoInstrumentations()],\n   });\n   sdk.start();\n   ```\n\n3. Add to start script: `node --require .\u002Fsrc\u002Finstrumentation.ts src\u002Findex.ts`\n\n4. Add custom spans for business-critical operations:\n   ```ts\n   import { trace } from '@opentelemetry\u002Fapi';\n   const tracer = trace.getTracer('payment-service');\n   \n   async function processPayment(order) {\n     return tracer.startActiveSpan('process-payment', async (span) => {\n       span.setAttribute('order.id', order.id);\n       span.setAttribute('payment.amount_cents', order.totalCents);\n       try {\n         const result = await paymentGateway.charge(order);\n         span.setAttribute('payment.status', result.status);\n         return result;\n       } catch (error) {\n         span.recordException(error);\n         span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n         throw error;\n       } finally {\n         span.end();\n       }\n     });\n   }\n   ```\n\n### OpenTelemetry Instrumentation (Python)\n\n1. Install: `pip install opentelemetry-distro opentelemetry-exporter-otlp`\n2. Run: `opentelemetry-instrument --service_name notification-service python app.py`\n3. For structured logs with trace context:\n   ```python\n   import structlog\n   from opentelemetry import trace\n\n   def add_trace_context(logger, method_name, event_dict):\n       span = trace.get_current_span()\n       ctx = span.get_span_context()\n       if ctx.is_valid:\n           event_dict['trace_id'] = format(ctx.trace_id, '032x')\n           event_dict['span_id'] = format(ctx.span_id, '016x')\n       return event_dict\n\n   structlog.configure(processors=[add_trace_context, structlog.dev.ConsoleRenderer()])\n   ```\n\n### Structured Logging with Trace Correlation\n\nEvery log line must include `trace_id` and `span_id`. Use JSON format:\n```json\n{\"level\":\"error\",\"msg\":\"payment failed\",\"trace_id\":\"abc123...\",\"span_id\":\"def456...\",\"service\":\"payment-service\",\"error\":\"timeout\",\"timestamp\":\"2025-01-15T10:30:00Z\"}\n```\n\nFor Node.js, use pino with a custom mixin:\n```js\nconst pino = require('pino');\nconst { trace } = require('@opentelemetry\u002Fapi');\n\nconst logger = pino({\n  mixin() {\n    const span = trace.getActiveSpan();\n    if (span) {\n      const ctx = span.spanContext();\n      return { trace_id: ctx.traceId, span_id: ctx.spanId };\n    }\n    return {};\n  }\n});\n```\n\n### OTel Collector Configuration\n\n```yaml\nreceivers:\n  otlp:\n    protocols:\n      grpc: { endpoint: \"0.0.0.0:4317\" }\n      http: { endpoint: \"0.0.0.0:4318\" }\n\nprocessors:\n  batch:\n    timeout: 5s\n    send_batch_size: 1000\n  memory_limiter:\n    limit_mib: 512\n\nexporters:\n  otlphttp\u002Ftempo:\n    endpoint: http:\u002F\u002Ftempo:4318\n  prometheusremotewrite:\n    endpoint: http:\u002F\u002Fmimir:9009\u002Fapi\u002Fv1\u002Fpush\n  loki:\n    endpoint: http:\u002F\u002Floki:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [memory_limiter, batch]\n      exporters: [otlphttp\u002Ftempo]\n    metrics:\n      receivers: [otlp]\n      processors: [memory_limiter, batch]\n      exporters: [prometheusremotewrite]\n    logs:\n      receivers: [otlp]\n      processors: [memory_limiter, batch]\n      exporters: [loki]\n```\n\n### Grafana Dashboard Provisioning\n\nCreate dashboards as JSON files in `grafana\u002Fdashboards\u002F`. Key panels:\n\n**Service Overview Dashboard:**\n- Request rate: `sum(rate(http_server_request_duration_seconds_count[5m])) by (service_name)`\n- P95 latency: `histogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket[5m])) by (le, service_name))`\n- Error rate: `sum(rate(http_server_request_duration_seconds_count{http_status_code=~\"5..\"}[5m])) \u002F sum(rate(http_server_request_duration_seconds_count[5m]))`\n\n**Alert Rules:**\n- Error rate > 5 % for 5 min → P2\n- P99 latency > 2s for 5 min → P3\n- Service health check failing for 1 min → P1\n\n### Signal Correlation in Grafana\n\n1. Loki data source: add derived field with regex `trace_id=(\\w+)` linking to Tempo.\n2. Tempo data source: enable \"Trace to logs\" linking to Loki with filter `{service_name=\"$service\"} | trace_id=\"$traceId\"`.\n3. Metrics → Logs: use exemplars in Mimir to link metric data points to trace IDs.\n\n## Examples\n\n### Example 1 — Quick OTel setup for Express app\n\n**Input:** \"Add observability to my Express API.\"\n\n**Output:** Create `src\u002Finstrumentation.ts` with the Node SDK config above, add the `--require` flag to the start script, set `OTEL_EXPORTER_OTLP_ENDPOINT=http:\u002F\u002Flocalhost:4318` and `OTEL_SERVICE_NAME=api-gateway` in `.env`.\n\n### Example 2 — Docker Compose for LGTM stack\n\n**Input:** \"Give me a docker-compose for the observability backend.\"\n\n**Output:**\n```yaml\nservices:\n  otel-collector:\n    image: otel\u002Fopentelemetry-collector-contrib:0.96.0\n    volumes: [\".\u002Fotel-collector-config.yaml:\u002Fetc\u002Fotelcol\u002Fconfig.yaml\"]\n    ports: [\"4317:4317\", \"4318:4318\"]\n  tempo:\n    image: grafana\u002Ftempo:2.4.0\n    command: [\"-config.file=\u002Fetc\u002Ftempo.yaml\"]\n    volumes: [\".\u002Ftempo.yaml:\u002Fetc\u002Ftempo.yaml\"]\n  loki:\n    image: grafana\u002Floki:3.0.0\n    ports: [\"3100:3100\"]\n  mimir:\n    image: grafana\u002Fmimir:2.11.0\n    command: [\"-config.file=\u002Fetc\u002Fmimir.yaml\"]\n    volumes: [\".\u002Fmimir.yaml:\u002Fetc\u002Fmimir.yaml\"]\n  grafana:\n    image: grafana\u002Fgrafana:10.4.0\n    ports: [\"3000:3000\"]\n    volumes: [\".\u002Fgrafana\u002Fprovisioning:\u002Fetc\u002Fgrafana\u002Fprovisioning\", \".\u002Fgrafana\u002Fdashboards:\u002Fvar\u002Flib\u002Fgrafana\u002Fdashboards\"]\n    environment:\n      GF_AUTH_ANONYMOUS_ENABLED: \"true\"\n      GF_AUTH_ANONYMOUS_ORG_ROLE: Admin\n```\n\n## Guidelines\n\n- **Instrument before you optimize.** You cannot improve what you cannot see.\n- **Auto-instrumentation first, custom spans second.** Auto-instrumentation covers 80 % of what you need. Add custom spans only for business-critical paths.\n- **Always correlate signals.** A trace without logs is incomplete. A metric spike without a trace is unactionable.\n- **Set resource limits on the collector.** Without `memory_limiter`, a traffic spike can OOM the collector and create a cascading failure.\n- **Use service.name consistently.** It is the primary grouping key across all three signal types. Mismatched names break correlation.\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FTerminalSkills\u002Fskills\u002Fobservability-setup",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill observability-setup",{"tool":40,"command":41},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill observability-setup",{"tool":43,"command":44},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill observability-setup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FTerminalSkills\u002Fskills\u002Fobservability-setup\u002FSKILL.md",true,[],[49,63,73,82,88,95],{"skillNo":50,"name":51,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":52,"categories":53,"keywords":59,"publishedAt":21},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[54],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[60,61,62],"model","model hierarchy","model hierarchy skill",{"skillNo":64,"name":65,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":69,"publishedAt":21},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[68],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[70,71,72],"MCP","zoom","zoom mcp",{"skillNo":74,"name":75,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":21},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[79,80,81,71],"sdk","zoom apps sdk","apps",{"skillNo":83,"name":84,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":21},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[84],{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":21},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[93],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[90],{"skillNo":96,"name":97,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":100,"publishedAt":21},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[97]]