Help us shape the future of Go! Take the 2026 Go Developer Survey

otelcontext

command module
v0.6.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 63 Imported by: 0

README

OtelContext

OtelContext

Turn traces, logs, and metrics into one clear view of your services.

Self-hosted OpenTelemetry collection, incident triage, and service mapping in one Go binary.

Latest release CI status Security status MIT license

OtelContext helps you answer the question that starts most incidents: what is broken, and what does it affect? Send it standard OpenTelemetry data and it connects service relationships, errors, latency, logs, and traces in a map built for investigation.

It starts with SQLite and no external services. When you need more, the main telemetry database can use PostgreSQL, MySQL, or SQL Server.

[!IMPORTANT] OtelContext is pre-1.0. Read the changelog before upgrading.

What you get

  • A live service map that shows dependencies, health, latency, and active anomalies.
  • Incident context in one place with related traces, logs, metrics, and impact analysis.
  • Ten MCP tools for investigating your system from an AI client or coding agent.
  • Standard OTLP input over gRPC and HTTP, so existing SDKs and Collectors can send data directly.
  • A simple first run with one binary and a local SQLite database.
  • Self-hosted data with retention controls, health probes, and Prometheus metrics.

Quick start

1. Install a release

Download the archive for your platform from GitHub Releases, or install the latest release with Go:

go install github.com/RandomCodeSpace/otelcontext@latest

2. Start OtelContext

otelcontext

That is enough for a local trial. OtelContext creates OtelContext.db, listens for OTLP gRPC on 4317, and serves the UI and HTTP endpoints on 8080.

Open http://localhost:8080.

Authentication is off by default so the browser UI works immediately. Keep this first run on your machine. Before exposing it to a network, follow Secure a deployment.

3. Send telemetry

Point an OpenTelemetry SDK or Collector at either endpoint:

Protocol Endpoint
OTLP gRPC localhost:4317
OTLP HTTP http://localhost:8080/v1/traces, /v1/logs, or /v1/metrics

To confirm the connection without setting up an application, send one sample error log:

curl -fsS http://localhost:8080/v1/logs \
  -H "Content-Type: application/json" \
  -d "{
    \"resourceLogs\": [{
      \"resource\": {\"attributes\": [{
        \"key\": \"service.name\",
        \"value\": {\"stringValue\": \"readme-demo\"}
      }]},
      \"scopeLogs\": [{\"logRecords\": [{
        \"timeUnixNano\": \"$(date +%s)000000000\",
        \"severityNumber\": 17,
        \"severityText\": \"ERROR\",
        \"body\": {\"stringValue\": \"OtelContext is receiving data\"}
      }]}]
    }]
  }"

ERROR is used because the default storage threshold keeps WARN and ERROR logs.

OpenTelemetry Collector example

Save this as part of your Collector configuration. Replace otelcontext with the host or service name running OtelContext.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch: {}

exporters:
  otlp/otelcontext:
    endpoint: otelcontext:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/otelcontext]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/otelcontext]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/otelcontext]

For an authenticated deployment, add an authorization bearer header and, if needed, x-tenant-id under the exporter.

Service and instance map

The web UI has three pages, linked from the top bar:

  • Overview (/): health summary, KPIs, the services that need attention, a domain map, services by health with one pip per instance, and storage and ingestion figures.
  • Service map (/service-map): services, instances and hosts in three lanes.
  • Explorer (/explorer): the GraphRAG application map with root-cause and impact analysis.

On the Service map, choose an environment and time range; the page refreshes every minute or on demand. Select a service, instance, host or call edge to inspect its identity, traffic, average latency and errors. Focus a service to load its one- or two-hop neighborhood. Switch to Instances to show the individual instances with their instance-to-instance call edges, plus a "runs on" link from each instance to its host. The view is in the URL (?service_name=, ?namespace=, ?mode=, ?range=), so it can be shared.

Send these resource attributes from each application:

Attribute Purpose
service.name Logical service, shared by its replicas
service.namespace Separates services with the same name in different applications
service.instance.id Distinguishes each running copy
deployment.environment.name Separates Dev, Test, and other environments
host.id or host.name Displays the latest reported host placement

The legacy deployment.environment attribute is also accepted. Missing environments appear under Unknown; host names never determine an environment. Calls require trace parent/child context propagated between applications. Sharing a host creates no call edge. Calls between two instances of the same service remain visible in the instance view.

The map works with SQLite and PostgreSQL in both legacy and aggregate modes. Run otelcontext migrate up with the deployment's database configuration before starting the new binary: additive schema version 6 creates topology_entities and topology_buckets. Existing history is preserved, but old telemetry cannot supply instance identities that were never retained. MySQL and SQL Server retain the existing explorer; the new topology API reports that this projection is unavailable.

Topology is a best-effort visualization, built from accepted traces before exemplar sampling. It stores five-minute summaries in the main database, with the configured HOT_RETENTION_DAYS. Summaries flush every five seconds and on graceful shutdown; a crash may lose the unflushed interval. Parent joins and retry deduplication last five minutes in memory and reset on restart. Host-only identities are marked unresolved; explicit instance IDs, or a host plus workload identity, distinguish copies. Metrics-only and logs-only resources can appear without invented call edges or trace rates.

Reads are limited to 24 hours, 40 displayed nodes by default (100 maximum), 200 edges, and 50,000 stored summary rows per request. Environment and neighborhood filters run before the storage read. The process retains at most 20,000 node/edge identities and 100,000 correlation spans. The UI reports truncated results, incomplete identities, and limited evidence. These bounds do not establish a load-tested performance or disk-size guarantee.

Storage & ingestion separates the main database, aggregate database, and both databases' WAL/SHM files. Sizes include indexes and reusable space, not just retained payloads. Export counters count successful OTLP requests, including partial-success responses, since process start; they are not event counts. Server ACK p99 uses the latest 1,024 successful exports and excludes network time. Memory is Go heap allocation. These stats are server-wide; topology reads remain tenant-scoped. The backing endpoints are /api/topology/catalog, /api/topology/graph, and /api/stats/runtime.

Query ingested metrics with PromQL

Local database (no Prometheus server)

Enable the embedded upstream PromQL engine over original metric samples stored in OtelContext's main SQLite or PostgreSQL database:

PROMQL_LOCAL_ENABLED=true
PROMETHEUS_URL=
PROMETHEUS_TENANT_ID=default
HOT_RETENTION_DAYS=7

Apply otelcontext migrate up with the same database configuration before starting the new binary. Schema version 5 adds promql_series and promql_samples; existing tables and summarized history are preserved. The usual database backup includes the new tables. Production startup checks the schema without migrating it.

The local backend needs no separate process, port, or server. OTLP metric ACKs require a successful database commit, and local storage participates in readiness. It reuses the existing PROMETHEUS_TIMEOUT_MS and concurrency settings listed below. PROMETHEUS_REQUIRED=false does not make local persistence optional. Local mode and PROMETHEUS_URL are mutually exclusive. As with the external backend, this initial implementation binds queries and metric writes to PROMETHEUS_TENANT_ID (default: DEFAULT_TENANT); other tenants retain their existing ingestion path and cannot query this backend.

Original samples are retained from activation onward. Older metric buckets and aggregate sketches cannot reconstruct original samples, counter resets, or labels, so they are not exposed as fabricated PromQL history. Queries use a consistent database snapshot and exclude expired samples even before the hourly retention worker deletes them. Local retention follows HOT_RETENTION_DAYS, independently of raw trace/log exemplar retention.

