CO

ctrlspice/otel-desktop-viewer

Developer tools
1280 stars 品質 46 トレンド 46

otel-desktop-viewer is a CLI tool for receiving OpenTelemetry traces while working on your local machine.

概要

otel-desktop-viewer is a local OpenTelemetry viewer. It uses the OpenTelemetry Collector, DuckDB, and Svelte. ~~Also, it has a dark mode~~ Y'all. I added another dark mode. It has dark modes now. Once running, the UI is at localhost:8000. The OTLP receivers listen on localhost:4317 (gRPC) and localhost:4318 (HTTP). Download a pre-built binary for your platform from Releases. On Windows, unzip the archive and run otel-desktop-viewer.exe. release binaries require (Ubuntu 24.04+, Debian 13+, Fedora 40+). This applies to the tarballs, the .deb/.rpm packages, and the Docker images. Stable releases include .deb and .rpm packages on GemFury. Building from source requires Go and CGO.

README

otel-desktop-viewer

Hello there.

otel-desktop-viewer is a local OpenTelemetry viewer. It uses the OpenTelemetry Collector, DuckDB, and Svelte.

Also, it has a dark mode
Y’all.
I added another dark mode.
It has two dark modes now.

Table of Contents

Screenshots

Traces

Metrics

Logs

Getting Started

Once running, the UI is at localhost:8000. The OTLP receivers listen on localhost:4317 (gRPC) and localhost:4318 (HTTP).

Via Homebrew Cask

On macOS:

brew tap ctrlspice/otel-desktop-viewer
brew install --cask otel-desktop-viewer

Via GitHub Releases

Download a pre-built binary for your platform from Releases.

Platform Architecture File
macOS Apple Silicon (M1–M4) otel-desktop-viewer_darwin_arm64.tar.gz
macOS Intel otel-desktop-viewer_darwin_amd64.tar.gz
Linux x86_64 otel-desktop-viewer_linux_amd64.tar.gz
Linux arm64 otel-desktop-viewer_linux_arm64.tar.gz
Windows x86_64 otel-desktop-viewer_windows_amd64.zip

On Windows, unzip the archive and run otel-desktop-viewer.exe.

Linux: release binaries require glibc 2.39 or newer (Ubuntu 24.04+, Debian 13+, Fedora 40+). This applies to the tarballs, the .deb/.rpm packages, and the Docker images.

# example: macOS Apple Silicon
curl -LO https://github.com/CtrlSpice/otel-desktop-viewer/releases/latest/download/otel-desktop-viewer_darwin_arm64.tar.gz
tar xzf otel-desktop-viewer_darwin_arm64.tar.gz
./otel-desktop-viewer

Via apt / dnf (Linux)

Stable releases include .deb and .rpm packages on GemFury.

Debian / Ubuntu:

curl -fsSL https://apt.fury.io/ctrlspice/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/fury.gpg
echo "deb [signed-by=/usr/share/keyrings/fury.gpg] https://apt.fury.io/ctrlspice/ * *" \
  | sudo tee /etc/apt/sources.list.d/fury.list
sudo apt update
sudo apt install otel-desktop-viewer

Fedora / RHEL:

sudo tee /etc/yum.repos.d/fury.repo <<EOF
[fury]
name=Gemfury Repo
baseurl=https://yum.fury.io/ctrlspice/
enabled=1
gpgcheck=0
EOF
sudo dnf install otel-desktop-viewer

Via go install

Building from source requires Go and CGO.

go version
go env CGO_ENABLED   # should print 1
gcc --version        # or cc --version

On Windows: You’ll need MSYS2 for CGO compilation:

  1. Install MSYS2: Download and install from https://www.msys2.org/

  2. Open MSYS2 UCRT64 terminal:

    • After installing MSYS2, you’ll see multiple terminal options in the Start Menu
    • Choose “MSYS2 UCRT64” (not “MSYS2 MinGW 64-bit” or “MSYS2 MSYS”)
    • Or run: C:\msys64\ucrt64.exe
  3. Install required packages:

    pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-toolchain
    
  4. Add MSYS2 to your PATH (choose one):

    Command Prompt (permanent):

    setx PATH "%PATH%;C:\msys64\ucrt64\bin"
    

    PowerShell (permanent):

    [Environment]::SetEnvironmentVariable("PATH", [Environment]::GetEnvironmentVariable("PATH", "User") + ";C:\msys64\ucrt64\bin", "User")
    

    PowerShell (current session only):

    $env:PATH += ";C:\msys64\ucrt64\bin"
    
  5. Restart your terminal for PATH changes to take effect

  6. Test the setup:

    gcc --version
    g++ --version
    

On Linux/macOS: the checks above are sufficient.

@latest resolves to the newest stable tag on the Go module proxy, not an alpha or beta release. Pin a version such as @v0.3.0, or use a GitHub Release binary to avoid compiling locally.

# install the CLI tool
go install github.com/CtrlSpice/otel-desktop-viewer@latest

# run it!
$(go env GOPATH)/bin/otel-desktop-viewer

# if you have $GOPATH/bin added to your $PATH you can call it directly!
otel-desktop-viewer

# if not you can add it to your $PATH by running this or adding it to
# your startup script (usually ~/.bashrc or ~/.zshrc)
export PATH="$(go env GOPATH)/bin:$PATH"

Via Docker

Docker does not require a local Go installation.

Pull from GitHub Container Registry (auto-selects your architecture):

docker pull ghcr.io/ctrlspice/otel-desktop-viewer:latest
docker run -p 8000:8000 -p 4317:4317 -p 4318:4318 ghcr.io/ctrlspice/otel-desktop-viewer:latest

Or pin a specific version:

docker pull ghcr.io/ctrlspice/otel-desktop-viewer:v0.3.0

Explicit per-arch tags are also available:

docker pull ghcr.io/ctrlspice/otel-desktop-viewer:latest-amd64
docker pull ghcr.io/ctrlspice/otel-desktop-viewer:latest-arm64

Or build locally from source (compiles the frontend and Go binary inside the image):

docker build --tag otel-desktop-viewer:latest .
docker run -p 8000:8000 -p 4317:4317 -p 4318:4318 otel-desktop-viewer:latest

Docker Compose

Running your app in Compose? Add the viewer as a service and export OTLP to otel-desktop-viewer:4318 (HTTP) or otel-desktop-viewer:4317 (gRPC).

services:
  app:
    image: your-apps-image-tag
    # Add your app configuration here

  otel-desktop-viewer:
    image: ghcr.io/ctrlspice/otel-desktop-viewer:latest
    ports:
      - "8000:8000"
      - "4317:4317"
      - "4318:4318"

Command Line Options

Telemetry is stored in memory by default. Use --db to persist to a file.

The bare command runs the viewer in the foreground. Keep it running while using the UI or client commands.

Automation should reuse an existing viewer. If none is available, start otel-desktop-viewer --open-browser=false as a managed child and wait for its HTTP endpoint. Stop it only if you started it.

Flags:
      --browser-port int     Port for the web UI and JSON-RPC API (default 8000)
      --db string            DuckDB file path (default: in-memory)
      --db-max-size string   Store size cap, e.g. 512MB or 2GB; oldest telemetry
                             is pruned past it. 0 disables pruning.
                             (default: 512MB in-memory, 2GB with --db)
      --grpc int             OTLP gRPC listen port (default 4317)
      --host string          Host for OTLP receivers and the web UI (default localhost)
      --http int             OTLP HTTP listen port (default 4318)
      --open-browser         Open the browser on launch (default true)
      --self-telemetry-endpoint string
                             Export the viewer's own traces and metrics to this OTLP/gRPC endpoint
  -h, --help                 help for otel-desktop-viewer
  -v, --version              version for otel-desktop-viewer
otel-desktop-viewer --db ./telemetry.duckdb --db-max-size 4GB

Observe the Viewer

Run another viewer to receive the observed viewer’s own traces and metrics:

otel-desktop-viewer --grpc 4327 --http 4328 --browser-port 8001

Then start the observed viewer in another terminal:

otel-desktop-viewer --self-telemetry-endpoint http://localhost:4327

Omitting --self-telemetry-endpoint keeps self-telemetry off. The endpoint is external to the observed viewer. The monitoring endpoint must remain running through observed viewer shutdown. The caller owns starting, stopping, and waiting for both foreground processes.

Query a Running Viewer

Start the viewer with the bare command, then query it from another terminal:

otel-desktop-viewer query 'SHOW TABLES'
otel-desktop-viewer query 'SELECT service_name, count(*) FROM spans GROUP BY service_name' --limit 50

The query command runs read-only SQL against the viewer at http://localhost:8000. It returns up to 25 rows as aligned columns by default. Use --endpoint for another viewer address, --limit for another row limit, or --json for the JSON result.

Search Telemetry from the CLI

With the viewer running, search its trace, log, or metric summaries from another terminal:

otel-desktop-viewer traces --service checkout --since 30m
otel-desktop-viewer logs --since 1h --limit 50
otel-desktop-viewer metrics --start 2026-10-02T08:00:00Z --end 2026-10-02T09:00:00Z --json

These commands use http://localhost:8000, search the last hour, and return up to 25 summaries. Use --endpoint, --service, --since, --start, --end, --limit, or --json to change those defaults.

In metrics --json, metricRef is the exact UUID text from metrics.id. The viewer generates it; OTLP does not provide it. It is valid only for that database. Pass it unchanged and do not parse it.

Inspect every compact span and trace-linked log row for one trace:

otel-desktop-viewer trace 0123456789abcdef0123456789abcdef
otel-desktop-viewer trace 0123456789abcdef0123456789abcdef --json

Table and JSON output contain the same untruncated fields. Trace start is min(spans.start_time). Trace duration is max(spans.end_time) - min(spans.start_time). Span offset and duration are calculated from received nanosecond timestamps and returned as exact decimal strings. Logs use the received timestamp unless it is zero, then use the received observed timestamp. Severity uses received text when present; otherwise it uses the display band derived from the received number. The body is the compact body_preview. Use query for complete stored log fields.

