Skip to content

Commit

Permalink
Standard library routing (#151)
Browse files Browse the repository at this point in the history
- Changed `server.go` to use standard routing instead of gorilla/mux
(thank you for your service)
- Wrote tests for the handlers and routing
- Fixed small bugs in `store.go` and `queries.go`
- Reintroduced CI testing, and the README because I was in there
- Made the AI copilot generate capybaras doing human things for my
amusement again

![A vibrant digital illustration featuring a capybara wearing a blue
kigurumi resembling a gopher. The capybara looks delightful as it waves
goodbye. Its friend, a gorilla, is reciprocating with a warm expression,
making the scene absolutely
charming.](https://github.com/user-attachments/assets/51d92766-060d-446f-a383-50c0d01d48a7)

---------

Co-authored-by: Jeremy Morrell <[email protected]>
  • Loading branch information
CtrlSpice and jmorrell authored Oct 21, 2024
1 parent 685513a commit 15bfe67
Show file tree
Hide file tree
Showing 13 changed files with 692 additions and 119 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on: push

jobs:

build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
# include:
# - os: windows-latest
# env:
# NO_WINDOWS_SERVICE: 0


steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.23

- name: Build
run: cd desktopcollector; go build -o ../otel-desktop-viewer

- name: Exporter Go Tests
run: cd desktopexporter; go test ./...

# - name: Integration
# run: ./.github/workflows/integration.sh
# shell: bash
# env:
# CI: true
33 changes: 33 additions & 0 deletions .github/workflows/integration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash

# Launch the app in the background
./otel-desktop-viewer &

# Save the process ID to kill it later
pid=$!

# Wait 2 seconds for everything to boot up
sleep 2

# Send an example trace
curl -i http://localhost:4318/v1/traces -X POST -H "Content-Type: application/json" -d @./.github/workflows/span.json

sleep 1

# Check that a trace summary has been created, and the rootServiceName is correct
response=$(curl 'http://localhost:8000/api/traces' -H "Content-Type: application/json")
echo $response

rootServiceName=$(jq -r '.traceSummaries[0].rootServiceName' <<< $response)
echo $rootServiceName

if [ $rootServiceName == "test-with-curl" ]
then
echo 'Exit status 0: All good.'
kill -15 $pid
exit 0
else
echo 'Exit status 1: unexpected rootServiceName ' + $rootServiceName
kill -15 $pid
exit 1
fi
37 changes: 37 additions & 0 deletions .github/workflows/span.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"anyValue": "test-with-curl"
}
}
]
},
"scopeSpans": [
{
"scope": {
"name": "manual-test"
},
"spans": [
{
"traceId": "71699b6fe85982c7c8995ea3d9c95df2",
"spanId": "3c191d03fa8be065",
"name": "spanitron",
"kind": 2,
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"status": {
"code": 1
}
}
]
}
]
}
]
}
184 changes: 184 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# otel-desktop-viewer

`otel-desktop-viewer` is a CLI tool for receiving OpenTelemetry traces while working
on your local machine that helps you visualize and explore your trace data without
needing to send it on to a telemetry vendor.

![otel-desktop-viewer demo 3 LQ](https://user-images.githubusercontent.com/56372758/218345612-381fe2ff-8245-429f-ba2f-ca6431585a16.gif)

Its goals are to be easy-to-install with minimal dependencies and fast. It is written in Go
as a custom exporter on top of the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector).
Also, it has a dark mode.

