Przejdź do głównej zawartości

Konfiguracja obserwowalnośćci

Obserwowalnośćć to różnica między zgadywaniem a wiedzą o tym, co dzieje się w twoich systemach. Czy śledzisz wydajność, debugujesz problemy, czy optymalizujesz koszty, Claude Code przekształca monitorowanie z myśli końcowej w praktykę rozwoju pierwszej klasy. Ta lekcja bada, jak wykorzystać pomoc AI do kompleksowej obserwowalnośćci.

Scenariusz: Twoja aplikacja obsługuje miliony żądań dziennie w dziesiątkach mikroserwisów. Gdy coś pójdzie nie tak, potrzebujesz odpowiedzi w sekundach, nie godzinach. Tradycyjne podejście: grep przez logi, zgadywanie korelacji, nadzieja na najlepsze. Z Claude Code: inteligentne monitorowanie, które mówi ci co jest nie tak, zanim użytkownicy to zauważą.

Tydzień 1: Podstawowe logowanie
- Console.log wszędzie
- Brak struktury czy spójności
- Logi gubione przy restarcie kontenera
Tydzień 2: Konfiguracja metryk
- Ręczna instrumentacja metryk
- Podstawowe wykresy CPU/pamięci
- Brak niestandardowych metryk biznesowych
Tydzień 3: Kryzys debugowania
- Problem produkcyjny o 3 w nocy
- Grep przez gigabajty logów
- Brak korelacji między usługami
Tydzień 4: Post-mortem
- "Potrzebujemy lepszego monitorowania"
- Zaczynamy od nowa z nowymi narzędziami
> Skonfiguruj OpenTelemetry dla naszej aplikacji Node.js:
> - Automatyczna instrumentacja
> - Niestandardowe spany i metryki
> - Korelacja logów
> - Export do wielu backendów
> - Konfiguracja specyficzna dla środowiska

Claude generuje kompleksową konfigurację:

  1. Zainstaluj zależności
Okno terminala
npm install @opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-metrics-otlp-http \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/instrumentation-http \
@opentelemetry/instrumentation-express \
@opentelemetry/instrumentation-winston
  1. Stwórz konfigurację telemetrii

    telemetry.js
    const { NodeSDK } = require('@opentelemetry/sdk-node');
    const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
    const { Resource } = require('@opentelemetry/resources');
    const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
    const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
    const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
    const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
    // Skonfiguruj atrybuty zasobów
    const resource = Resource.default().merge(
    new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: process.env.SERVICE_NAME || 'myapp',
    [SemanticResourceAttributes.SERVICE_VERSION]: process.env.SERVICE_VERSION || '1.0.0',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'development',
    'team.name': 'platform',
    'team.owner': 'engineering'
    })
    );
    // Skonfiguruj eksporter tracingu
    const traceExporter = new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || 'http://localhost:4318/v1/traces',
    headers: {
    'api-key': process.env.OTEL_EXPORTER_API_KEY
    }
    });
    // Skonfiguruj eksporter metryk
    const metricExporter = new OTLPMetricExporter({
    url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || 'http://localhost:4318/v1/metrics',
    headers: {
    'api-key': process.env.OTEL_EXPORTER_API_KEY
    }
    });
    // Zainicjalizuj SDK
    const sdk = new NodeSDK({
    resource,
    traceExporter,
    metricReader: new PeriodicExportingMetricReader({
    exporter: metricExporter,
    exportIntervalMillis: 10000
    }),
    instrumentations: [
    getNodeAutoInstrumentations({
    '@opentelemetry/instrumentation-fs': {
    enabled: false // Wyłącz hałaśliwą instrumentację fs
    }
    })
    ]
    });
    // Zainicjalizuj i eksportuj
    sdk.start()
    .then(() => console.log('OpenTelemetry zainicjalizowane'))
    .catch((error) => console.error('Nieudana inicjalizacja OpenTelemetry', error));
    module.exports = { sdk };
  2. Dodaj do punktu wejścia aplikacji

    // index.js - Musi być pierwszym importem!
    require('./telemetry');
    const express = require('express');
    const { trace, metrics } = require('@opentelemetry/api');
    // Pobierz tracer i meter
    const tracer = trace.getTracer('myapp');
    const meter = metrics.getMeter('myapp');
    // Stwórz niestandardowe metryki
    const requestCounter = meter.createCounter('http_requests', {
    description: 'Liczba żądań HTTP',
    unit: '1'
    });
    const requestDuration = meter.createHistogram('http_request_duration', {
    description: 'Czas trwania żądań HTTP',
    unit: 'ms'
    });
    const app = express();
    // Middleware dla niestandardowych metryk
    app.use((req, res, next) => {
    const start = Date.now();
    res.on('finish', () => {
    const duration = Date.now() - start;
    const labels = {
    method: req.method,
    route: req.route?.path || 'unknown',
    status_code: res.statusCode.toString()
    };
    requestCounter.add(1, labels);
    requestDuration.record(duration, labels);
    });
    next();
    });
  3. Dodaj niestandardowe spany

    // Przykład niestandardowej instrumentacji
    async function processOrder(orderId) {
    // Rozpocznij nowy span
    return tracer.startActiveSpan('process_order', async (span) => {
    try {
    // Dodaj atrybuty spanu
    span.setAttributes({
    'order.id': orderId,
    'order.processing_step': 'validation'
    });
    // Waliduj zamówienie
    const order = await validateOrder(orderId);
    // Stwórz span potomny dla płatności
    await tracer.startActiveSpan('process_payment', async (paymentSpan) => {
    paymentSpan.setAttributes({
    'payment.amount': order.total,
    'payment.currency': order.currency
    });
    await processPayment(order);
    paymentSpan.setStatus({ code: SpanStatusCode.OK });
    });
    // Zapisz niestandardową metrykę
    orderProcessingCounter.add(1, {
    status: 'success',
    payment_method: order.paymentMethod
    });
    span.setStatus({ code: SpanStatusCode.OK });
    return order;
    } catch (error) {
    span.recordException(error);
    span.setStatus({
    code: SpanStatusCode.ERROR,
    message: error.message
    });
    throw error;
    } finally {
    span.end();
    }
    });
    }