Inspect one span with full typed detail and every log associated with that exact trace and span ID:

otel-desktop-viewer span 000000000000002a
otel-desktop-viewer span 0123456789abcdef0123456789abcdef 000000000000002a --json

A standalone span ID returns a not-found result, one exact span, or stable span summaries when the ID occurs in multiple traces. The default limit is 25; use --limit to request more or fewer. Ambiguous summaries include both IDs, the exact match count, and whether more rows are available. It never chooses between duplicate span IDs from different traces. The qualified form selects only the requested trace and span pair. Both not-found forms are successful structured results.

Configuring Your OpenTelemetry SDK

Point your app’s OTLP exporter at the viewer. Send to http://localhost:4318 (HTTP) or http://localhost:4317 (gRPC).

If your SDK supports configuration via environment variables, you can use:

# HTTP
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"

# gRPC
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"

Declarative configuration

SDKs that support declarative configuration can use a YAML file instead. Save this as otel-config.yaml:

file_format: "1.1"

resource:
  attributes:
    - name: service.name
      value: my-service

tracer_provider:
  processors:
    - batch:
        exporter:
          otlp_http:
            endpoint: http://localhost:4318/v1/traces

meter_provider:
  readers:
    - periodic:
        exporter:
          otlp_http:
            endpoint: http://localhost:4318/v1/metrics

logger_provider:
  processors:
    - batch:
        exporter:
          otlp_http:
            endpoint: http://localhost:4318/v1/logs

Then point your app at it:

export OTEL_CONFIG_FILE=/path/to/otel-config.yaml

[!NOTE] When a config file is used, SDKs ignore the traditional OTEL_* environment variables entirely (aside from ${VAR} substitution inside the file itself).

Example With otel-cli

otel-cli can send test traces from shell scripts, including attributes, events, propagated context, and background spans.

Start the desktop viewer in one terminal:

otel-desktop-viewer

In another terminal, point otel-cli at the viewer:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

Quick span: wrap any command:

otel-cli exec --service my-service --name "check the archive" curl -s -o /dev/null https://archive.org/

Chained spans: otel-cli propagates context automatically:

otel-cli exec --kind producer --service demo --name produce -- \
  otel-cli exec --kind consumer --service demo --name consume sleep 0.2

Rich trace: background span, events, attributes, and linked child spans:

sockdir=$(mktemp -d)
carrier=$(mktemp)

otel-cli span background \
  --service "otel-cli-example" \
  --name "script runtime" \
  --attrs "deployment.environment=local,team=platform" \
  --tp-carrier "$carrier" \
  --sockdir "$sockdir" &
sleep 0.1

otel-cli span event --name "starting work" --attrs "phase=setup,attempt=1" --sockdir "$sockdir"

otel-cli exec --service "otel-cli-example" --name "fetch example" --kind client \
  --attrs "http.url=https://example.com" \
  --tp-carrier "$carrier" \
  curl -s -o /dev/null https://example.com

otel-cli exec --kind producer --service "otel-cli-example" --name "hand off" \
  --tp-carrier "$carrier" -- \
  otel-cli exec --kind consumer --service "otel-cli-example" --name "process" sleep 0.1

otel-cli span event --name "finished" --attrs "phase=teardown,status=ok" --sockdir "$sockdir"
otel-cli span end --sockdir "$sockdir"

Open http://localhost:8000/traces to explore the result. For more otel-cli features (custom span times, {{traceparent}} in command args, config files, and a built-in TUI server), see the otel-cli README.

Agent Usage Skill

Print the guide bundled with your installed viewer:

otel-desktop-viewer skills

While the viewer is running, the same guide is available at http://localhost:8000/llms.txt.

Install the otel-desktop-viewer skill from this repository:

npx skills add CtrlSpice/otel-desktop-viewer --skill otel-desktop-viewer

The OTel Desktop Viewer skill gives coding agents focused read-only SQL examples for inspecting telemetry in a running viewer. It requires a build where otel-desktop-viewer --help lists query.

Implementation

The CLI is a custom OpenTelemetry Collector distribution. Its desktop exporter writes telemetry to the store owned by the duckdb extension. The extension:

  • exposes data through a JSON-RPC API at POST /rpc
  • serves a Svelte web UI embedded in the binary via go:embed

DuckDB runs in memory by default. Use --db for file-backed storage.

See ARCHITECTURE.md for a full system overview.

What’s With the Axolotl??

Her name is Lulu Axol’Otel. She is very pink, and I love her.

More seriously, I like to give my side projects an animal theme to add a little aesthetic interest on what otherwise might be fairly plain applications.

Contributing

See CONTRIBUTING.md. Please read our Code of Conduct before participating.

License

Apache 2.0, see LICENSE

View this README on GitHub

推奨ツール

別のキーワードを試すか、フィルタを外してください。

インストール

npx skillfish add ctrlspice/otel-desktop-viewer