feat: add OpenTelemetry tracing export (OTLP/HTTP) - #245
Open
LauJosefsen wants to merge 29 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Adds optional OpenTelemetry tracing to the gitte CLI, exporting spans via OTLP/HTTP when configured (or when standard OTEL_EXPORTER_OTLP_* env vars are set), and threads the root span context through gitops/actions execution paths.
Changes:
- Introduces a new
telemetrypackage to resolve config/env settings and initialize a global OTLP/HTTP tracer provider. - Instruments
gitops.syncProjectandactions.runGroupTaskwith spans/attributes and error recording. - Adds telemetry configuration to
config.GitteConfig, documentation to README, and unit tests for resolution/init + attribute helpers.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| telemetry/telemetry.go | Telemetry resolution + global tracer provider initialization + root-span helper |
| telemetry/telemetry_test.go | Tests for precedence resolution and init/shutdown behavior |
| cmd/root.go | Initializes telemetry and creates root span per CLI invocation; flushes on exit |
| gitops/gitops.go | Adds gitops sync span and git context attributes (branch/SHA/dirty) |
| gitops/telemetry_test.go | Tests git context attribute helper behavior |
| actions/runner.go | Adds action run span, action context attributes, and exit-code attribute |
| actions/telemetry_test.go | Tests action attribute helper behavior |
| config/types.go | Adds telemetry block to config types |
| config/types_test.go | Tests YAML unmarshal for telemetry config |
| README.md | Documents telemetry usage, env vars, and precedence |
| go.mod | Adds OpenTelemetry SDK + OTLP/HTTP exporter dependencies |
| go.sum | Updates sums for new/transitive dependencies |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+77
to
+83
| func Init(ctx context.Context, cfg *config.GitteConfig, version string) (func(), error) { | ||
| otel.SetErrorHandler(noopErrorHandler{}) | ||
|
|
||
| r := Resolve(cfg) | ||
| if !r.Enabled { | ||
| return func() {}, nil | ||
| } |
Comment on lines
+102
to
+104
| // finishTelemetry records the final command status on the root span and flushes | ||
| // pending spans. Safe to call when telemetry is disabled (handles are nil). | ||
| func finishTelemetry(err error) { |
Comment on lines
+279
to
+281
| ctx, span := telemetry.Tracer().Start(ctx, "action.run") | ||
| setActionAttrs(span, taskName, projName, strings.Join(cmds, " ")) | ||
| defer func() { |
Comment on lines
+3
to
+16
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| sdktrace "go.opentelemetry.io/otel/sdk/trace" | ||
| "go.opentelemetry.io/otel/sdk/trace/tracetest" | ||
| "go.opentelemetry.io/otel" | ||
| ) | ||
|
|
||
| func TestSetGitContextAttrs(t *testing.T) { | ||
| exp := tracetest.NewInMemoryExporter() | ||
| tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp)) | ||
| otel.SetTracerProvider(tp) | ||
| t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) |
Comment on lines
+12
to
+16
| func TestSetActionAttrs(t *testing.T) { | ||
| exp := tracetest.NewInMemoryExporter() | ||
| tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp)) | ||
| otel.SetTracerProvider(tp) | ||
| t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) |
Comment on lines
+174
to
+178
| ## Telemetry | ||
|
|
||
| Gitte can export OpenTelemetry traces to an OTLP/HTTP endpoint (e.g. Elastic | ||
| APM) to help debug failures. Traces capture the command run, per-repo git | ||
| context (branch, commit SHA, dirty state), and per-task outcomes with errors. |
Comment on lines
+54
to
+57
| google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect | ||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect | ||
| google.golang.org/grpc v1.81.1 // indirect | ||
| google.golang.org/protobuf v1.36.11 // indirect |
…ecorded data - Guard getHeadSHA behind span.IsRecording() so it never execs when telemetry is off (was running git rev-parse on every repo every sync regardless) - Shorten flush timeout 3s -> 1s so an unreachable endpoint adds at most 1s on exit - Document that CLI args and action command lines are recorded; advise keeping secrets in env (never exported) - Drop redundant gitte.repo double-set; drop always-nil error return from Init
- 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
… process env is not
LauJosefsen
force-pushed
the
lejo/otel-telemetry
branch
from
July 13, 2026 14:05
a78dd95 to
0fa6e11
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds optional OpenTelemetry instrumentation to gitte, exported via OTLP/HTTP to an OTLP-compatible backend (e.g. Elastic APM). The goal is to make failures on developer machines observable and debuggable — what command ran, what failed, and why — without manual reproduction.
What gets exported
Traces — each gitte invocation is one trace with a structured hierarchy:
Standalone commands (
gitte startup/gitops/actions) emit the relevant phase span directly under the command root.Task span attributes include
gitte.task,gitte.project,gitte.command,gitte.exit_code,gitte.features(enabled, in-scope feature gates) andgitte.env(gitte-injectedKEY=VALUEenv — project env,env_when, feature-gate env; not the process environment). Errors are recorded on the relevant span.Logs — action and startup command output is shipped as OTEL logs, correlated to the span that produced each line (stdout → INFO, stderr → WARN;
[HINT]lines taggedgitte.hint=true). Gitops output is not logged (its outcomes live in span attributes).Configuration
Resolution precedence:
GITTE_TELEMETRY=off(kill-switch) >GITTE_TELEMETRY_URL> configendpoint> standardOTEL_EXPORTER_OTLP_*env vars. The token may instead be supplied per-developer viaOTEL_EXPORTER_OTLP_HEADERSto keep it out of shared config.GITTE_TELEMETRY=offGITTE_TELEMETRY_URLGITTE_TELEMETRY_LOGS=offGITTE_TELEMETRY_DEBUG=1OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_EXPORTER_OTLP_HEADERSDesign / reliability
GITTE_TELEMETRY_DEBUG); batch processors with bounded queues (logs drop on overflow); flush on exit is bounded to ~1s;AlwaysSample.user.name/host.name(to identify the developer/machine), repo name (never the full remote URL). The process environment is never exported.Dependencies
OpenTelemetry traces stack at v1.44.0, logs stack at v0.20.0 (
go.opentelemetry.io/otel{,/sdk,/log,/sdk/log}+ theotlptracehttp/otlploghttpexporters).