The upstream Prometheus 3.15.0 Go engine and OTLP converter (Apache-2.0) provide query and conversion semantics. Names and labels retain UTF-8/dots. Resource attributes and instrumentation scope are preserved as series labels. Gauges, cumulative counters, classic histogram buckets, native exponential histograms, and summary series are supported. Delta sums and histograms are rejected; configure cumulative SDK export. Queries use millisecond timestamps and the standard five-minute lookback. Identical series/timestamp retries are idempotent; a different value at the same timestamp rejects the whole batch.

Local requests have additional fixed work bounds: 10,000 input datapoints and 10,000 translated samples per export; 8 MiB of translated sample/label payload; 128 labels and 16 KiB of labels per series; 64 KiB per encoded sample. Samples outside retention or over five minutes in the future are rejected. Queries inspect at most 10,000 candidate series across selectors, select at most 1,000 series per selector, read at most 100,000 samples / 32 MiB of payload, and use the engine's 100,000-sample evaluation bound. Excess work returns an explicit error rather than partial aggregates. Narrow selectors or query intervals when these bounds are reached. HTTP range queries also enforce 11,000 steps.

Full-resolution metric storage consumes additional database space, including indexes and WAL. These request limits are not a total disk quota. Existing aggregate-mode capacity measurements do not establish this backend's storage or ingestion performance; size retention for the actual metric volume.

External backend (optional alternative)

Set PROMETHEUS_URL with PROMQL_LOCAL_ENABLED=false to use an external backend. With both disabled, ingestion and metric reads keep their legacy local paths. The /metrics scrape reports OtelContext's own health and counters; both PromQL backends query application and host metrics received through OTLP.

Configure Prometheus with its native OTLP receiver enabled and this configuration:

global:
  scrape_interval: 1m
otlp:
  translation_strategy: NoTranslation
  promote_resource_attributes:
    - service.name
    - service.instance.id
    - host.id
    - host.name

Start Prometheus with --web.enable-otlp-receiver, set its retention explicitly with --storage.tsdb.retention.time=7d, and bound query work with --query.max-samples=100000 --query.timeout=10s. The reference integration test uses Prometheus 3.15.0. Prometheus owns metric retention and storage; OtelContext's local retention settings do not control that backend.

Then configure OtelContext:

PROMETHEUS_URL=http://127.0.0.1:9090
PROMETHEUS_TENANT_ID=default
PROMETHEUS_REQUIRED=false
Setting Default Meaning
PROMETHEUS_URL empty HTTP(S) base URL; no embedded credentials, query, or fragment
PROMETHEUS_TENANT_ID DEFAULT_TENANT The only tenant allowed to write or query this backend
PROMETHEUS_REQUIRED false Require backend acceptance before local metric processing and include the backend in /ready
PROMETHEUS_TIMEOUT_MS 10000 Request deadline, including ingest admission; range 1..30000
PROMETHEUS_MAX_CONCURRENT 8 Active queries and metadata reads; range 1..64
PROMETHEUS_MAX_INGEST_CONCURRENT 16 Separate active metric forwarding slots; range 1..256
PROMETHEUS_MAX_INGEST_WAITERS 256 Exporters waiting for an ingest slot; range 1..4096

Use a dedicated backend for the configured tenant. Tenant binding gates access to the entire backend; it does not add tenant label filters to PromQL. Existing tenant precedence applies. Other tenants keep local ingestion and metric reads, and their PromQL requests are refused before reaching the backend.

Forwarding preserves the original OTLP metric names, resource and point attributes, timestamps, units, histogram payloads, and exemplars before local projection. Send cumulative sums and histograms. Delta sums and histograms are refused by this backend contract. No delta conversion or metric renaming runs in OtelContext. With NoTranslation, dotted names and label keys need quoted selectors, for example {__name__="system.cpu.utilization","host.name"="node-a"}. Promote additional resource attributes in Prometheus if queries need them as labels.

