Przejdź do głównej zawartości

Monitorowanie i obserwowalność z agentami AI polegają na tym, że Claude Code, Codex lub Cursor instrumentują serwis przez OpenTelemetry, generują konfigurację kolektora, dashboardów i alertów, a każdy element jest sprawdzany maszynowo — testami reguł promtool, walidatorami konfiguracji i zapytaniami na żywo przez serwery MCP Sentry i Grafany — więc nikt nie musi czytać wygenerowanego YAML-a linijka po linijce.

Serwis checkout sporadycznie zwraca 504. Ślady urywają się na spanie bramki, dashboard jest zielony, a alert, który powinien był zadziałać, odwołuje się do metryki, której instrumentacja przestała emitować dwa wydania temu. Agent postawi cały stos w jedno popołudnie; ta strona mówi, jak się upewnić, że to, co postawił, jest prawdą.

Strona jest dla developera, który odpowiada za serwis, i tech leada, który zatwierdza alerting. Dotyczy obserwowania twojego serwisu. Obserwowanie samych agentów — ich śladów, wywołań narzędzi, kosztów i logów audytowych — opisuje strona obserwowalność agentów.

  • Bootstrap OpenTelemetry dla serwisu Node.js na aktualnych pakietach, z nazwami metryk, które te pakiety faktycznie emitują.
  • Reguły alertów z testami jednostkowymi promtool, dzięki czemu pytanie „czy alert odpali przy 10% błędów i zamilknie przy 1%?” rozstrzyga CI, a nie opinia w code review.
  • Polecenia walidujące każdą wygenerowaną konfigurację (Collector, Loki, Alertmanager) i prompt, który sprawdza, czy każdy panel dashboardu zwraca dane.
  • Dostęp tylko do odczytu do Sentry i Grafany przez MCP, żeby agent czytał prawdziwe nazwy metryk i błędy zamiast je zgadywać.
  • Plan naprawczy dla pięciu typowych sposobów, w jakie psuje się obserwowalność zbudowana przez agenta.

W jakiej kolejności budować monitoring serwisu z agentem?

Dział zatytułowany „W jakiej kolejności budować monitoring serwisu z agentem?”

Kolejność nie jest przypadkowa: każda warstwa jest tyle warta, co warstwa pod nią. Alert na nieistniejącej metryce nigdy nie odpali i nic nie zgłosi, że nie odpala.

KrokCo generuje agentCo dowodzi poprawności bez czytania kodu
1. InstrumentacjaBootstrap SDK OpenTelemetryTrace ID żądania znaleziony w Tempo; lista nazw metryk z Prometheusa
2. PotokKonfiguracja Collectora, Prometheusa, Loki, Tempo i Grafanyotelcol validate, loki -verify-config=true, promtool check config
3. AlertyReguły Prometheusa i routing Alertmanagerapromtool test rules, amtool check-config
4. DashboardyJSON dashboardu GrafanyZapytanie każdego panelu zwraca dane (sprawdzone przez Grafana MCP)
5. Dostęp na żywoSerwery MCP Sentry i GrafanyTokeny tylko do odczytu; agent odpowiada na jedno pytanie incydentowe od zapytania do wniosku

Nie pozwól agentowi przeskoczyć od razu do dashboardów. Dashboard zbudowany, zanim krok 1 został udowodniony, stoi na zgadniętych nazwach metryk.

Prompt jest ten sam we wszystkich trzech narzędziach; różni się miejsce uruchomienia i sposób zatwierdzania zmian. Prompt przypina aktualne API, bo modele uczone na starszych tutorialach sięgają po usuniętą klasę Resource i po nazwy metryk, których już nie ma.

Uruchom go z katalogu głównego repozytorium. Interaktywnie: włącz claude i wklej prompt. Do przebiegu skryptowego zapisz prompt w pliku i użyj trybu print. acceptEdits zatwierdza tylko edycje plików i odrzuca polecenia powłoki niedozwolone przez twoje reguły uprawnień, więc zezwól na trzy polecenia, których prompt potrzebuje do instalacji pakietów, uruchomienia usługi i wywołania GET /health (flagi sprawdzone w Claude Code 2.1.283):

Okno terminala
claude -p "$(cat prompts/otel-bootstrap.md)" --permission-mode acceptEdits \
--allowedTools "Bash(npm *)" "Bash(node *)" "Bash(curl *)" --output-format json

Wynik JSON zawiera ID sesji i końcową odpowiedź, które możesz zarchiwizować razem z pull requestem.

