Skip to content

Commit 7765e33

Browse files
committed
Otel viewer: Python/JS web app for OpenTelemetry trace viewing
1 parent 685ec8e commit 7765e33

File tree

16 files changed

+1932
-0
lines changed

16 files changed

+1932
-0
lines changed

rewatch/otel-viewer/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__pycache__/
2+
.venv/
3+
.ruff_cache/
4+
traces.db
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

rewatch/otel-viewer/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# otel-viewer
2+
3+
A local OTLP trace viewer for rewatch development. Receives OpenTelemetry traces, stores them in SQLite, and provides a web UI for browsing and exporting span data.
4+
5+
This tool was vibe-coded with Claude and replaces the previous Jaeger-based workflow.
6+
7+
## Why not Jaeger?
8+
9+
We originally used the [Jaeger all-in-one](https://www.jaegertracing.io/) Docker image to view OTLP traces. It works, but has several drawbacks for our use case:
10+
11+
- **Requires Docker** — an extra dependency that not every contributor has running, and the image is heavy for what we need.
12+
- **Generic UI** — Jaeger is designed for production distributed tracing across many services. Its UI is cluttered with service selectors, operation dropdowns, and tag filters that are irrelevant when you're just debugging a single rewatch build or LSP session.
13+
- **No LLM export** — we frequently want to copy a subtree of spans (with attributes and events) as JSON to paste into an LLM conversation for debugging. Jaeger has no way to do this.
14+
- **No deep links to traces** — Jaeger generates opaque URLs that don't map cleanly to trace IDs. Our viewer gives each trace a permalink at `/traces/{trace_id}`.
15+
16+
otel-viewer is a single Python file with a SQLite database. It starts instantly, needs no Docker, and has a focused UI tailored to rewatch development.
17+
18+
## Setup
19+
20+
Requires [uv](https://docs.astral.sh/uv/), a fast Python package and project manager written in Rust. It replaces `pip`, `virtualenv`, and `pyenv` in a single tool — it handles installing Python itself, creating virtual environments, and installing dependencies. If you've never worked with Python before, `uv` is the only thing you need to install.
21+
22+
On macOS:
23+
24+
```bash
25+
brew install uv
26+
```
27+
28+
Then install the project dependencies:
29+
30+
```bash
31+
cd rewatch/otel-viewer
32+
uv sync
33+
```
34+
35+
## Usage
36+
37+
Start the viewer:
38+
39+
```bash
40+
uv run python server.py
41+
```
42+
43+
This starts a server on port 4707 that acts as both an OTLP collector and a web UI.
44+
45+
Run rewatch with tracing pointed at the viewer:
46+
47+
```bash
48+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4707 cargo run --manifest-path rewatch/Cargo.toml -- build
49+
```
50+
51+
Open http://localhost:4707 in your browser to browse traces.
52+
53+
## Features
54+
55+
- **Trace list**: Shows all captured traces with timestamp, root span name, duration, and span count.
56+
- **Lazy-loaded span tree**: Click a trace to see its root spans. Expand spans to load children on demand.
57+
- **Export for LLM**: Select spans via checkboxes, click "Copy for LLM" to copy the full subtree (including all descendants) as JSON to your clipboard.
58+
59+
## API
60+
61+
The following endpoints are available for programmatic access:
62+
63+
- `GET /api/traces` — list all traces
64+
- `GET /api/traces/{trace_id}/roots` — root spans of a trace
65+
- `GET /api/spans/{span_id}/children` — direct children of a span
66+
- `GET /api/spans/{span_id}` — full detail for a single span (attributes, events)
67+
- `POST /api/export` — export selected spans with full subtrees; body: `{"span_ids": ["id1", "id2"]}`
68+
69+
## How traces work
70+
71+
Only **finished traces** are shown in the UI. A trace is considered finished when its root span (a span with no parent) has been received. This means:
72+
73+
- Traces from completed `build`, `clean`, or `format` commands appear immediately.
74+
- LSP traces only appear after the LSP server shuts down cleanly and flushes its telemetry. If the LSP process is killed without a graceful shutdown (e.g. `kill -9`), the root `rewatch.lsp` span is never sent and the trace won't be listed.
75+
- During development, use `--watch` mode (`uv run python server.py --watch`) and restart the LSP server cleanly (via editor restart or SIGTERM) to see its trace.
76+
77+
Spans from in-progress or interrupted traces are still stored in the database but hidden from the trace list.
78+
79+
## Viewing test traces
80+
81+
The rewatch integration tests (`tests/rewatch_tests/`) can forward their OTEL traces to this viewer. Start the viewer, then run tests with `OTEL_VIEWER_ENDPOINT` set:
82+
83+
```bash
84+
# Terminal 1
85+
cd rewatch/otel-viewer
86+
uv run python server.py
87+
88+
# Terminal 2
89+
cd tests/rewatch_tests
90+
OTEL_VIEWER_ENDPOINT=http://localhost:4707 yarn test tests/build.test.mjs
91+
```
92+
93+
Test traces appear in the viewer alongside manually triggered builds. The forwarding is fire-and-forget — if the viewer isn't running, tests pass normally.
94+
95+
## Data
96+
97+
Traces are stored in `traces.db` (SQLite) in the `otel-viewer` directory. Delete this file to start fresh.

0 commit comments

Comments
 (0)