Monitoring & Logs
Monitoring & Logs
Current Observability Stack
| Component | Purpose | Port |
|---|---|---|
OpenTelemetry SDK (@terma/observability) | Auto-instrumentation of HTTP, metrics & traces | — |
| Prometheus | Metrics collection (pull) | prometheus:9090 |
| Jaeger | Trace collection & visualization (OTLP HTTP) | jaeger:4318 (traces), jaeger:16686 (UI) |
| Grafana | Dashboards (auto-provisioned) | grafana:3000 |
| pino | Structured JSON logs with traceId | — |
1. OpenTelemetry (Auto-instrumentation)
The packages/observability package initializes the OTel SDK at service startup:
- HTTP server (Fastify) — automatic spans via
@opentelemetry/instrumentation-http - HTTP client — W3C
traceparentheader injection on every outgoing request - PrometheusExporter — metrics on port
9090(api) /9091(bot) - OTLPTraceExporter — traces sent to Jaeger (push)
Verify OTel Status
curl http://localhost:9090/metrics | head -20curl http://localhost:3000/api/health | jq .otelExpected /health response:
{ "status": "ok", "version": "0.1.2", "otel": { "initialized": true, "tracer": "started", "meter": "started" }, "uptime": 12345}2. Prometheus (Metrics)
Collection
Each service runs a built-in Prometheus exporter:
| Service | Port | Endpoint |
|---|---|---|
| api | 9090 | /metrics |
| bot | 9091 | /metrics |
Prometheus is configured via scripts/prometheus-conf/prometheus.yml.
Available Metrics
http.server.duration— latency histogram (ms)http.server.request_count— request count by route/statusprocess.memory.usage— process memoryprocess.cpu.utilization— process CPU
Accessing Prometheus
# Port forwarding (without Grafana)ssh -L 9090:localhost:9090 user@vps# Open: http://localhost:90903. Jaeger (Tracing)
End-to-end Propagation
Telegram → bot → api → dbW3C Trace Context (traceparent) is automatically injected on every HTTP call:
- Bot → API:
createApiClientinjectspropagation.inject() - API → DB: HTTP-level tracing (Fastify)
View Traces in Jaeger UI
# Port forwardingssh -L 16686:localhost:16686 user@vps# Open: http://localhost:16686Searching for traces:
- Select service
terma-apiorterma-bot - Set time range
- Click Find Traces
Waterfall flamegraph:
grammy.command.start → api-client.fetch (/internal/users/ensure) → Fastify.route (POST /internal/users/ensure) → db.query (drizzle)4. Grafana (Dashboards)
Grafana is auto-provisioned via scripts/grafana/datasources.yml + scripts/grafana/dashboards.yml.
Panels
- Total Requests — request rate (api/bot)
- Success Rate — percentage of successful requests
- HTTP Errors Breakdown — 4xx/5xx errors by status code
- Latency p50/p95/p99 — latency percentiles
- Last 5xx Errors — recent errors with
traceId - Service Uptime — Prometheus target status
Error Search by traceId
In the Last 5xx Errors panel, recent errors are displayed with clickable Jaeger links:
- Click on a traceId in the panel
- Jaeger UI opens with the full waterfall trace
- Analyze latency and errors on each span
Port Forwarding
ssh -L 3000:localhost:3000 user@vps# Open: http://localhost:3000# Login: admin / password from ${GRAFANA_PASSWORD}5. Structured Logs (pino)
All services produce JSON logs with the following fields:
{ "time": "2025-06-15T10:30:00.123Z", "level": "info", "msg": "request completed", "traceId": "4e1f2a3b...", "spanId": "8c9d0e1f...", "name": "http.request", "method": "GET", "url": "/api/health", "statusCode": 200, "responseTime": 12.3}Viewing Logs
# Tail all service logsdocker compose -f docker-compose.stage.yml logs --tail=50 -f
# Search by traceIddocker compose -f docker-compose.stage.yml logs | grep "4e1f2a3b"
# Filter errorsdocker compose -f docker-compose.stage.yml logs | grep '"level":"error"'6. Docker Monitoring Services
Services are started via docker-compose.stage.yml:
# Start the monitoring stackdocker compose -f docker-compose.stage.yml up -d prometheus jaeger grafana
# Check statusdocker compose -f docker-compose.stage.yml ps
# View logsdocker compose -f docker-compose.stage.yml logs -f prometheus7. Health Check
Endpoint
GET /api/health → 200 OKResponse includes OTel status:
{ "status": "ok", "version": "0.1.2", "otel": { "initialized": true, "tracer": "started", "meter": "started" }, "uptime": 12345}Cron Check
*/3 * * * * curl -sf https://termagrand.ru/api/health \ || curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ -d "chat_id=${ADMIN_CHAT_ID}&text=🚨+Healthcheck+FAILED+termagrand.ru"External Uptime Monitor
Recommended: UptimeRobot (free plan, 5-min interval) or BetterStack.
Expected status: 200 OK.
8. Security & 152-FZ Compliance
- Metrics — aggregated numeric values (no PII)
- Traces — technical identifiers (traceId, spanId) and URL templates
- Logs — traceId/spanId, no PII
- Request bodies are not recorded in span attributes
- HTTP URL paths without query parameters (pathname only)