Wynik powinien wyglądać mniej więcej tak. Wersje pakietów aktualne na 2026-09-26: @opentelemetry/sdk-node 0.222.0, @opentelemetry/resources 2.11.0, @opentelemetry/semantic-conventions 1.43.0.

// src/instrumentation.ts — compiled, then loaded with: node --require ./dist/instrumentation.js dist/server.js
// (ESM service: node --experimental-loader=@opentelemetry/instrumentation/hook.mjs --import ./dist/instrumentation.js dist/server.js)
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { resourceFromAttributes } from '@opentelemetry/resources';
import {
ATTR_DEPLOYMENT_ENVIRONMENT_NAME,
ATTR_SERVICE_NAME,
ATTR_SERVICE_VERSION,
} from '@opentelemetry/semantic-conventions';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: process.env.SERVICE_NAME ?? 'checkout',
[ATTR_SERVICE_VERSION]: process.env.SERVICE_VERSION ?? '0.0.0',
[ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: process.env.NODE_ENV ?? 'development',
}),
traceExporter: new OTLPTraceExporter(),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter(),
exportIntervalMillis: 30_000,
}),
instrumentations: [
getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-fs': { enabled: false } }),
],
});
sdk.start();
process.on('SIGTERM', () => sdk.shutdown().finally(() => process.exit(0)));

Eksportery same czytają OTEL_EXPORTER_OTLP_ENDPOINT ze środowiska. Zwykłe node --require ./src/instrumentation.ts nie zadziała na pliku .ts — dlatego prompt prosi o skompilowany plik. Serwis ESM ("type": "module") wymaga więcej: --require nie przechwytuje importów modułów ES, więc Express i http mogą działać, nie tworząc ani jednego spanu. Załaduj plik przez node --import ./dist/instrumentation.js i zarejestruj hook loadera @opentelemetry/instrumentation/hook.mjs zgodnie z przewodnikiem OpenTelemetry dla ESM, a potem potwierdź konfigurację, odnajdując identyfikator śladu żądania w Tempo.

Jakie nazwy metryk HTTP emituje aktualne OpenTelemetry?

Dział zatytułowany „Jakie nazwy metryk HTTP emituje aktualne OpenTelemetry?”

To pułapka, która po cichu psuje alerty pisane przez agentów. Od @opentelemetry/instrumentation-http 0.222.0 (sprawdzone 2026-09-26) README pakietu mówi, że emitowane są wyłącznie stabilne konwencje semantyczne HTTP: histogram http.server.request.duration w sekundach, z atrybutami takimi jak http.route i http.response.status_code. Wersje do 0.221.0 domyślnie emitowały stare http.server.duration w milisekundach. Tutoriale sprzed 0.222.0 — a więc i modele na nich uczone — używają starej nazwy.

Po translacji z OTLP do Prometheusa nowy histogram zwykle pojawia się jako http_server_request_duration_seconds_bucket, _count i _sum, z etykietami http_route i http_response_status_code. „Zwykle” jest tu ważne: dokładne nazwy zależą od ustawień translacji w twoim Prometheusie. Stąd zasada: agent czyta nazwy metryk z działającego Prometheusa, zanim napisze pierwsze zapytanie.

Okno terminala
# Terminal: list what Prometheus actually stores
curl -s http://localhost:9090/api/v1/label/__name__/values | jq -r '.data[]' | grep '^http_server'

Jeśli ta lista jest pusta, zatrzymaj się. Nic dalej nie będzie poprawne.

Poproś o plik Compose (albo wartości Helma) jednym promptem, a potem zwaliduj każdą wygenerowaną konfigurację narzędziem danego komponentu, zanim cokolwiek uruchomisz. Walidator jest szybszy niż debugowanie pętli restartów i nie wymaga czytania pliku. Zastąp LOKI_VERSION tagiem 3.x, który wdrażasz.

Okno terminala
# Terminal: validate the configs the agent generated
otelcol validate --config=otel-collector.yaml # use your distribution's binary, e.g. otelcol-contrib
promtool check config prometheus.yml
docker run --rm -v "$PWD":/config grafana/loki:LOKI_VERSION -config.file=/config/loki.yaml -verify-config=true

W Loki agenci najczęściej produkują martwą składnię. Loki 3.0 usunęło ustawienia shared_store dla shipperów TSDB i BoltDB, a structured metadata jest domyślnie włączone i wymaga, by aktywny schemat używał jednocześnie indeksu tsdb i schematu v13 (według przewodnika aktualizacji Loki 3.0 od Grafany). Wygenerowana konfiguracja z boltdb-shipper, schema: v11 albo shared_store nie przejdzie walidacji. Aktualny kształt:

# loki.yaml (Loki 3.x): TSDB index, schema v13, no shared_store anywhere
schema_config:
configs:
- from: 2026-01-01
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
aws:
s3: s3://s3.eu-central-1.amazonaws.com/checkout-logs
s3forcepathstyle: true
compactor:
working_directory: /loki/compactor

Wygeneruj reguły alertów, które przetestujesz bez czytania

Dział zatytułowany „Wygeneruj reguły alertów, które przetestujesz bez czytania”

Reguła alertu to mały program, a promtool test rules to jego runner testów jednostkowych. Agent pisze regułę i test razem; test podaje syntetyczne serie i sprawdza, kiedy alert odpala, a kiedy milczy. Ty przeglądasz przypadki testowe — „odpala przy 10% błędów, milczy przy 1%” — czyli dwie linijki intencji, a nie PromQL.

alerts.yml
groups:
- name: checkout-slo
rules:
- alert: CheckoutHighErrorRatio
expr: |
sum by (job) (rate(http_server_request_duration_seconds_count{job="checkout", http_response_status_code=~"5.."}[5m]))
/
sum by (job) (rate(http_server_request_duration_seconds_count{job="checkout"}[5m]))
> 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "5xx ratio above 5% on {{ $labels.job }}"
runbook_url: "https://runbooks.example.com/checkout-5xx"
# alerts_test.yml — run with: promtool test rules alerts_test.yml
rule_files: [alerts.yml]
evaluation_interval: 1m
tests:
# 10% of requests fail: the alert must fire once `for: 5m` has elapsed
- interval: 1m
input_series:
- series: 'http_server_request_duration_seconds_count{job="checkout", http_route="/pay", http_response_status_code="500"}'
values: '0+10x20'
- series: 'http_server_request_duration_seconds_count{job="checkout", http_route="/pay", http_response_status_code="200"}'
values: '0+90x20'
alert_rule_test:
- eval_time: 3m
alertname: CheckoutHighErrorRatio
exp_alerts: []
- eval_time: 15m
alertname: CheckoutHighErrorRatio
exp_alerts:
- exp_labels: { severity: critical, job: checkout }
exp_annotations:
summary: "5xx ratio above 5% on checkout"
runbook_url: "https://runbooks.example.com/checkout-5xx"
# 1% of requests fail: the alert must stay silent
- interval: 1m
input_series:
- series: 'http_server_request_duration_seconds_count{job="checkout", http_route="/pay", http_response_status_code="500"}'
values: '0+1x20'
- series: 'http_server_request_duration_seconds_count{job="checkout", http_route="/pay", http_response_status_code="200"}'
values: '0+99x20'
alert_rule_test:
- eval_time: 15m
alertname: CheckoutHighErrorRatio
exp_alerts: []

Gdy agent później edytuje regułę, trzymaj plik testu poza jego zasięgiem — z tego samego powodu, dla którego chronisz każdą wyrocznię testową: agent poproszony o „przejście testu” może osłabić asercję zamiast naprawić regułę. Zobacz ochronę wyroczni testowej.

Routing Alertmanagera traktujesz tak samo: amtool check-config alertmanager.yml wyłapuje błędy składni i nieznane odbiorniki, a amtool config routes test --config.file=alertmanager.yml severity=critical service=checkout pokazuje, do którego odbiornika trafi dany zestaw etykiet.

Walidatory chronią cię tylko wtedy, gdy uruchamiają się przy każdej zmianie. Taki job sprawia, że „alert odpala przy 10% błędów” staje się wymaganym checkiem pull requesta:

# .github/workflows/observability.yml (excerpt)
jobs:
alert-tests:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Test alert rules
run: |
docker run --rm -v "$PWD/observability:/w" -w /w --entrypoint promtool \
prom/prometheus:PROMETHEUS_VERSION check rules alerts.yml
docker run --rm -v "$PWD/observability:/w" -w /w --entrypoint promtool \
prom/prometheus:PROMETHEUS_VERSION test rules alerts_test.yml

Zastąp PROMETHEUS_VERSION tagiem Prometheusa, którego używasz na produkcji, żeby reguły testował ten sam parser, który je ładuje. Szerszy potok opisuje strona potoki CI/CD.

Wygenerowany dashboard psuje się po cichu: zła nazwa metryki renderuje się jako „No data”, co wygląda jak spokojny system. Zamiast klikać po panelach, niech agent uruchomi zapytanie każdego panelu przez serwer Grafana MCP i zgłosi te, które nic nie zwracają.