> Skonfiguruj rozproszone tracowanie w naszych mikroserwisach:
> - Propagacja kontekstu tracingu
> - Mapowanie zależności usług
> - Analiza ścieżki krytycznej
> - Identyfikacja wąskich gardeł wydajności
// Propagacja kontekstu tracingu
const { propagation, trace, context } = require('@opentelemetry/api');
// Klient HTTP z propagacją tracingu
async function callDownstreamService(url, data) {
const span = trace.getActiveSpan();
// Stwórz nagłówki z kontekstem tracingu
const headers = {};
propagation.inject(context.active(), headers);
try {
const response = await fetch(url, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
span?.addEvent('downstream_service_called', {
'http.url': url,
'http.status_code': response.status
});
return response.json();
} catch (error) {
span?.recordException(error);
throw error;
}
}
// Wyciągnij kontekst tracingu w usłudze odbierającej
app.use((req, res, next) => {
// Wyciągnij kontekst tracingu z przychodzącego żądania
const extractedContext = propagation.extract(
context.active(),
req.headers
);
// Kontynuuj tracing z wyciągniętym kontekstem
context.with(extractedContext, () => {
next();
});
});
> Skonfiguruj strukturalne logowanie z:
> - Formatem JSON dla łatwego parsowania
> - ID korelacji
> - Poziomami logów i samplingiem
> - Integracją z OpenTelemetry
> - Maskowaniem wrażliwych danych
logger.js
const winston = require('winston');
const { trace, context } = require('@opentelemetry/api');
// Niestandardowy format zawierający informacje o tracingu
const traceFormat = winston.format((info) => {
const span = trace.getActiveSpan();
if (span) {
const spanContext = span.spanContext();
info.traceId = spanContext.traceId;
info.spanId = spanContext.spanId;
}
// Dodaj request ID jeśli dostępne
const requestId = context.active().getValue('requestId');
if (requestId) {
info.requestId = requestId;
}
return info;
});
// Maskowanie wrażliwych danych
const maskSensitive = winston.format((info) => {
const sensitive = ['password', 'token', 'apiKey', 'pesel', 'creditCard'];
const mask = (obj) => {
if (typeof obj !== 'object' || obj === null) return obj;
const masked = Array.isArray(obj) ? [] : {};
for (const [key, value] of Object.entries(obj)) {
if (sensitive.some(s => key.toLowerCase().includes(s))) {
masked[key] = '***REDACTED***';
} else if (typeof value === 'object' && value !== null) {
masked[key] = mask(value);
} else {
masked[key] = value;
}
}
return masked;
};
if (info.meta) {
info.meta = mask(info.meta);
}
return info;
});
// Stwórz instancję loggera
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
traceFormat(),
maskSensitive(),
winston.format.json()
),
defaultMeta: {
service: process.env.SERVICE_NAME || 'myapp',
environment: process.env.NODE_ENV || 'development'
},
transports: [
new winston.transports.Console({
format: process.env.NODE_ENV === 'development'
? winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
: winston.format.json()
})
]
});
// Dodaj sampling logów dla wysokowolumenowych logów
logger.sample = (rate = 0.1) => {
return {
log: (level, message, meta) => {
if (Math.random() < rate) {
logger.log(level, message, { ...meta, sampled: true });
}
}
};
};
module.exports = logger;
// Wzorce agregacji logów
class LogAggregator {
constructor() {
this.buffers = new Map();
this.flushInterval = 5000; // 5 sekund
setInterval(() => this.flush(), this.flushInterval);
}
aggregate(key, data) {
if (!this.buffers.has(key)) {
this.buffers.set(key, {
count: 0,
firstSeen: Date.now(),
lastSeen: Date.now(),
samples: []
});
}
const buffer = this.buffers.get(key);
buffer.count++;
buffer.lastSeen = Date.now();
// Trzymaj tylko pierwsze 5 próbek
if (buffer.samples.length < 5) {
buffer.samples.push(data);
}
}
flush() {
for (const [key, buffer] of this.buffers) {
if (buffer.count > 0) {
logger.info('Zagregowany wpis logu', {
key,
count: buffer.count,
duration: buffer.lastSeen - buffer.firstSeen,
samples: buffer.samples
});
}
}
this.buffers.clear();
}
}
// Użycie dla zdarzeń wysokiej częstotliwości
const aggregator = new LogAggregator();
// Zamiast logowania każdego żądania
app.use((req, res, next) => {
aggregator.aggregate(`request:${req.method}:${req.path}`, {
userAgent: req.headers['user-agent'],
ip: req.ip
});
next();
});
> Zaimplementuj niestandardowe metryki biznesowe:
> - Czas przetwarzania zamówień
> - Przychód na minutę
> - Wskaźnik porzucania koszyka
> - Wskaźniki sukcesu API
> - Metryki adopcji funkcji
metrics.js
const { metrics } = require('@opentelemetry/api');
const meter = metrics.getMeter('business-metrics');
// Definicje metryk biznesowych
const orderCounter = meter.createCounter('orders_total', {
description: 'Łączna liczba zamówień',
unit: '1'
});
const revenueCounter = meter.createCounter('revenue_total', {
description: 'Łączny przychód',
unit: 'PLN'
});
const cartAbandonmentGauge = meter.createUpDownCounter('cart_abandonment', {
description: 'Liczba porzuconych koszyków',
unit: '1'
});
const apiLatencyHistogram = meter.createHistogram('api_latency', {
description: 'Opóźnienie endpointów API',
unit: 'ms'
});
const activeUsersGauge = meter.createObservableGauge('active_users', {
description: 'Liczba aktywnych użytkowników'
});
// Skonfiguruj callback observable gauge
activeUsersGauge.addCallback(async (observableResult) => {
const count = await getActiveUserCount();
observableResult.observe(count, {
period: '5m'
});
});
// Pomocnicy metryk biznesowych
class BusinessMetrics {
static recordOrder(order) {
orderCounter.add(1, {
status: order.status,
payment_method: order.paymentMethod,
customer_type: order.isNewCustomer ? 'new' : 'returning'
});
revenueCounter.add(order.total, {
currency: order.currency,
region: order.region
});
}
static recordCartAbandonment(cart) {
cartAbandonmentGauge.add(1, {
value: cart.total,
items_count: cart.items.length,
reason: cart.abandonmentReason || 'unknown'
});
}
static recordApiCall(endpoint, method, duration, success) {
apiLatencyHistogram.record(duration, {
endpoint,
method,
success: success.toString()
});
}
static async recordFeatureUsage(feature, userId) {
const featureCounter = meter.createCounter(`feature_usage_${feature}`, {
description: `Użycie funkcji ${feature}`
});
featureCounter.add(1, {
user_segment: await getUserSegment(userId),
first_time: await isFirstTimeUsage(userId, feature)
});
}
}
module.exports = BusinessMetrics;
// Monitorowanie wydajności
const performanceObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Rejestruj metryki wydajności
const histogram = meter.createHistogram(`performance_${entry.entryType}`, {
description: `Timing wydajności dla ${entry.entryType}`,
unit: 'ms'
});
histogram.record(entry.duration, {
name: entry.name,
type: entry.entryType
});
}
});
performanceObserver.observe({
entryTypes: ['measure', 'navigation', 'resource']
});
// Wydajność zapytań bazy danych
const dbQueryHistogram = meter.createHistogram('db_query_duration', {
description: 'Czas wykonania zapytania bazy danych',
unit: 'ms'
});
// Owijanie zapytań bazodanowych
async function instrumentedQuery(sql, params) {
const startTime = performance.now();
const labels = {
operation: sql.split(' ')[0].toUpperCase(),
table: extractTableName(sql)
};
try {
const result = await db.query(sql, params);
labels.success = 'true';
return result;
} catch (error) {
labels.success = 'false';
labels.error_type = error.constructor.name;
throw error;
} finally {
const duration = performance.now() - startTime;
dbQueryHistogram.record(duration, labels);
}
}
> Skonfiguruj Prometheus do zbierania metryk:
> - Konfiguracja scrapowania
> - Reguły nagrywania
> - Reguły alertowania
> - Konfiguracja federacji
prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: 'production'
region: 'pl-central-1'
# Konfiguracja alertowania
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# Pliki reguł
rule_files:
- 'recording_rules.yml'
- 'alerting_rules.yml'
# Konfiguracje scrapowania
scrape_configs:
# Metryki aplikacji
- job_name: 'myapp'
static_configs:
- targets: ['myapp:9090']
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '([^:]+):.*'
replacement: '${1}'
# Kubernetes service discovery
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
# recording_rules.yml
groups:
- name: myapp_recording_rules
interval: 30s
rules:
# Wskaźnik żądań
- record: myapp:http_requests:rate5m
expr: rate(http_requests_total[5m])
# Wskaźnik błędów
- record: myapp:http_errors:rate5m
expr: rate(http_requests_total{status=~"5.."}[5m])
# P95 opóźnienia
- record: myapp:http_latency:p95
expr: histogram_quantile(0.95, rate(http_request_duration_bucket[5m]))
# Metryki biznesowe
- record: myapp:orders:rate1h
expr: rate(orders_total[1h])
- record: myapp:revenue:rate1h
expr: rate(revenue_total[1h])
# alerting_rules.yml
groups:
- name: myapp_alerts
rules:
- alert: HighErrorRate
expr: myapp:http_errors:rate5m > 0.05
for: 5m
labels:
severity: critical
team: platform
annotations:
summary: "Wykryto wysoki wskaźnik błędów"
description: "Wskaźnik błędów to {{ $value | humanizePercentage }} dla {{ $labels.instance }}"
- alert: HighLatency
expr: myapp:http_latency:p95 > 1000
for: 10m
labels:
severity: warning
annotations:
summary: "Wykryto wysokie opóźnienie"
description: "Opóźnienie P95 to {{ $value }}ms"
- alert: LowOrderRate
expr: myapp:orders:rate1h < 10
for: 30m
labels:
severity: warning
team: business
annotations:
summary: "Niski wskaźnik zamówień"
description: "Wskaźnik zamówień spadł do {{ $value }} zamówień/godzinę"
> Stwórz kompleksowe dashboardy Grafana:
> - Przegląd systemu
> - Metryki biznesowe
> - Analiza wydajności
> - Śledzenie błędów
> - Monitorowanie SLA
dashboard.json
{
"dashboard": {
"title": "Dashboard produkcyjny MyApp",
"panels": [
{
"title": "Wskaźnik żądań",
"type": "graph",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (method)",
"legendFormat": "{{method}}"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
},
{
"title": "Wskaźnik błędów",
"type": "graph",
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) * 100",
"legendFormat": "Błędy %"
}
],
"alert": {
"conditions": [
{
"evaluator": { "params": [5], "type": "gt" },
"operator": { "type": "and" },
"query": { "params": ["A", "5m", "now"] },
"reducer": { "params": [], "type": "avg" }
}
]
},
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
},
{
"title": "Percentyle czasu odpowiedzi",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_bucket[5m])) by (le))",
"legendFormat": "p99"
},
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_bucket[5m])) by (le))",
"legendFormat": "p95"
},
{
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_bucket[5m])) by (le))",
"legendFormat": "p50"
}
],
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }
},
{
"title": "KPI biznesowe",
"type": "stat",
"targets": [
{
"expr": "sum(rate(orders_total[1h])) * 3600",
"legendFormat": "Zamówienia/Godzinę"
},
{
"expr": "sum(rate(revenue_total[1h])) * 3600",
"legendFormat": "Przychód/Godzinę"
},
{
"expr": "sum(active_users)",
"legendFormat": "Aktywni użytkownicy"
}
],
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }
}
]
}
}
> Skonfiguruj śledzenie błędów z:
> - Automatycznym przechwytywaniem błędów
> - Zbieraniem stack trace
> - Kontekstem użytkownika
> - Śledzeniem wersji
> - Grupowaniem błędów
error-tracking.js
const Sentry = require('@sentry/node');
const { ProfilingIntegration } = require('@sentry/profiling-node');
// Zainicjalizuj Sentry
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
release: process.env.SERVICE_VERSION,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Sentry.Integrations.Express({ app }),
new ProfilingIntegration()
],
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
profilesSampleRate: 0.1,
beforeSend(event, hint) {
// Odfiltruj znane problemy
if (event.exception?.values?.[0]?.type === 'NetworkError') {
return null;
}
// Dodaj niestandardowy kontekst
event.extra = {
...event.extra,
nodeVersion: process.version,
memory: process.memoryUsage()
};
return event;
}
});
// Middleware obsługi błędów
app.use((err, req, res, next) => {
// Loguj do naszego loggera
logger.error('Nieobsłużony błąd', {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method,
ip: req.ip,
userAgent: req.get('user-agent')
});
// Wyślij do Sentry z kontekstem
Sentry.withScope((scope) => {
scope.setContext('request', {
url: req.url,
method: req.method,
headers: req.headers,
query: req.query,
body: req.body
});
scope.setUser({
id: req.user?.id,
email: req.user?.email,
ip_address: req.ip
});
scope.setTag('endpoint', req.route?.path || 'unknown');
scope.setLevel('error');
Sentry.captureException(err);
});
// Wyślij odpowiedź błędu
res.status(err.status || 500).json({
error: {
message: process.env.NODE_ENV === 'production'
? 'Błąd wewnętrzny serwera'
: err.message,
id: res.sentry
}
});
});
alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: 'YOUR_SLACK_WEBHOOK'
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'default'
routes:
- match:
severity: critical
receiver: pagerduty
continue: true
- match:
team: platform
receiver: platform-slack
- match:
team: business
receiver: business-alerts
receivers:
- name: 'default'
slack_configs:
- channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
- name: 'pagerduty'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_KEY'
description: '{{ .GroupLabels.alertname }}: {{ .CommonAnnotations.summary }}'
- name: 'platform-slack'
slack_configs:
- channel: '#platform-alerts'
send_resolved: true
title: '🚨 {{ .GroupLabels.alertname }}'
text: |
*Alert:* {{ .GroupLabels.alertname }}
*Ważność:* {{ .CommonLabels.severity }}
*Opis:* {{ .CommonAnnotations.description }}
*Runbook:* <{{ .CommonAnnotations.runbook_url }}|Zobacz Runbook>
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'cluster', 'service']
> Skonfiguruj monitorowanie telemetrii Claude Code:
> - Metryki użycia
> - Śledzenie kosztów
> - Analiza wydajności
> - Metryki adopcji zespołu
Okno terminala
# Włącz telemetrię Claude Code
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"
# Dodaj niestandardowe atrybuty do śledzenia zespołu
export OTEL_RESOURCE_ATTRIBUTES="department=engineering,team.id=platform,cost_center=eng-123"
// Zapytania dashboardu metryk Claude Code
const claudeMetrics = {
// Produktywność programistów
linesOfCode: `
sum(rate(claude_code.lines_of_code.count[1d])) by (user_account_uuid, type)
`,
// Śledzenie kosztów
costPerTeam: `
sum(claude_code.cost.usage) by (department, team_id)
`,
// Wskaźnik akceptacji narzędzi
acceptanceRate: `
sum(rate(claude_code.code_edit_tool.decision{decision="accept"}[1h])) /
sum(rate(claude_code.code_edit_tool.decision[1h])) * 100
`,
// Aktywni programiści
activeDevelopers: `
count(count by (user_account_uuid)(claude_code.session.count))
`,
// Użycie języków
languageDistribution: `
sum(claude_code.code_edit_tool.decision) by (language)
`
};
> Skonfiguruj kompleksowe APM:
> - Śledzenie transakcji
> - Analiza zapytań bazy danych
> - Monitorowanie usług zewnętrznych
> - Wykorzystanie zasobów
// Integracja APM
const apm = require('elastic-apm-node').start({
serviceName: process.env.SERVICE_NAME,
secretToken: process.env.ELASTIC_APM_SECRET_TOKEN,
serverUrl: process.env.ELASTIC_APM_SERVER_URL,
environment: process.env.NODE_ENV,
transactionSampleRate: 0.1,
captureBody: 'errors',
errorOnAbortedRequests: true,
captureErrorLogStackTraces: 'always',
usePathAsTransactionName: false
});
// Niestandardowe śledzenie transakcji
async function complexBusinessOperation(data) {
const transaction = apm.startTransaction('process_order', 'business');
try {
// Śledź operacje bazy danych
const span = apm.startSpan('validate_inventory', 'db');
const inventory = await checkInventory(data.items);
span.end();
// Śledź wywołania zewnętrznych API
const paymentSpan = apm.startSpan('process_payment', 'external');
const payment = await processPayment(data.payment);
paymentSpan.end();
// Śledź niestandardowe operacje
const fulfillmentSpan = apm.startSpan('create_fulfillment', 'custom');
const order = await createFulfillmentOrder(data, payment);
fulfillmentSpan.end();
transaction.result = 'success';
return order;
} catch (error) {
apm.captureError(error);
transaction.result = 'error';
throw error;
} finally {
transaction.end();
}
}
> Zaimplementuj monitorowanie SLO:
> - Cele dostępności
> - Cele opóźnienia
> - Budżety błędów
> - Alerty wskaźnika spalania
slo_rules.yml
groups:
- name: slo_rules
interval: 30s
rules:
# SLO dostępności - 99.9%
- record: slo:availability:ratio
expr: |
sum(rate(http_requests_total{status!~"5.."}[5m])) /
sum(rate(http_requests_total[5m]))
# Wskaźnik spalania budżetu błędów
- record: slo:error_budget:burn_rate_1h
expr: |
(1 - slo:availability:ratio) / (1 - 0.999) * (30 * 24) / 1
- record: slo:error_budget:burn_rate_6h
expr: |
(1 - slo:availability:ratio) / (1 - 0.999) * (30 * 24) / 6
# SLO opóźnienia - 95% żądań poniżej 500ms
- record: slo:latency:ratio
expr: |
sum(rate(http_request_duration_bucket{le="0.5"}[5m])) /
sum(rate(http_request_duration_count[5m]))
# Alerty multi-window multi-burn-rate
- alert: ErrorBudgetBurn
expr: |
(
slo:error_budget:burn_rate_1h > 14.4
and
slo:error_budget:burn_rate_6h > 6
)
labels:
severity: critical
slo: availability
annotations:
summary: "Budżet błędów spala się zbyt szybko"
description: "Wskaźnik spalania budżetu błędów to {{ $value }} razy normalny"

Nauczyłeś się, jak wykorzystać Claude Code do kompleksowej obserwowalnośćci - od instrumentacji po wizualizację po alertowanie. Kluczem jest traktowanie monitorowania jako pierwszoklasowego obywatela w procesie rozwoju, nie jako myśli końcowej.

Pamiętaj: Nie możesz naprawić tego, czego nie widzisz. Używaj Claude Code do budowania obserwowalnośćci w swoje aplikacje od samego początku, tworząc systemy, które mówią ci co jest nie tak, zanim twoi użytkownicy to zauważą. Z odpowiednim monitorowaniem będziesz wdrażać z pewnością siebie i spać lepiej w nocy.