![OpenTelemetryDesktopViewer](https://user-images.githubusercontent.com/56372758/217080670-3001cb67-ab20-4ae2-ac55-82ca04bad815.png)

## Getting started

#### via Homebrew
```bash
brew tap CtrlSpice/homebrew-otel-desktop-viewer
brew install otel-desktop-viewer
```

#### via `go install`
Make sure you have [go](https://go.dev/) installed.

```bash
# 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"
```

Running the CLI will open a browser tab to `localhost:8000` to load the UI,
and spin up a server listening on `localhost:4318` for OTLP http payloads and
`localhost:4317` for OTLP grpc payloads.

## Command Line Options
```bash
Flags:
--browser int The port number where we expose our data (default 8000)
--grpc int The port number on which we listen for OTLP grpc payloads (default 4317)
-h, --help help for otel-desktop-viewer
--http int The port number on which we listen for OTLP http payloads (default 4318)
-v, --version version for otel-desktop-viewer
```

## Configuring your OpenTelemetry SDK

To send telemetry to `otel-desktop-viewer` from your application, you need to
configure an OTLP exporter to send via grpc to `http://localhost:4317` or via
http to `http://localhost:4318`.

If your OpenTelemetry SDK OTLP exporter supports [configuration via environment
variables](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/)
then you should be able to send to `otel-desktop-viewer` with the following environment
variables set

```
# For HTTP:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
# For GRPC:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
```
## Keyboard navigation and shortcuts
```bash
Navigation:
Move up the trace summary list: ← or h
Move down the trace summary list: → or l
Move up the span list: ↑ or k
Move down the span list: ↓ or j

Shortcuts:
Clear all traces: ctrl + l
Refresh the page: r
Bring up the keyboard help dialog: ?
```


## Example with `otel-cli`

If you have [`otel-cli`](https://github.com/equinix-labs/otel-cli) installed, you can
send some example data with the following script.

```bash
# start the desktop viewer (best to do this in a separate terminal)
otel-desktop-viewer

# configure otel-cli to send to our desktop viewer endpoint
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

# use otel-cli to generate spans!
otel-cli exec --service my-service --name "curl google" curl https://google.com

# a more visually interesting example trace
carrier=$(mktemp)
sockdir=$(mktemp -d)
otel-cli span background \
--service "otel-cli-example" \
--name "otel-cli-example background span" \
--tp-print \
--tp-carrier $carrier \
--sockdir $sockdir &
sleep 0.1 # give the background server just a few ms to start up
otel-cli span event --name "cool thing" --attrs "foo=bar" --sockdir $sockdir
otel-cli exec --service "otel-cli-example" --name "curl google" --tp-carrier $carrier curl https://google.com
sleep 0.1
otel-cli exec --service "otel-cli-example" --name "curl google" --tp-carrier $carrier curl https://google.com
sleep 0.1
otel-cli span event --name "another cool thing\!" --attrs "foo=bar" --sockdir $sockdir
otel-cli span end --sockdir $sockdir
```

![otel-cli-example](https://user-images.githubusercontent.com/56372758/217082956-23c60f2d-f882-4c78-a205-f744596fac21.png)

## Why does this exist?

When we send OpenTelemetry telemetry to a tracing vendor we expect to be able to visualize our
data, but when working locally our experience often looks more like this:

```
{
"Name": "Poll",
"SpanContext": {
"TraceID": "4d7f165e90111f4fc9003d5bbf7aca81",
"SpanID": "1f7a655a9b7a6f4b",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "4d7f165e90111f4fc9003d5bbf7aca81",
"SpanID": "d1b8f7f96ad9d1a0",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
...
```

You can use [Jaeger's all-in-one](https://www.jaegertracing.io/docs/1.41/deployment/#all-in-one)
distribution, but this requires quite a bit of additional knowledge for the end-user around docker and
navigating a lot of configuration options. Additionally the user experience is not focused around
"show me the data I just emitted".

The goals for `otel-desktop-viewer` are to allow a user to install it with one command, require
minimal configuration and additional tooling, and be as approachable as possible for developers
at all levels of experience.


## Implementation

The CLI is implemented in Go building on top of the OpenTelemetry Collector. A custom
`desktop` exporter is registered that:

- collects trace data in memory
- exposes this trace data via an HTTP API
- serves a static React app that renders the collected traces

All of the static web assets are built into the final binary using the [go:embed](https://blog.jetbrains.com/go/2021/06/09/how-to-use-go-embed-in-go-1-16/)
directive so that the binary is self-contained and relocatable.

## 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](https://github.com/CtrlSpice/bumblebee-consolematch) an
[animal theme](https://github.com/CtrlSpice/yak-vs-yak) to add a little aesthetic
interest on what otherwise might be fairly plain applications.

## License
Apache 2.0, see LICENSE
16 changes: 15 additions & 1 deletion desktopcollector/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ module github.com/CtrlSpice/otel-desktop-viewer/desktopcollector
go 1.23.0

require (
github.com/CtrlSpice/otel-desktop-viewer/desktopexporter v0.0.0-20241007201340-685513a8f94c
github.com/spf13/cobra v1.8.1
go.opentelemetry.io/collector/component v0.107.0
go.opentelemetry.io/collector/confmap v0.107.0
go.opentelemetry.io/collector/confmap/provider/envprovider v0.107.0
go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.107.0
go.opentelemetry.io/collector/connector v0.107.0
go.opentelemetry.io/collector/exporter v0.107.0
go.opentelemetry.io/collector/exporter/debugexporter v0.107.0
go.opentelemetry.io/collector/extension v0.107.0
go.opentelemetry.io/collector/otelcol v0.107.0
go.opentelemetry.io/collector/processor v0.107.0
Expand All @@ -21,6 +21,7 @@ require (
)

require (
github.com/apache/arrow/go/v17 v17.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand All @@ -30,24 +31,32 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v24.3.25+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/providers/confmap v0.1.0 // indirect
github.com/knadh/koanf/v2 v2.1.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/marcboeker/go-duckdb v1.8.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mostynb/go-grpc-compression v1.2.3 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
Expand All @@ -60,6 +69,7 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.opentelemetry.io/collector v0.107.0 // indirect
go.opentelemetry.io/collector/client v1.13.0 // indirect
go.opentelemetry.io/collector/component/componentprofiles v0.107.0 // indirect
Expand Down Expand Up @@ -106,8 +116,12 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.22.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
Expand Down
Loading

0 comments on commit 15bfe67

Please sign in to comment.