Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Open-source service map visualization tool that generates service dependency maps from OpenTelemetry trace data.

License

Notifications You must be signed in to change notification settings

jack5341/otelmap

Open more actions menu

Repository files navigation

OTELMAP

OTel Map Server

OpenTelemetry traces → ClickHouse storage → Service Map API. This project ingests OTLP (HTTP and gRPC) into ClickHouse via the OpenTelemetry Collector, and exposes an API to build a service map and basic SLO-style stats from collected spans.

OTELMAP

Architecture

  • NGINX reverse proxy routes traffic:
    • /api/* → Go API server (server:8000)
    • /v1/* → OTLP HTTP to collector (otelcollector:4318)
    • gRPC traffic → OTLP gRPC to collector (otelcollector:4317)
  • OpenTelemetry Collector writes to ClickHouse tables
  • Go API reads from ClickHouse and returns a service-map DTO
flowchart TD
    %% Nodes
    A[🌐 Internet] --> B[NGINX]

    %% Tracing Flow
    B -->|OTLP /v1/tracing| C[OpenTelemetry Collector]
    C -->|Insert traces| D[(ClickHouse)]

    %% Application Flow
    B -->|Create token / Get service map| E[Server]
    E -->|Store / Query data| D

    %% Endpoints
    D --> F[📊 Persisted Data]
Loading

Prerequisites

  • Docker and Docker Compose

Environment variables (.env.production)

Create .env.production in the repo root with:

# Required
PORT=8000
SERVICE_NAME=otel-map-server
LOG_LEVEL=info
SHUTDOWN_TIMEOUT_SECONDS=10
CLICKHOUSE_DSN=clickhouse://default:default@clickhouse:9000/default?dial_timeout=5s&compress=true
BASE_URL=localhost

Run with Docker Compose

docker compose pull
docker compose up -d

Services:

  • clickhouse:9000 (native), 8123 (HTTP)
  • otelcollector:4317 (gRPC), 4318 (HTTP), 13133 (health)
  • server:8000 (internal API)
  • nginx:80, 443 (reverse proxy)

After startup, the endpoints will be available at:

  • http://localhost/api/v1/* (API endpoints)
  • http://localhost/v1/traces (OTLP HTTP ingest)
  • http://localhost:4317 (OTLP gRPC ingest - direct to collector)

API Endpoints

  • GET /api/v1/healthz → health check
  • GET /api/v1/readyz → readiness check
  • POST /api/v1/session-token → returns a session token and example ingest config
  • GET /api/v1/session-events?token=<uuid> → SSE endpoint for listening to trace events
  • GET /api/v1/service-map/:session-token?start=RFC3339&end=RFC3339 → get service map

Service Map Response

  • Direct, Jaeger-style service dependencies are returned as deduplicated parent→child edges.
  • Per-service fields:
    • name, count, rps, throughput_bps, error_rate.
  • Per-edge fields:
    • source (service), target (service), rps.
  • Time window:
    • Service rps and edge rps are computed over the last 60 seconds relative to the request time.

Example

{
  "services": [
    {
      "name": "frontend",
      "count": 1234,
      "rps": 12.3,
      "throughput_bps": 456789.0,
      "error_rate": 0.02
    },
    {
      "name": "backend",
      "count": 980,
      "rps": 9.8,
      "throughput_bps": 321000.0,
      "error_rate": 0.01
    }
  ],
  "edges": [
    {
      "source": { "name": "frontend", "count": 1234, "rps": 12.3, "throughput_bps": 456789.0, "error_rate": 0.02 },
      "rps": 8.5,
      "target": { "name": "backend", "count": 980, "rps": 9.8, "throughput_bps": 321000.0, "error_rate": 0.01 }
    }
  ]
}

Conceptually aligned with Jaeger’s service dependency graph. See: Jaeger repository.

Tracing Ingest

First, create a session token:

curl -X POST http://localhost/api/v1/session-token

The response contains:

  • token: your unique session token (UUID)
  • ingest.otlp_http_url: e.g., http://localhost/v1/traces
  • ingest.otlp_grpc_url: e.g., http://localhost:4317
  • ingest.resource_attribute: { key: "otelmap.session_token", value: <token> }

Important: Ensure your tracer sets the resource attribute otelmap.session_token with your session token value so spans are associated with your session.

Context Propagation

  • NGINX forwards traceparent, tracestate, and baggage headers
  • Echo middleware extracts W3C headers into the request context
  • Global propagator is set to TraceContext + Baggage
  • All spans created in handlers and MapManager use the incoming context for proper trace continuity

Troubleshooting

  • Ingest: curl http://localhost/v1/traces -I should return 405/404 (collector present)
  • Collector health: curl http://localhost:13133/healthz
  • ClickHouse connectivity: docker exec -it <clickhouse-container> clickhouse-client --query "SELECT 1"
  • Service map empty: ensure spans include the otelmap.session_token resource attribute matching your session token
  • Check collector logs: docker logs otel
  • Check server logs: docker logs <server-container-name>

Development

Standard Go module project. Key packages:

  • cmd/server: main entrypoint
  • internal/http: Echo routing and middleware
  • internal/handlers: handlers for health, session token, and service map
  • pkg/map_manager: builds the map DTO from ClickHouse rows

About

Open-source service map visualization tool that generates service dependency maps from OpenTelemetry trace data.

Topics

Resources

License

Stars

Watchers

Forks

Morty Proxy This is a proxified and sanitized view of the page, visit original site.