https://github.com/mashiro/otelop
Local OTel viewer for dev loops — single binary, in-memory, browser UI.
https://github.com/mashiro/otelop
developer-tools go logs metrics observability opentelemetry otel otlp react traces
Last synced: 4 days ago
JSON representation
Local OTel viewer for dev loops — single binary, in-memory, browser UI.
- Host: GitHub
- URL: https://github.com/mashiro/otelop
- Owner: mashiro
- License: mit
- Created: 2026-04-09T11:14:43.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-09T01:44:44.000Z (8 days ago)
- Last Synced: 2026-06-09T03:22:42.109Z (8 days ago)
- Topics: developer-tools, go, logs, metrics, observability, opentelemetry, otel, otlp, react, traces
- Language: Go
- Size: 2.16 MB
- Stars: 11
- Watchers: 0
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README

# otelop
A local OpenTelemetry viewer for traces, metrics, and logs.
Single binary, in-memory, browser UI.
[](go.mod)
[](frontend/package.json)
[](LICENSE)
---
## What it is
`otelop` runs a local OTLP receiver and shows whatever it gets in a browser. No Docker, no database, no Jaeger/Prometheus/Loki to wire up. Start the binary, point your app at it, open the page.
It's meant for the loop where you're writing instrumentation and just want to see what came through.

## Features
- Single binary with the frontend embedded
- OTLP gRPC and HTTP receivers (built-in OpenTelemetry Collector)
- Optional OTLP forwarding to one upstream endpoint
- Traces, metrics, and logs in one UI
- Live updates over WebSocket
- GraphQL API at `/graphql`
- MCP server, so agents can query the same data
- In-memory ring buffers — no persistence, no setup
## Install
With Go:
```bash
go install github.com/mashiro/otelop/cmd/otelop@latest
```
With mise:
```bash
mise use -g github:mashiro/otelop
```
With Docker:
```bash
docker run --rm -p 4317:4317 -p 4318:4318 -p 4319:4319 ghcr.io/mashiro/otelop:latest
```
## Quick start
```bash
otelop start
```
This detaches into the background so your terminal stays free. Use `otelop status` to see what it is listening on and `otelop stop` to shut it down. Pass `--foreground` (or `-f`) if you want logs in the current terminal.
Then point your app at it:
```bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 your-app
```
And open .
### With AI coding agents
Any AI coding agent that supports OpenTelemetry can export to `otelop`, so you can watch the agent's API calls, tool runs, and prompts live. For example:
- [Claude Code](https://docs.claude.com/en/docs/claude-code/monitoring-usage)
- [Codex](https://developers.openai.com/codex/config-advanced)
## Endpoints
| Port | Purpose |
|---|---|
| `4319` | Web UI + GraphQL |
| `4317` | OTLP gRPC receiver |
| `4318` | OTLP HTTP receiver |
## Commands
```
otelop start [flags] # launch in the background (default), or foreground with -f
otelop stop # stop the background server
otelop status # show PID, listen addresses, and buffered counts
otelop version
```
`start` flags:
```
--foreground, -f run in the foreground instead of detaching
--http Web UI listen address (default :4319)
--otlp-grpc OTLP gRPC receiver endpoint (default 0.0.0.0:4317)
--otlp-http OTLP HTTP receiver endpoint (default 0.0.0.0:4318)
--proxy-url upstream OTLP endpoint for forwarding
--proxy-protocol upstream OTLP protocol (grpc|http)
--proxy-auth-type upstream OTLP auth type (bearer|basic|headers)
--proxy-auth-token upstream bearer token
--proxy-auth-username upstream basic auth username
--proxy-auth-password upstream basic auth password
--proxy-header upstream header (repeatable key=value)
--trace-cap max traces in memory (default 1000)
--metric-cap max metric series in memory (default 3000)
--log-cap max log entries in memory (default 5000)
--max-data-points max data points per series (default 1000)
--log-level debug|info|warn|error (default warn)
```
PID, log, and metadata files live in `$XDG_STATE_HOME/otelop/` (defaults to `~/.local/state/otelop/`).
## Configuration
Every `start` flag can be set three ways. Higher precedence wins:
1. CLI flag (`otelop start --http :4319`)
2. Environment variable (`OTELOP_HTTP=:4319 otelop start`)
3. TOML config file at `$XDG_CONFIG_HOME/otelop/config.toml` (defaults to `~/.config/otelop/config.toml`; override with `OTELOP_CONFIG_FILE=/path/to/config.toml`)
Example `~/.config/otelop/config.toml`:
```toml
http = ":4319"
otlp_grpc = "0.0.0.0:4317"
otlp_http = "0.0.0.0:4318"
trace_cap = 1000
metric_cap = 3000
log_cap = 5000
max_data_points = 1000
log_level = "warn"
debug = false
[proxy]
url = "https://collector.internal:4318"
protocol = "http"
[proxy.auth]
type = "bearer"
token = "replace-me"
```
The matching environment variables are `OTELOP_HTTP`, `OTELOP_OTLP_GRPC`, `OTELOP_OTLP_HTTP`, `OTELOP_PROXY_URL`, `OTELOP_PROXY_PROTOCOL`, `OTELOP_PROXY_AUTH_TYPE`, `OTELOP_PROXY_AUTH_TOKEN`, `OTELOP_PROXY_AUTH_USERNAME`, `OTELOP_PROXY_AUTH_PASSWORD`, `OTELOP_PROXY_HEADERS`, `OTELOP_TRACE_CAP`, `OTELOP_METRIC_CAP`, `OTELOP_LOG_CAP`, `OTELOP_MAX_DATA_POINTS`, `OTELOP_LOG_LEVEL`, and `OTELOP_DEBUG`.
When proxying is enabled, `otelop` still buffers incoming telemetry locally for the UI and also forwards the same traces, metrics, and logs to the configured upstream OTLP endpoint.
`proxy.auth.type` supports:
- `bearer`: sends `Authorization: Bearer `
- `basic`: sends `Authorization: Basic `
- `headers`: sends the exact headers configured under `[proxy.auth.headers]` or `--proxy-header`
Do not embed credentials in `proxy.url`; `otelop` rejects URLs with userinfo such as `https://user:pass@example.com`.
## License
MIT