# Docker Compose stack for xrpld OpenTelemetry observability. # # Provides services for local development: # - otel-collector: receives OTLP traces from xrpld, batches and # forwards them to Tempo. Also tails xrpld log files # via filelog receiver and exports to Loki. Listens on ports # 4317 (gRPC) and 4318 (HTTP). # - tempo: Grafana Tempo tracing backend, queryable via Grafana Explore # on port 3000. Recommended for production (S3/GCS storage, TraceQL). # - loki: Grafana Loki log aggregation backend for centralized log # ingestion and log-trace correlation (Phase 8). # - grafana: dashboards on port 3000, pre-configured with Tempo, # Prometheus, and Loki datasources. # # Usage: # docker compose -f docker/telemetry/docker-compose.yml up -d # # Configure xrpld to export traces by adding to xrpld.cfg: # [telemetry] # enabled=1 # endpoint=http://localhost:4318/v1/traces services: # OpenTelemetry Collector: receives spans from xrpld via OTLP protocol, # batches them for efficiency, and forwards to Tempo for storage. otel-collector: image: otel/opentelemetry-collector-contrib:0.121.0 command: ["--config=/etc/otel-collector-config.yaml"] ports: - "4317:4317" # OTLP gRPC - "4318:4318" # OTLP HTTP (traces + native OTel metrics) - "8889:8889" # Prometheus metrics (spanmetrics + OTLP) # StatsD UDP port removed — beast::insight now uses native OTLP. # Uncomment if using server=statsd fallback: # - "8125:8125/udp" volumes: # Mount collector pipeline config (receivers → processors → exporters) - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro # Mount the xrpld log root for the filelog receiver. The telemetry # configs write to docker/telemetry/data/logs//debug.log, so # the default source is the repo-relative ./data/logs — user-owned and # needing no root, so `docker compose up` works with no setup. Override # XRPLD_LOG_DIR to point at another root (e.g. the integration test sets # it to its own workdir). Mounted read-only so the collector only tails. - ${XRPLD_LOG_DIR:-./data/logs}:/var/log/xrpld:ro depends_on: - tempo - loki networks: - xrpld-telemetry # Grafana Tempo: distributed tracing backend that stores and indexes # spans. Queryable via TraceQL in Grafana Explore. tempo: image: grafana/tempo:2.7.2 command: ["-config.file=/etc/tempo.yaml"] ports: - "3200:3200" # Tempo HTTP API (health check, query) volumes: # Mount Tempo storage and ingestion config - ./tempo.yaml:/etc/tempo.yaml:ro # Persistent volume for trace data (WAL + blocks) - tempo-data:/var/tempo networks: - xrpld-telemetry # Grafana Loki for centralized log ingestion and log-trace # correlation. Loki 3.x supports native OTLP ingestion, so the OTel # Collector exports via otlphttp to Loki's /otlp endpoint. # Query logs via Grafana Explore -> Loki at http://localhost:3000. loki: image: grafana/loki:3.4.2 ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml volumes: - loki-data:/loki networks: - xrpld-telemetry prometheus: # Pinned to the v2.53 LTS line for reproducible, config-stable runs. image: prom/prometheus:v2.53.3 ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus depends_on: - otel-collector networks: - xrpld-telemetry # Grafana: visualization UI with Tempo pre-configured as a datasource. # Anonymous admin access enabled for local development convenience. grafana: image: grafana/grafana:11.5.2 # Alerting secrets/addresses (Slack webhook, alert email) come from this # gitignored file; Grafana expands the ${VARS} referenced in the alerting # provisioning YAML. Absent file = unset vars = receivers have no live # destination, which is fine for a local stack. See .env.alerting.example. env_file: - path: .env.alerting required: false environment: - GF_AUTH_ANONYMOUS_ENABLED=true # No login required for local dev - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin # Full access without auth # Remote image rendering: point Grafana at the renderer container. # These belong on the grafana service (the server delegates renders); # the callback URL is how the renderer fetches the panel from grafana. - GF_RENDERING_SERVER_URL=http://renderer:8081/render - GF_RENDERING_CALLBACK_URL=http://grafana:3000/ # SMTP for the critical-tier email receiver and the Slack webhook / email # address for the contact points all come from the env_file above, which # injects them straight into the container environment for Grafana to # expand. They are deliberately NOT duplicated here: a compose-level # `environment:` entry is interpolated from the host shell and would # override (and blank out) the env_file values. ports: - "3000:3000" # Grafana web UI volumes: # Auto-provision Tempo datasource and search filters on startup - ./grafana/provisioning:/etc/grafana/provisioning:ro - ./grafana/dashboards:/var/lib/grafana/dashboards:ro depends_on: - tempo - prometheus - loki - renderer networks: - xrpld-telemetry # Grafana image renderer: a sidecar that renders panels/dashboards to PNG # for image export and alerting. Grafana calls it at http://renderer:8081. renderer: image: grafana/grafana-image-renderer:latest ports: - "8081:8081" # Renderer HTTP endpoint (called by grafana) networks: - xrpld-telemetry # Named volume for Tempo trace storage (WAL and compacted blocks). # Data persists across container restarts. Remove with: # docker compose -f docker/telemetry/docker-compose.yml down -v volumes: tempo-data: prometheus-data: loki-data: # Isolated bridge network so services communicate by container name # (e.g., the collector reaches Tempo at http://tempo:4317). networks: xrpld-telemetry: driver: bridge