Skip to content

Commit 024cc4f

Browse files
committed
fix(telemetry): address review comments
- Set the global OTEL error handler only when telemetry is enabled - Correct finishTelemetry doc comment (handles are nil only when uninitialized) - Restore the previous tracer provider in gitops/actions attribute tests - Document the telemetry config block in docs/config.md
1 parent 6c8001e commit 024cc4f

5 files changed

Lines changed: 49 additions & 5 deletions

File tree

actions/telemetry_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import (
1212
func TestSetActionAttrs(t *testing.T) {
1313
exp := tracetest.NewInMemoryExporter()
1414
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp))
15+
prev := otel.GetTracerProvider()
1516
otel.SetTracerProvider(tp)
16-
t.Cleanup(func() { _ = tp.Shutdown(context.Background()) })
17+
t.Cleanup(func() {
18+
otel.SetTracerProvider(prev)
19+
_ = tp.Shutdown(context.Background())
20+
})
1721

1822
_, span := tp.Tracer("test").Start(context.Background(), "action.run")
1923
setActionAttrs(span, "proj:up:default", "proj", "docker compose up")

cmd/root.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ func Execute() {
9999
}
100100

101101
// finishTelemetry records the final command status on the root span and flushes
102-
// pending spans. Safe to call when telemetry is disabled (handles are nil).
102+
// pending spans. Safe to call when telemetry was never initialized (e.g.
103+
// completion commands or an early config failure), where the handles remain nil.
103104
func finishTelemetry(err error) {
104105
if globalRootSpan != nil {
105106
if err != nil {

docs/config.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ An optional `.gitte-override.yml` in the same directory is deep-merged on top, u
1818
- [searchFor](#searchfor)
1919
- [feature\_gates](#feature_gates)
2020
- [sources](#sources)
21+
- [telemetry](#telemetry)
2122
- [Remote configuration](#remote-configuration)
2223

2324
---
@@ -34,6 +35,7 @@ sources: # auto-discovery sources (optional)
3435
searchFor: # global output pattern matching (optional)
3536
actionOverride: # per-action overrides (optional)
3637
retry: # global retry defaults (optional)
38+
telemetry: # OpenTelemetry trace export (optional)
3739
```
3840
3941
---
@@ -397,6 +399,38 @@ gitte run up --discover # discover, then sync, then run actions
397399

398400
---
399401

402+
## telemetry
403+
404+
Gitte can export OpenTelemetry traces over OTLP/HTTP to an OTLP-compatible backend (e.g. Elastic APM) to help debug failures. Telemetry is enabled whenever an endpoint is resolved.
405+
406+
```yaml
407+
telemetry:
408+
endpoint: https://apm.example.com:8200 # OTLP/HTTP endpoint
409+
headers: # arbitrary export headers (optional)
410+
Authorization: "Bearer <secret-token>" # or: "ApiKey <base64-key>"
411+
```
412+
413+
| Field | Description |
414+
|-------|-------------|
415+
| `endpoint` | OTLP/HTTP endpoint to export spans to. Telemetry is enabled when this resolves to a non-empty value. |
416+
| `headers` | Map of HTTP headers attached to every export request — typically authentication (`Authorization`). |
417+
418+
Each invocation produces one trace: a root span for the command, child spans for each repo sync (branch, commit SHA, dirty flag) and each action task (command, exit code), with errors recorded on the relevant span. The OS username (`user.name`) and hostname (`host.name`) are attached to every trace to identify which developer and machine produced it.
419+
420+
The gitte CLI arguments and each action's command line are exported as span attributes. Keep secrets out of action command definitions and CLI arguments — pass them via environment variables, which are not exported. Full remote URLs are never collected (repos are identified by name only).
421+
422+
Environment variables override or disable telemetry:
423+
424+
| Variable | Effect |
425+
|----------|--------|
426+
| `GITTE_TELEMETRY=off` | Disable telemetry locally (kill-switch) |
427+
| `GITTE_TELEMETRY_URL` | Override the endpoint |
428+
| `OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_HEADERS` | Standard OTEL env vars, honored as a fallback when no gitte endpoint is set |
429+
430+
Precedence: `GITTE_TELEMETRY=off` > `GITTE_TELEMETRY_URL` > config `endpoint` > `OTEL_EXPORTER_OTLP_*`. Telemetry is best-effort and never blocks or slows gitte; export failures are silently ignored and flushing on exit is time-bounded.
431+
432+
---
433+
400434
## Remote configuration
401435

402436
Gitte can load its configuration from a remote git repository. Create a `.gitte-env` file alongside `.gitte.yml`:

gitops/telemetry_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import (
1212
func TestSetGitContextAttrs(t *testing.T) {
1313
exp := tracetest.NewInMemoryExporter()
1414
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp))
15+
prev := otel.GetTracerProvider()
1516
otel.SetTracerProvider(tp)
16-
t.Cleanup(func() { _ = tp.Shutdown(context.Background()) })
17+
t.Cleanup(func() {
18+
otel.SetTracerProvider(prev)
19+
_ = tp.Shutdown(context.Background())
20+
})
1721

1822
_, span := tp.Tracer("test").Start(context.Background(), "gitops.sync")
1923
setGitContextAttrs(span, "main", "abc123", true)

telemetry/telemetry.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,14 @@ func resourceAttributes(version, username, hostname string) []attribute.KeyValue
100100
// always non-nil and safe to call; setup failures and disabled telemetry both
101101
// degrade to a no-op shutdown.
102102
func Init(ctx context.Context, cfg *config.GitteConfig, version string) func() {
103-
otel.SetErrorHandler(noopErrorHandler{})
104-
105103
r := Resolve(cfg)
106104
if !r.Enabled {
107105
return func() {}
108106
}
109107

108+
// Only mutate process-wide OTEL state once telemetry is known to be enabled.
109+
otel.SetErrorHandler(noopErrorHandler{})
110+
110111
var opts []otlptracehttp.Option
111112
if !r.UseSDKEnv {
112113
opts = append(opts, otlptracehttp.WithEndpointURL(r.Endpoint))

0 commit comments

Comments
 (0)