{"id":47705481,"url":"https://github.com/cameronxie/otel-polyglot","last_synced_at":"2026-05-10T14:02:17.825Z","repository":{"id":343670120,"uuid":"1178665311","full_name":"CameronXie/otel-polyglot","owner":"CameronXie","description":"A polyglot project for validating OpenTelemetry SDK implementations across multiple languages using identical API endpoints that emit consistent telemetry signals for testing observability backends.","archived":false,"fork":false,"pushed_at":"2026-04-27T11:28:02.000Z","size":274,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T12:28:55.990Z","etag":null,"topics":["aspnet-core","c-sharp","fastapi","go-gin","grafana","loki","mcp-servers","opentelemetry","otel-collector","prometheus","tempo"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CameronXie.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-11T08:43:54.000Z","updated_at":"2026-04-27T11:27:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/CameronXie/otel-polyglot","commit_stats":null,"previous_names":["cameronxie/otel-polyglot"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/CameronXie/otel-polyglot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CameronXie%2Fotel-polyglot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CameronXie%2Fotel-polyglot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CameronXie%2Fotel-polyglot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CameronXie%2Fotel-polyglot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CameronXie","download_url":"https://codeload.github.com/CameronXie/otel-polyglot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CameronXie%2Fotel-polyglot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32495949,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","c-sharp","fastapi","go-gin","grafana","loki","mcp-servers","opentelemetry","otel-collector","prometheus","tempo"],"created_at":"2026-04-02T17:55:19.189Z","updated_at":"2026-05-10T14:02:17.810Z","avatar_url":"https://github.com/CameronXie.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OTel Polyglot\n\n[![CI](https://github.com/CameronXie/otel-polyglot/actions/workflows/ci.yaml/badge.svg)](https://github.com/CameronXie/otel-polyglot/actions/workflows/ci.yaml)\n\nA polyglot project for validating OpenTelemetry SDK implementations across multiple\nlanguages. Each service implements an identical API surface and emits the same telemetry\nsignals, enabling direct comparison of instrumentation patterns and consistent evaluation\nof observability backends.\n\n## Architecture\n\nEach service exports traces, metrics, and logs over gRPC (OTLP) to an OpenTelemetry\nCollector, which routes each signal type to its respective backend for storage and\nvisualisation.\n\n```mermaid\ngraph LR\n    subgraph Services\n        S1[Service Implementation A]\n        S2[Service Implementation B]\n        S3[Service Implementation N]\n    end\n\n    subgraph MCP Servers\n        M1[MCP Server A]\n        M2[MCP Server B]\n    end\n\n    subgraph Observability Stack\n        C[OTel Collector]\n        G[Grafana]\n        L[Loki]\n        T[Tempo]\n        P[Prometheus]\n    end\n\n    S1 -- OTLP/gRPC --\u003e C\n    S2 -- OTLP/gRPC --\u003e C\n    S3 -- OTLP/gRPC --\u003e C\n    M1 -- HTTP --\u003e S1\n    M1 -- HTTP --\u003e S2\n    M2 -- HTTP --\u003e S1\n    M2 -- HTTP --\u003e S3\n    M1 -- OTLP/gRPC --\u003e C\n    M2 -- OTLP/gRPC --\u003e C\n    C -- traces --\u003e T\n    C -- metrics --\u003e P\n    C -- logs --\u003e L\n    T --\u003e G\n    P --\u003e G\n    L --\u003e G\n```\n\nThe local development environment uses\n[Grafana OTel LGTM](https://github.com/grafana/docker-otel-lgtm), an all-in-one\ncontainer that bundles the OTel Collector, Loki, Grafana, Tempo, and Prometheus.\n\n## Service Specification\n\nAll service implementations must conform to this contract. The specification defines\nthe HTTP endpoints each service exposes, the request and response payloads, and the\nOpenTelemetry signals it must emit.\n\n### Endpoints\n\n| Endpoint   | Method | Description                                              |\n|------------|--------|----------------------------------------------------------|\n| `/health`  | GET    | Returns service health status                            |\n| `/forward` | GET    | Forwards GET requests to all configured URLs in parallel |\n\n### Endpoint Payloads\n\n#### `GET /health`\n\n**Response** `200 OK`\n\n```json\n{\n  \"status\": \"healthy\"\n}\n```\n\n#### `GET /forward`\n\nSends concurrent GET requests to every URL configured via the service's forward URL\nsetting and returns aggregated results. Individual request failures are included in the\nresponse rather than failing the entire batch.\n\n**Response** `200 OK`\n\n```json\n{\n  \"results\": [\n    {\n      \"url\": \"https://httpbin.org/get\",\n      \"status_code\": 200,\n      \"body\": \"...\",\n      \"duration_seconds\": 0.152\n    }\n  ]\n}\n```\n\n**Error Response** `500 Internal Server Error`\n\n```json\n{\n  \"error\": \"batch processing failed\"\n}\n```\n\n### Service Telemetry Signals\n\nEvery service must emit the following OpenTelemetry signals. Span names, metric\ninstrument names, and log export mechanisms must match across all implementations.\n\n| Signal  | Name                               | Description                               |\n|---------|------------------------------------|-------------------------------------------|\n| Traces  | HTTP server/client spans           | Auto-instrumented by the framework        |\n| Traces  | `forward.batch`, `forward.request` | Custom spans for forward operations       |\n| Metrics | `forward.requests`                 | Counter — total outbound forward requests |\n| Metrics | `forward.duration`                 | Histogram — outbound request duration (s) |\n| Logs    | Application logs                   | Structured logs exported via OTLP         |\n\n### Service Telemetry Details\n\n#### Traces\n\n| Endpoint   | Span Name         | Kind     | Description                                     |\n|------------|-------------------|----------|-------------------------------------------------|\n| All        | HTTP server spans | Server   | Auto-instrumented by the framework middleware   |\n| `/forward` | `forward.batch`   | Internal | Parent span covering the entire batch operation |\n| `/forward` | `forward.request` | Client   | One child span per forwarded URL                |\n\nContext propagation: W3C Trace Context, W3C Baggage.\n\n#### Metrics\n\n| Endpoint   | Name               | Type      | Unit | Attributes                     |\n|------------|--------------------|-----------|------|--------------------------------|\n| `/forward` | `forward.requests` | Counter   | `1`  | `server.address`, `url.scheme` |\n| `/forward` | `forward.duration` | Histogram | `s`  | `server.address`, `url.scheme` |\n\nHistogram bucket boundaries (seconds):\n`0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10`\n\n#### Logs\n\nStructured logs exported via OTLP. Every log record must include the active trace and\nspan ID for correlation with distributed traces.\n\n| Endpoint   | Message                            | Level | Context                  | Attributes                  |\n|------------|------------------------------------|-------|--------------------------|-----------------------------|\n| `/forward` | `Starting forward batch`           | Info  | Batch operation started  | `url.count`, `baggage`      |\n| `/forward` | `Forward batch completed`          | Info  | Batch succeeded          | `results.count`             |\n| `/forward` | `Batch processing failed`          | Error | Batch failed             | `error`                     |\n| `/forward` | `Failed to execute request`        | Error | HTTP request failed      | `error`                     |\n| `/forward` | `Unexpected error in forward task` | Error | Unexpected exception     | `error`                     |\n| `/forward` | `Upstream returned error status`   | Warn  | Upstream HTTP 4xx/5xx    | `http.response.status_code` |\n| `/forward` | `Request completed successfully`   | Info  | Single request succeeded | —                           |\n\n## Services\n\nThe table below lists available service implementations. Each implements the full\n[service specification](#service-specification) in a different language and framework.\nRefer to individual READMEs for language-specific configuration and development\ninstructions.\n\n| Service    | Language | Framework    | Docs                            |\n|------------|----------|--------------|---------------------------------|\n| go-gin     | Go       | Gin          | [README](./services/go-gin)     |\n| py-fastapi | Python   | FastAPI      | [README](./services/py-fastapi) |\n| cs-aspnet  | C#       | ASP.NET Core | [README](./services/cs-aspnet)  |\n\n## MCP Specification\n\nMCP (Model Context Protocol) servers expose tools for AI clients to invoke. In\nthis project, those tools call service endpoints over HTTP and emit OTel traces,\nmetrics, and logs for each invocation. All MCP server implementations must emit\nthe OpenTelemetry signals defined below.\n\n### MCP Telemetry Signals\n\nMetric attributes: `mcp.tool.name` on all instruments; `mcp.tool.status` on\n`mcp.tool.calls` and `mcp.tool.duration`.\n\n| Signal  | Name                | Type            | Description                                      |\n|---------|---------------------|-----------------|--------------------------------------------------|\n| Traces  | `mcp.tool/{name}`   | Internal span   | One span per tool invocation                     |\n| Traces  | HTTP client spans   | Client          | Auto-instrumented fetch() with W3C Trace Context |\n| Metrics | `mcp.tool.calls`    | Counter         | Total tool invocations                           |\n| Metrics | `mcp.tool.duration` | Histogram       | Tool execution duration (ms)                     |\n| Metrics | `mcp.tool.active`   | Up-Down Counter | In-flight tool calls                             |\n| Logs    | Application logs    | OTLP            | Structured logs exported via OTLP                |\n\n## MCP Servers\n\nThe table below lists available MCP server implementations. Each emits the\n[MCP telemetry signals](#mcp-telemetry-signals). Refer to individual READMEs\nfor language-specific configuration and development instructions.\n\n| Server | Language   | Transport              | Docs               |\n|--------|------------|------------------------|--------------------|\n| ts-mcp | TypeScript | stdio, streamable-http | [README](./mcp/ts) |\n\n## Observability Stack\n\nThe default Docker Compose profile starts the observability backend using\n[Grafana OTel LGTM](https://github.com/grafana/docker-otel-lgtm). This single\ncontainer bundles an OTel Collector, Loki, Grafana, Tempo, and Prometheus.\n\n| Component      | URL / Port              | Description                             |\n|----------------|-------------------------|-----------------------------------------|\n| Grafana        | http://localhost:3000   | Unified dashboards for all signal types |\n| Prometheus     | http://localhost:9000   | Metrics storage and querying            |\n| OTel Collector | `localhost:4317` (gRPC) | Receives OTLP traces, metrics, and logs |\n| Tempo          | (internal)              | Distributed trace storage               |\n| Loki           | (internal)              | Log aggregation and querying            |\n\nThe LGTM container uses its built-in OTel Collector and default backend configuration.\nThe Prometheus scrape configuration is customised via `prometheus/prometheus.yaml`,\nwhich is mounted into the container at startup. The `otel/collector/config.yaml`\ndefines a standalone collector configuration for use outside the LGTM image.\n\n## Project Structure\n\n```\n.\n├── .devcontainer         # Dev Container configurations (per-service)\n│   └── cs-aspnet         # Rider + .NET SDK\n├── CHANGELOG.md          # Release history\n├── CLAUDE.md\n├── Makefile              # Root-level build and orchestration targets\n├── README.md\n├── certs                 # TLS certificates (auto-generated by make up)\n├── docker\n│   └── dev               # Development container with Claude Code\n├── docker-compose.yml    # Service profiles and observability stack\n├── mcp                   # MCP server implementations\n│   └── ts                # TypeScript MCP server\n├── otel                  # OTel Collector configuration overrides\n│   └── collector\n├── prometheus            # Prometheus scrape configuration\n│   └── prometheus.yaml\n└── services\n    ├── cs-aspnet         # C# ASP.NET Core implementation\n    ├── go-gin            # Go-Gin implementation\n    └── py-fastapi        # Python-FastAPI implementation\n```\n\n## Local Development\n\nThe development environment runs entirely in Docker. The root Makefile provides targets\nfor starting the observability stack, running individual services, and executing tests.\n\n### Dev Containers\n\nService-specific Dev Container configurations are provided in `.devcontainer/`. These\nstart the service alongside the observability stack in a container with the language SDK\nand tooling pre-installed. Open a configuration with a Dev Container-compatible IDE to get\na fully configured development environment.\n\n| Service   | Path                      |\n|-----------|---------------------------|\n| cs-aspnet | `.devcontainer/cs-aspnet` |\n\n### Requirements\n\n- Docker\n- Docker Compose\n\n### Make Targets\n\n```bash\nmake up SERVICES=\u003cservice\u003e      # Start observability stack and the specified service\nmake down                       # Stop and remove all containers\nmake ci                         # Run CI checks for all services and MCP servers in Docker\nmake ci-\u003cname\u003e                  # Run CI checks for a service or MCP server (e.g., make ci-go-gin, make ci-ts-mcp)\nmake ci-mcp                     # Run CI checks for all MCP servers\nmake build                      # Build Docker images for all services and MCP servers\nmake docker-build-\u003cservice\u003e     # Build Docker image for a single service (e.g., make docker-build-go-gin)\nmake docker-build-mcp-\u003cserver\u003e  # Build Docker image for an MCP server (e.g., make docker-build-mcp-ts-mcp)\nmake docker-publish             # Build and publish all service images to registry\nmake docker-publish-\u003cservice\u003e   # Build and publish a service image (e.g., make docker-publish-go-gin)\nmake lint-actions               # Lint GitHub Actions workflows\n```\n\nThe `up` target always starts the observability stack (lgtm). Pass\n`SERVICES=\u003cservice\u003e` to additionally start a service container — for example,\n`make up SERVICES=go-gin`.\n\n## TLS / Certificates\n\nThe `certs/` directory contains self-signed TLS certificates for secure communication\nbetween services and the OTel Collector. Running `make up` generates these automatically\nif they do not exist.\n\n| File                 | Purpose                      |\n|----------------------|------------------------------|\n| `ca.crt`             | Certificate authority        |\n| `otel-collector.crt` | Collector server certificate |\n| `otel-collector.key` | Collector private key        |\n\n\u003e **Warning:** These certificates are for local development only. Do not use them in\n\u003e production environments.\n\n## Troubleshooting\n\nCommon issues when running services with the observability stack.\n\n| Problem                          | Cause                              | Fix                                                           |\n|----------------------------------|------------------------------------|---------------------------------------------------------------|\n| No traces or metrics in Grafana  | Collector endpoint not reachable   | Verify `OTEL_EXPORTER_OTLP_ENDPOINT` and network connectivity |\n| `/forward` returns empty results | No forward URLs configured         | Set the service's forward URLs env var or CLI flag            |\n| TLS handshake errors             | Connecting to a non-TLS collector  | Set `OTEL_EXPORTER_OTLP_INSECURE=true`                        |\n| Upstream request timeouts        | Target URL too slow or unreachable | Check target URLs; timeout values are service-specific        |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcameronxie%2Fotel-polyglot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcameronxie%2Fotel-polyglot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcameronxie%2Fotel-polyglot/lists"}