In optional mode, forwarding failures leave local ingestion running and return an OTLP partial-success warning; missing backend data is not queued for replay. In required mode, backend outages return retryable gRPC RESOURCE_EXHAUSTED or HTTP 429 before local accounting. Invalid backend input returns gRPC INVALID_ARGUMENT or HTTP 400. A successful backend write followed by a local projection failure returns a warning when the backend accepted all resources. Backend partial-success rejections are preserved and must not be retried. Local projection skips the forwarded resources in that case because the backend does not identify individual rejected points. /ready probes backend availability only when required; it does not validate retention, OTLP enablement, or translation.

HTTP query endpoints accept GET parameters or POST form data:

curl -fsSG http://localhost:8080/api/v1/query \
  --data-urlencode 'query={__name__="system.cpu.utilization","host.name"="node-a"}'
curl -fsSG http://localhost:8080/api/v1/query_range \
  --data-urlencode 'query={__name__="system.cpu.utilization"}' \
  --data-urlencode 'start=2026-10-18T00:00:00Z' \
  --data-urlencode 'end=2026-10-19T00:00:00Z' \
  --data-urlencode 'step=5m'

Responses preserve Prometheus envelopes, labels, timestamps, result types, warnings, and backend errors. GET /api/metadata/metrics discovers retained metric names, with optional service_name, start, and end filters. The existing GET /api/metrics?name=... view returns evaluated scalar samples for the bound tenant, marked sampled with OtelContext-Metric-Source: local or prometheus. It cannot flatten native histograms; use PromQL for those. Host service names use host/<host.id>, falling back to host.name when no host ID is available.

MCP advertises promql_query and promql_query_range through tools/list. They return uncached Prometheus envelopes and share MCP's concurrency, timeout, and response-size bounds. resources/list advertises the readable OtelContext://system/graph resource; the internal scrape is not an MCP resource.

The client caps query parameters at 64 KiB, query ranges at seven days, returned series at 1,000, and backend responses at 8 MiB. Each forwarded request is capped at 64 MiB; active and waiting exports share a 128 MiB protobuf wire-size budget. Encoding happens after admission. This budget does not cover incoming network decoding or total process memory. Readiness has its own slot. MCP further caps query text at 16 KiB, evaluation points at 11,000 per series, and results at its existing 4 MiB response limit. Prometheus must also enforce its own query and storage limits. This optional feature does not certify 200-service production capacity.

Investigate with MCP

Connect an MCP client to http://localhost:8080/mcp to investigate the same telemetry from an agent. The tools can:

  • Build an anomaly timeline.
  • Show the service map and service health.
  • Analyze likely root causes and downstream impact.
  • Reconstruct a trace graph.
  • Search logs with bounded results.

Trace-based answers say whether the supporting exemplar is complete, partial, or no longer retained. OtelContext does not present a partial trace as the whole story.

Upgrade without guessing

The same binary can check and upgrade its database before it starts any receiver or background work:

./otelcontext migrate status
./otelcontext migrate up

If the database came from an older release that predates migration tracking, identify it once before upgrading:

./otelcontext migrate baseline --from v0.3.1
# or: --from v0.4.0-beta.2
./otelcontext migrate up

The baseline command validates the database first; it does not repair or guess. Create a complete OtelContext backup before upgrading, and keep the previous signed binary until migrate status reports result=ready. Versioned production checks are available for SQLite and unpartitioned PostgreSQL 16. MySQL, SQL Server, and PostgreSQL daily partitioning keep their existing preview AutoMigrate path.

For a one-host Linux deployment, use the shipped systemd unit and environment example and follow the install, upgrade, and rollback runbook. Direct execution remains supported for local use.

Back up and restore

After stopping OtelContext cleanly, use the same binary and environment as the service:

otelcontext backup create --out /absolute/path/to/backups

The published bundle keeps the main database, any mode-required aggregate database, the dead-letter queue, generated TLS identity, and a hashed manifest together. Restore into new database and sidecar paths; the command will not overwrite the source or an existing target:

otelcontext backup restore --bundle /absolute/path/to/backups/otelcontext-backup-...

Restore starts the same candidate briefly, waits for /ready, and shuts it down again. Keep the old binary, configuration, data, and bundle until the restored deployment passes your checks. The backup and restore runbook covers database-specific tools, fresh-target setup, validation, and rollback.

Secure a deployment

With no authentication or TLS settings configured, production mode starts without either:

APP_ENV=production ./otelcontext

Authentication is optional. Enable it when the deployment boundary requires a credential:

  • API_KEY for a shared operator credential.
  • AUTH_TRUST_EXTERNAL=true when a trusted reverse proxy owns authentication and tenant identity.

Authenticated example:

export API_KEY="$(openssl rand -hex 32)"
./otelcontext

Clients then send Authorization: Bearer <key>. Authentication and TLS are independently optional at runtime; configure each one when the deployment boundary requires it.

The browser UI does not currently store an API key. For an authenticated browser deployment, put OtelContext behind a same-origin proxy that authenticates the user and injects the credential for REST, MCP, and WebSocket traffic.

See the operations guide for database, TLS, retention, proxy, and health-check setup.

Common configuration

OtelContext reads environment variables and an optional .env file in its working directory.

Setting Default Use it for
HTTP_PORT 8080 UI, REST, OTLP HTTP, MCP, WebSockets, and probes
GRPC_PORT 4317 OTLP gRPC
DB_DRIVER sqlite sqlite, postgres, mysql, or sqlserver
DB_DSN OtelContext.db Database connection string
API_KEY empty Shared bearer authentication
DEFAULT_TENANT default Scope used when no trusted tenant is supplied
HOT_RETENTION_DAYS 7 Main retention horizon
STORE_MIN_SEVERITY WARN Lowest log severity stored in the main database
AGGREGATE_MODE legacy legacy, aggregate-shadow, or aggregate

Start with .env.example. The default legacy mode is the right choice for a first run. Aggregate modes are for measured high-volume deployments; review the aggregate gate report before enabling them.

Build from source

Use the Go version in go.mod:

git clone https://github.com/RandomCodeSpace/otelcontext.git
cd otelcontext

CGO_ENABLED=0 go build -o otelcontext .

The client-rendered UI is committed as plain HTML, CSS, and JavaScript and is embedded automatically. There is no Node.js install or frontend build step.

Run the core checks with:

go build ./...
go vet ./...
go test -race -timeout 180s ./...

License

OtelContext is available under the MIT License.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
aggregate
Package aggregate implements the aggregate-first accounting engine: every accepted telemetry point is reduced into a small number of series deltas before sampling, so aggregate counts describe traffic rather than the sampling rate.
Package aggregate implements the aggregate-first accounting engine: every accepted telemetry point is reduced into a small number of series deltas before sampling, so aggregate counts describe traffic rather than the sampling rate.
ai
api
api/views
Package views provides explicit JSON view models for the HTTP API.
Package views provides explicit JSON view models for the HTTP API.
authn
Package authn holds the transport-independent authentication primitives shared by the HTTP, WebSocket, and gRPC surfaces: the tenant key store and the authenticated principal that travels on a request context.
Package authn holds the transport-independent authentication primitives shared by the HTTP, WebSocket, and gRPC surfaces: the tenant key store and the authenticated principal that travels on a request context.
backup
Package backup owns OtelContext's offline, manifest-bound backup bundle.
Package backup owns OtelContext's offline, manifest-bound backup bundle.
graph
Package graph provides an in-memory service dependency graph rebuilt periodically from recent span data.
Package graph provides an in-memory service dependency graph rebuilt periodically from recent span data.
graphrag
Package graphrag provides a layered in-memory graph for real-time observability retrieval — error chains, root cause analysis, impact analysis.
Package graphrag provides a layered in-memory graph for real-time observability retrieval — error chains, root cause analysis, impact analysis.
httpconst
Package httpconst centralises HTTP header names and content-type strings shared by the API, MCP, and OTLP-HTTP handlers so the same literal isn't duplicated across packages.
Package httpconst centralises HTTP header names and content-type strings shared by the API, MCP, and OTLP-HTTP handlers so the same literal isn't duplicated across packages.
latency
Package latency defines the shared provenance vocabulary for percentile values.
Package latency defines the shared provenance vocabulary for percentile values.
mcp
Package mcp implements the HTTP Streamable MCP (Model Context Protocol) server over JSON-RPC 2.0 with SSE streaming, allowing any AI agent (Claude, GPT, Cursor, etc.) to discover and call OtelContext tools natively.
Package mcp implements the HTTP Streamable MCP (Model Context Protocol) server over JSON-RPC 2.0 with SSE streaming, allowing any AI agent (Claude, GPT, Cursor, etc.) to discover and call OtelContext tools natively.
membudget
Package membudget detects the memory budget available to the process — (in order) cgroup v2, cgroup v1, then host RAM from /proc/meminfo — so consumers can scale their allocations against the actual quota instead of hardcoding sizes.
Package membudget detects the memory budget available to the process — (in order) cgroup v2, cgroup v1, then host RAM from /proc/meminfo — so consumers can scale their allocations against the actual quota instead of hardcoding sizes.
migrate
Package migrate owns OtelContext's ordered main-database schema contract.
Package migrate owns OtelContext's ordered main-database schema contract.
prometheus
Package prometheus provides tenant-bound OTLP metric storage and PromQL, using either the local database and embedded engine or an external server.
Package prometheus provides tenant-bound OTLP metric storage and PromQL, using either the local database and embedded engine or an external server.
tls
Package tlsbootstrap provides zero-friction self-signed certificate generation for development and internal deployments.
Package tlsbootstrap provides zero-friction self-signed certificate generation for development and internal deployments.
topology
Package topology owns the mode-selected service-topology read contract.
Package topology owns the mode-selected service-topology read contract.
tsdb
Package tsdb provides an in-memory ring buffer for per-metric sliding windows with pre-computed aggregates.
Package tsdb provides an in-memory ring buffer for per-metric sliding windows with pre-computed aggregates.
ui
test
authservice command
browser
Package browser contains the browser-tagged black-box UI smoke test.
Package browser contains the browser-tagged black-box UI smoke test.
gate/gatecore
Package gatecore holds the pure logic of the seven-day aggregate release gate (#202): configuration, parsers, threshold evaluation, the projection fit, the result schema, and the Markdown renderer.
Package gatecore holds the pure logic of the seven-day aggregate release gate (#202): configuration, parsers, threshold evaluation, the projection fit, the result schema, and the Markdown renderer.
orderservice command
paymentservice command
production/rawcapacity command
Command rawcapacity is the production-repository side of simulate.py.
Command rawcapacity is the production-repository side of simulate.py.
production/rawseed command
Command rawseed creates bounded correlated raw exemplars in a NEW fixture directory using the application's versioned migrations and repository APIs.
Command rawseed creates bounded correlated raw exemplars in a NEW fixture directory using the application's versioned migrations and repository APIs.
readproof
Package readproof holds the untagged half of the read-latency proof (issue #289, decision #281): the result schema, exact ordered percentiles, threshold evaluation and the JSON writer.
Package readproof holds the untagged half of the read-latency proof (issue #289, decision #281): the result schema, exact ordered percentiles, threshold evaluation and the JSON writer.
releasecandidate command
Command releasecandidate verifies signed draft release assets and assembles the release-candidate-v1.json evidence index consumed by the release workflow.
Command releasecandidate verifies signed draft release assets and assembles the release-candidate-v1.json evidence index consumed by the release workflow.
shippingservice command
userservice command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL