- JDK 25 or newer. A JVM build needs any JDK 25+; the native build needs a
GraalVM 25 distribution with
native-imageavailable. - Maven wrapper (
./mvnw) from this repository - Codex CLI installed and authenticated when verifying live Codex usage polling
The Maven wrapper downloads Maven 3.9.12.
Compile:
./mvnw -q -DskipTests compileRun tests:
./mvnw -q testRun in Quarkus dev mode:
./mvnw quarkus:devPackage JVM artifact:
./mvnw -DskipTests packageThe JVM package is written under target/quarkus-app/ and starts via
target/quarkus-app/quarkus-run.jar.
Package native binary:
./mvnw -DskipTests -Dnative packageThe native build requires a GraalVM 25 distribution with native-image available
(set GRAALVM_HOME / JAVA_HOME to it, or put native-image on PATH).
Alternatively build inside a container with
-Dquarkus.native.container-build=true (needs a container runtime). The GitHub
release workflow builds the published macOS binary directly on the macos-14
runner with GraalVM 25.
application.properties sets:
quarkus.package.output-name=codex-usage-dashboard
quarkus.package.jar.add-runner-suffix=false
so the intended release artifact name is target/codex-usage-dashboard.
Health:
curl -s http://127.0.0.1:4318/healthDashboard:
curl -s -o /tmp/codex-usage-dashboard.html -w '%{http_code}\n' \
'http://127.0.0.1:4318/?range=6h&grain=5m'Core API:
curl -s 'http://127.0.0.1:4318/api/summary?range=6h'
curl -s 'http://127.0.0.1:4318/api/summary?source=claude&range=6h'
curl -s 'http://127.0.0.1:4318/api/credits/by-type-timeseries?range=6h&grain=5m'
curl -s 'http://127.0.0.1:4318/api/cost/by-type-timeseries?source=codex&range=6h&grain=5m'
curl -s 'http://127.0.0.1:4318/api/tokens/by-type-timeseries?source=claude&range=6h&grain=5m'
curl -s 'http://127.0.0.1:4318/api/cost/by-model?source=claude&range=6h'
curl -s 'http://127.0.0.1:4318/api/tokens/by-model?source=claude&range=6h'
curl -s 'http://127.0.0.1:4318/api/tokens/by-trigger-timeseries?source=codex&range=6h&grain=5m'
curl -s 'http://127.0.0.1:4318/api/tokens/by-model-trigger?source=claude&range=6h'
curl -s 'http://127.0.0.1:4318/api/events/conversations?source=codex&limit=5&range=6h'
curl -s 'http://127.0.0.1:4318/api/events/completions?limit=5&range=6h'
curl -s 'http://127.0.0.1:4318/api/events/errors?limit=5&range=6h'OTLP/HTTP JSON should fail explicitly:
curl -s -X POST \
-H 'Content-Type: application/json' \
-d '{}' \
-w '\n%{http_code}\n' \
http://127.0.0.1:4318/v1/logsExpected status: 415.
Codex can send OTLP directly to the gRPC receiver:
[otel]
exporter = { otlp-grpc = { endpoint = "http://127.0.0.1:4317" } }Claude Code can send OTLP logs directly to the same receiver. For persistent
local setup, merge this into ~/.claude/settings.json:
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://127.0.0.1:4317"
}
}For a one-off session:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317
claudeOnly Claude Code log/events are used by this dashboard. Metrics and traces are accepted by the receiver but discarded.
An OpenTelemetry Collector can fan out to this app with an OTLP/HTTP protobuf exporter:
exporters:
otlphttp/codex_usage_dashboard:
endpoint: http://127.0.0.1:4318The exporter appends /v1/logs automatically. gzip is supported.
Raw records are the short-term source of truth. To replay derived rows after changing annotation logic:
DELETE FROM annotated_events;
UPDATE cursor SET value = '0' WHERE name = 'annotate_log_id';Then let the annotate job run again.
Per-row annotate exceptions stop the pass before the failing row, so parser or DB bugs remain replayable after the fix is deployed.
Retention can remove old rows from otel_log_records after the annotate cursor
has passed them. Derived chart history can remain in annotated_events, but
rows whose raw source was deleted cannot be replayed and /api/events/{id}/raw
will return 404.
Before creating a public repository or release:
- Do not commit
data/. - Do not commit
target/. - Do not commit local logs or
.envfiles. - Verify the release binary starts with localhost-only defaults.
- Verify
README.mdstill describes binary usage rather than development setup.
Release flow:
- Push the release commit to
main. - Create and push an annotated version tag, for example
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z. - Let the release workflow build the native binary and create a draft GitHub Release with the zip asset attached.
- Review the draft release notes and asset, then publish the release manually.