Ta tabela to dowód, który dołączasz do pull requesta. Tech lead zatwierdza na podstawie „każdy panel zwraca serie” i zrzutu ekranu, a nie JSON-a.

Daj agentowi dostęp do sygnałów z produkcji przez MCP

Dział zatytułowany „Daj agentowi dostęp do sygnałów z produkcji przez MCP”

Dwa oficjalne serwery MCP od dostawców pozwalają agentowi czytać to, co widzi dyżurny: Sentry (zgłoszenia błędów, zdarzenia, ślady i analiza Seer) oraz Grafana (dashboardy, Prometheus, Loki, incydenty). Popularność według dossier MCP, odczyt 2026-09-26: grafana/mcp-grafana ma około 3,5 tys. gwiazdek na GitHubie, a getsentry/sentry-mcp około 862. Sentry jest też w marketplace’ach pluginów Claude Code i Codeksa; Grafana udostępnia pluginy Claude Code (grafana-mcp, grafana-cloud-mcp). Użytkownicy Datadoga mają oficjalny zdalny serwer https://mcp.datadoghq.com/v1/mcp (US1; inne regiony zmieniają host). Pełny katalog znajdziesz na stronie serwery MCP do obserwowalności.

Okno terminala
# Sentry: remote server with OAuth; authenticate with /mcp inside a session
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
# Grafana: read-only; use a Viewer-role service-account token
claude mcp add grafana -e GRAFANA_URL=https://grafana.example.com \
-e GRAFANA_SERVICE_ACCOUNT_TOKEN="$GRAFANA_TOKEN" -- uvx mcp-grafana --disable-write

Jeśli twój zespół używa Sentry, jego skille uzupełniają serwer MCP: sentry-instrument, sentry-debug-issue i sentry-otel-exporter-setup są w getsentry/sentry-for-ai. Zainstalujesz je przez claude plugin install sentry@claude-plugins-official; w Codeksie przez /plugins → wyszukaj „sentry” (wpis jest na liście openai-curated; forma CLI codex plugin add sentry@openai-curated wymaga zalogowanego konta, bo marketplace jest udostępniany przy logowaniu; sprawdzone w codex-cli 0.157.1); w Cursorze zainstaluj wtyczkę Sentry dla Cursora (getsentry/plugin-cursor); inni agenci mogą pobrać te same skille z skills.sentry.dev. Samo repozytorium getsentry/sentry-for-ai jest źródłem skilli, a jego README każe instalować wtyczkę dla danego klienta, a nie repozytorium (sprawdzone 2026-10-02). Skill wybierz, gdy chcesz powtarzalnej procedury; serwer MCP — gdy agent potrzebuje danych na żywo.

Właściciel serwisu zatwierdza dowody; tech lead odpowiada za politykę alertowania. Żaden z nich nie czyta wygenerowanego YAML-a od początku do końca. Pull request, który dodaje lub zmienia monitoring, zawiera:

  • Listę nazw metryk http_server_* z działającego Prometheusa i trace ID jednego żądania znalezionego w Tempo.
  • Zielone wyniki otelcol validate, promtool check config, loki -verify-config=true i amtool check-config.
  • Zielone promtool test rules, gdzie każdy alert ma jeden przypadek „musi odpalić” i jeden „musi milczeć”.
  • Tabelę paneli z promptu dashboardowego, bez pustych paneli.
  • Potwierdzenie, że tokeny MCP są tylko do odczytu.

Tech lead przegląda przypadki testowe i progi, bo to w nich zapisana jest intencja: próg 5%, opóźnienie for: 5m i cel SLO.

Co się psuje, gdy agent buduje twoją obserwowalność?

Dział zatytułowany „Co się psuje, gdy agent buduje twoją obserwowalność?”

Objaw: puste panele, alerty, które nigdy nie odpalają, zielone promtool test rules przy cichej produkcji. Przyczyna: agent napisał z pamięci http_server_duration albo http_requests_total, a instrumentacja emituje http.server.request.duration. Naprawa: uruchom powyższy curl z nazwami metryk i przekaż agentowi listę:

Here is the output of /api/v1/label/__name__/values filtered to http_server_*. Rewrite every
query in observability/ to use only these names, rerun promtool test rules with series named
the same way, and list each query you changed.

Każdy taki przypadek oznacz w dzienniku incydentów klasą błędu agenta, która za nim stoi — najczęściej „wymyślone API” albo „nieaktualne dane treningowe” — żeby widzieć, jaką kontrolę dodać w następnej kolejności. Klasy definiuje taksonomia błędów.