[FEATURE] Native OpenTelemetry (OTLP) support for metrics + traces
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- go, kubernetes
- Domain
- backend, observability-sre
Research direction
Start by reading go.mod and cmd/kelos-controller/main.go, then inspect the controller-runtime Manager options and reconcile methods for TaskSpawner, Task, AgentConfig, and Workspace. Check cmd/kelos-spawner and docs/reference.md for the related entry points. Done means the agreed minimum metrics and reconcile traces use the documented OTLP environment variables without removing the existing Prometheus surface.
Written by the indexing model from the issue text.
Description
Add OpenTelemetry (OTLP) support for metrics, traces, and logs
Current behavior
Kelos v0.29.0 exposes Prometheus metrics via controller-runtime's default
metricsserver on --metrics-bind-address=:8080. That's scrapeable,
but the only exposed surface is Prometheus — there is no OTLP exporter
for metrics, no tracing instrumentation, and no OTEL logs pipeline.
go.mod confirms: no go.opentelemetry.io/* dependencies; only
github.com/prometheus/client_golang.
The telemetry package and kelos-telemetry CronJob are unrelated —
they send anonymous aggregate usage stats to kelos-dev's PostHog
endpoint (daily heartbeat, version/count rollup). That is product
analytics, not observability telemetry.
Operators running Kelos in a mixed-signal observability stack (OTEL
Collector → Grafana/Honeycomb/Datadog/Tempo/Loki/etc.) today have two
options:
- Stand up a Prometheus scrape + OTEL Collector bridge just for Kelos,
doubling the collector config footprint - Skip Kelos metrics entirely and rely on log parsing
Neither gives distributed traces of TaskSpawner → Task → agent pod, which
would be the highest-value signal for debugging why a particular agent
run stalled, which GitHub API call throttled, which webhook processing
step hung, etc.
Expected behavior
Kelos should emit:
- Metrics via OTLP/gRPC or OTLP/HTTP — push to the endpoint set by
the standardOTEL_EXPORTER_OTLP_ENDPOINTenv var (plus the usual
OTEL_EXPORTER_OTLP_HEADERS,OTEL_SERVICE_NAME,
OTEL_RESOURCE_ATTRIBUTES). Falls back to the existing Prometheus
endpoint if OTLP env vars are unset — zero-migration for existing
operators. - Traces — instrument the reconcile loop on each controller
(TaskSpawner, Task, AgentConfig, Workspace) so one trace per reconcile
shows discovery → CRD update → spawn. Controller-runtime has
pluggabletrace.TracerProviderhooks — wiring is straightforward. - Task-level spans — propagate a trace through the agent pod
lifecycle (git clone init container → agent container → capture
container). Agent container can emit spans into the same trace via
OTEL_EXPORTER_OTLP_ENDPOINTpassed through the spawner. - Logs via OTLP/logs — optional, less urgent; stdout + a sidecar
collector still works for most stacks.
Minimum viable: (1) and (2). (3) and (4) can land later.
Steps to reproduce (current state observation)
-
Install Kelos per the Quick Start:
kelos install. -
Inspect the controller's observability surface:
kubectl port-forward -n kelos-system deploy/kelos-controller-manager 8080 curl -s http://localhost:8080/metrics | head -20You see Prometheus-format metrics (controller-runtime defaults).
-
Check for OTLP-related env vars the controller honors:
kubectl exec -n kelos-system deploy/kelos-controller-manager -- /kelos --help 2>&1 | grep -i otel # No matches. -
Attempt to configure an OTLP export target:
kubectl set env deploy/kelos-controller-manager -n kelos-system \ OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 \ OTEL_SERVICE_NAME=kelos-controllerController restarts. No traces or metrics ever appear at the
collector — the env vars are ignored because Kelos doesn't link in
an OTLP exporter. -
Confirm in
go.mod:grep -i otel go.mod # (no output)
Anything else
Environment
| Field | Value |
|---|---|
| Kelos version | v0.29.0 |
| Controller-runtime | v0.23.1 |
| Go | 1.22+ (per go.mod) |
| Cluster | Kubernetes 1.35.2 on Talos Linux |
Scope suggestion for a first PR
Keep it additive and opt-in:
-
Add dependencies:
go.opentelemetry.io/otel,
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc,
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc,
go.opentelemetry.io/otel/sdk/{metric,trace,resource}. -
In
cmd/kelos-controller/main.go, afteropts.Schemeis set,
initialise aMeterProvider+TracerProviderwhen
OTEL_EXPORTER_OTLP_ENDPOINTis set. Pass them into
controller-runtime'sManageroptions (Manager.MetricsBindAddress
gets a dual-export setup,Manager.Controller.SkipNameValidation
unchanged). -
Wrap each reconcile method with a span:
ctx, span := tracer.Start(ctx, "reconcile.TaskSpawner") defer span.End() -
For the spawner pod (
cmd/kelos-spawner), same treatment with a
separateOTEL_SERVICE_NAME=kelos-spawnerresource label. -
Document the env vars in
docs/reference.mdalongside the existing
--metrics-bind-addressflag.
(3) and (4) — agent Task span injection, log pipeline — can be
follow-up PRs once the core is in.
Related Kelos features that would benefit immediately
- Task failure debugging — a trace showing "spawner discovered N items
→ Task created → git clone init → agent runtime: 3m42s → capture exit
code" eliminates the current "read 100KB of JSONL logs" workflow. - PR comment latency investigation —
githubIssuespolling has a
pollIntervalfloor of 5m; traces would surface where time goes
(GitHub API, CRD update, Task create delay, agent startup). - Multi-cluster operator view — when operators run Kelos in several
clusters, OTLP into a single collector gives the aggregate view
Prometheus federation can't cleanly do.
Happy to send a PR
If maintainers agree with the approach, I can submit a PR implementing
the controller-side metrics + reconcile spans (item 1, 2, 3, 5) against
main. Let me know.
- Dominant language
- Go
- Stars
- 331
- Forks
- 40
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 70
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from kelos-dev/kelos
-
actor/kelos generated-by-kelos kind/bug priority/important-longterm triage-accepted
Difficulty 1/5 Under an hour Newbie friendliness 76/100
-
actor/kelos generated-by-kelos kind/bug priority/important-soon triage-accepted
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
actor/kelos kind/feature priority/important-soon triage-accepted
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
actor/kelos generated-by-kelos kind/bug priority/important-longterm triage-accepted
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
actor/kelos generated-by-kelos kind/cleanup priority/important-longterm triage-accepted
Difficulty 1/5 Under an hour Newbie friendliness 78/100
Similar issues
-
optimization optimization:agents-md-curator
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
githubnext/gh-aw-cao#13143 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
blinklabs-io/bursa#904 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
yanet-platform/ipfw-go#129 ·
-
bug confmap/provider/googlesecretmanagerprovider needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
open-telemetry/opentelemetry-collector-contrib#51273 · 2 comments ·
-
bug: AI Gateway client filter lists "Unknown" twice when NULL and literal Unknown clients coexist Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 90/100