Skip to content

feat: add OpenTelemetry support for task execution - #2933

Open
lwencel-priv wants to merge 2 commits into
go-task:mainfrom
Margays:otel-support
Open

feat: add OpenTelemetry support for task execution#2933
lwencel-priv wants to merge 2 commits into
go-task:mainfrom
Margays:otel-support

Conversation

@lwencel-priv

@lwencel-priv lwencel-priv commented Jul 23, 2026

Copy link
Copy Markdown

Introduce OpenTelemetry-based tracing to the project, enabling distributed tracing and improved observability for task and command execution. It adds a new telemetry initialization module, instruments core execution paths with trace spans and attributes, and updates dependencies to include the necessary OpenTelemetry packages.

Example output with export OTEL_TRACES_EXPORTER=console:

{"Name":"shell[0]","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"d1fe586ea540da61","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"f4849205374edb00","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:29:54.84908243Z","EndTime":"2026-07-23T20:30:17.75387468Z","Attributes":[{"Key":"command.index","Value":{"Type":"STRING","Value":"0"}},{"Key":"command.defer","Value":{"Type":"BOOL","Value":false}},{"Key":"command.type","Value":{"Type":"STRING","Value":"shell"}},{"Key":"command.shell","Value":{"Type":"STRING","Value":"golangci-lint run"}},{"Key":"command.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":0,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"lint","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"f4849205374edb00","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"d22a17d82e64dc8a","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:29:54.556372703Z","EndTime":"2026-07-23T20:30:17.753891055Z","Attributes":[{"Key":"task.name","Value":{"Type":"STRING","Value":"lint"}},{"Key":"task.call","Value":{"Type":"STRING","Value":"lint"}},{"Key":"task.indirect","Value":{"Type":"BOOL","Value":true}},{"Key":"task.silent","Value":{"Type":"BOOL","Value":false}},{"Key":"task.watch","Value":{"Type":"BOOL","Value":false}},{"Key":"task.dir","Value":{"Type":"STRING","Value":"/workspaces/task"}},{"Key":"task.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":1,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"task:lint","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"d22a17d82e64dc8a","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"8620918a29070478","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:29:54.556202746Z","EndTime":"2026-07-23T20:30:17.753895847Z","Attributes":[{"Key":"command.index","Value":{"Type":"STRING","Value":"0"}},{"Key":"command.defer","Value":{"Type":"BOOL","Value":false}},{"Key":"command.type","Value":{"Type":"STRING","Value":"task"}},{"Key":"command.task","Value":{"Type":"STRING","Value":"lint"}},{"Key":"command.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":1,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"shell[0]","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"92b588e810fc1b46","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"767f71bb024d84cf","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:30:17.75532809Z","EndTime":"2026-07-23T20:30:24.206736563Z","Attributes":[{"Key":"command.index","Value":{"Type":"STRING","Value":"0"}},{"Key":"command.defer","Value":{"Type":"BOOL","Value":false}},{"Key":"command.type","Value":{"Type":"STRING","Value":"shell"}},{"Key":"command.shell","Value":{"Type":"STRING","Value":"go install gotest.tools/gotestsum@latest"}},{"Key":"command.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":0,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"gotestsum:install","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"767f71bb024d84cf","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"cfe7c036baeaa240","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:30:17.754852759Z","EndTime":"2026-07-23T20:30:24.206750147Z","Attributes":[{"Key":"task.name","Value":{"Type":"STRING","Value":"gotestsum:install"}},{"Key":"task.call","Value":{"Type":"STRING","Value":"gotestsum:install"}},{"Key":"task.indirect","Value":{"Type":"BOOL","Value":true}},{"Key":"task.silent","Value":{"Type":"BOOL","Value":false}},{"Key":"task.watch","Value":{"Type":"BOOL","Value":false}},{"Key":"task.dir","Value":{"Type":"STRING","Value":"/workspaces/task"}},{"Key":"task.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":1,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"shell[0]","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"4e2907facf929fe9","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"cfe7c036baeaa240","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:30:24.857795412Z","EndTime":"2026-07-23T20:30:45.571448546Z","Attributes":[{"Key":"command.index","Value":{"Type":"STRING","Value":"0"}},{"Key":"command.defer","Value":{"Type":"BOOL","Value":false}},{"Key":"command.type","Value":{"Type":"STRING","Value":"shell"}},{"Key":"command.shell","Value":{"Type":"STRING","Value":"gotestsum -f 'pkgname' ./..."}},{"Key":"command.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":0,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"test","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"cfe7c036baeaa240","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"ae9c8548f08f3cef","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:30:17.754370678Z","EndTime":"2026-07-23T20:30:45.571468171Z","Attributes":[{"Key":"task.name","Value":{"Type":"STRING","Value":"test"}},{"Key":"task.call","Value":{"Type":"STRING","Value":"test"}},{"Key":"task.indirect","Value":{"Type":"BOOL","Value":true}},{"Key":"task.silent","Value":{"Type":"BOOL","Value":false}},{"Key":"task.watch","Value":{"Type":"BOOL","Value":false}},{"Key":"task.dir","Value":{"Type":"STRING","Value":"/workspaces/task"}},{"Key":"task.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":2,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"task:test","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"ae9c8548f08f3cef","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"8620918a29070478","TraceFlags":"01","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:30:17.753916847Z","EndTime":"2026-07-23T20:30:45.571470421Z","Attributes":[{"Key":"command.index","Value":{"Type":"STRING","Value":"1"}},{"Key":"command.defer","Value":{"Type":"BOOL","Value":false}},{"Key":"command.type","Value":{"Type":"STRING","Value":"task"}},{"Key":"command.task","Value":{"Type":"STRING","Value":"test"}},{"Key":"command.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":1,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}
{"Name":"default","SpanContext":{"TraceID":"807d532209df37b0b3855588e976a287","SpanID":"8620918a29070478","TraceFlags":"01","TraceState":"","Remote":false},"Parent":{"TraceID":"00000000000000000000000000000000","SpanID":"0000000000000000","TraceFlags":"00","TraceState":"","Remote":false},"SpanKind":1,"StartTime":"2026-07-23T20:29:54.555981289Z","EndTime":"2026-07-23T20:30:45.571471588Z","Attributes":[{"Key":"task.name","Value":{"Type":"STRING","Value":"default"}},{"Key":"task.call","Value":{"Type":"STRING","Value":"default"}},{"Key":"task.indirect","Value":{"Type":"BOOL","Value":false}},{"Key":"task.silent","Value":{"Type":"BOOL","Value":false}},{"Key":"task.watch","Value":{"Type":"BOOL","Value":false}},{"Key":"task.dir","Value":{"Type":"STRING","Value":"/workspaces/task"}},{"Key":"task.outcome","Value":{"Type":"STRING","Value":"executed"}}],"Events":null,"Links":null,"Status":{"Code":"Ok","Description":""},"DroppedAttributes":0,"DroppedEvents":0,"DroppedLinks":0,"ChildSpanCount":2,"Resource":[{"Key":"service.name","Value":{"Type":"STRING","Value":"unknown_service:task"}},{"Key":"telemetry.sdk.language","Value":{"Type":"STRING","Value":"go"}},{"Key":"telemetry.sdk.name","Value":{"Type":"STRING","Value":"opentelemetry"}},{"Key":"telemetry.sdk.version","Value":{"Type":"STRING","Value":"1.44.0"}}],"InstrumentationScope":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null},"InstrumentationLibrary":{"Name":"github.com/go-task/task/v3","Version":"","SchemaURL":"","Attributes":null}}

@lwencel-priv
lwencel-priv force-pushed the otel-support branch 3 times, most recently from def6093 to b1a54a0 Compare July 23, 2026 20:32
@andreynering

Copy link
Copy Markdown
Member

Hey @lwencel-priv!

I'm not familiar to OpenTelemetry.

How is this useful to you? What's your use case?

@lwencel-priv

Copy link
Copy Markdown
Author
image I leverage OpenTelemetry to monitor CI/CD pipeline performance and system health comprehensively. The telemetry platform allows to measure execution duration for individual tasks, conduct comparative analysis to identify performance trends, and pinpoint failure locations with precision for faster incident resolution.

Following env variables allows to send data to Jaeger:

OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_COMPRESSION=gzip
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<otel-endpoint>/v1/traces
OTEL_SERVICE_NAME=taskfile-ci

# Required only in case of self-signed certificates
OTEL_EXPORTER_OTLP_CERTIFICATE=/etc/ssl/certs/ca-certificates.crt

@vmaerten

Copy link
Copy Markdown
Member

Hello!

Like @andreynering said, could you explain your use case in a bit more detail? Also, could you open an issue so we can discuss it there?

Even if the change is small, adding a dependency isn't something we do lightly, especially one like Prometheus. We'd like to see if there's broader demand for it first.

An issue also gives other users a chance to share their use cases and 👍 it if they have the same need.

@trulede

trulede commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

@lwencel-priv this reminds me of #2617 which uses the Go slog to a similar effect.

Could you use that slog alongside an OpenTelemetry Collector/Sidecar (or similar) to get a same effect. A more generic approach, like log/slog, would give people the opportunity in integrate whatever logging infrastructure they need.

@lwencel-priv

Copy link
Copy Markdown
Author

Good point — slog and OpenTelemetry can carry similar execution metadata, but they model it differently.

log/slog OpenTelemetry traces
Primary model Structured events/logs Spans with lifecycle
Duration Must be calculated from events Native start/end
Parent/child tasks Custom fields + reconstruction Native span hierarchy
Parallel execution Possible, but requires correlation Native
Jaeger/Tempo visualization Requires conversion/reconstruction Direct
Dependency footprint Very small / stdlib Larger
Generic logging integrations Excellent Focused on observability

Using slog together with an OTel Collector would normally produce OTel logs, not traces. To get the same visualization as this PR, something downstream would still need to correlate task.start/task.finish, IDs, parents, durations, failures, etc. — effectively reconstructing spans.

So I think slog is a good fit for generic structured lifecycle events, while traces are a better semantic fit for the execution tree/timing use case here.

That said, I agree the current OTel dependency footprint is worth looking at. A traces-only OTLP integration, without autoexport, may be a better compromise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants