Skip to content

Commit 1afae8b

Browse files
Opentelemetry instrumentation (#205)
1 parent bd117d3 commit 1afae8b

46 files changed

Lines changed: 924 additions & 503 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ jobs:
4141
- name: Lint
4242
uses: golangci/golangci-lint-action@v9
4343
with:
44-
version: v2.10.1
44+
version: v2.12.2

.golangci.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
---
33
version: "2"
44
linters:
5-
enable: # list taken from https://golangci-lint.run/usage/linters/ - last updated 2026-01-09 for v2.8.0
5+
enable: # list taken from https://golangci-lint.run/usage/linters/ - last updated 2026-05-11 for v2.12.2
66
# enabled by default, but list them here to be explicit
77
- errcheck
88
- govet
@@ -16,6 +16,7 @@ linters:
1616
- bidichk # checks for dangerous unicode character sequences
1717
- bodyclose # checks whether HTTP response body is closed successfully
1818
- canonicalheader # checks for canonical names in HTTP headers
19+
- clickhouselint # detects common mistakes with the ClickHouse native Go driver API
1920
- containedctx # detects struct contained context.Context field
2021
#- contextcheck # checks for inherited context.Context
2122
- copyloopvar # detects places where loop variables are copied
@@ -45,15 +46,16 @@ linters:
4546
- gochecknoinits # checks that no init functions are present in Go code
4647
- gochecksumtype # checks exhaustiveness on Go "sum types"
4748
#- gocognit # Computes and checks the cognitive complexity of functions
48-
- goconst # finds repeated strings that could be replaced by a constant
49+
#- goconst # finds repeated strings that could be replaced by a constant
4950
- gocritic # provides diagnostics that check for bugs, performance and style issues
5051
#- gocyclo # Computes and checks the cyclomatic complexity of functions
5152
- godoclint # Checks golang docs best practices (godoc)
5253
#- godot # Check if comments end in a period
5354
#- godox # Tool for detection of FIXME, TODO and other comment keywords
5455
#- goheader # Checks is file header matches to pattern
5556
- gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
56-
- gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
57+
#- gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
58+
- gomodguard_v2 # Allow and blocklist linter for direct Go module dependencies
5759
- goprintffuncname # checks that printf-like functions are named with f at the end
5860
- gosec # inspects source code for security problems
5961
- gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase.
@@ -171,4 +173,4 @@ formatters:
171173
- gci
172174
- gofmt
173175
- gofumpt
174-
- goimports
176+
- goimports

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.5.0] - 2026-05-11
11+
1012
### Added
1113

1214
- `workflows`: Added `client.Jobs.QueryLogs` and `client.Jobs.QuerySpans` to query logs and trace spans for a job, plus a telemetry query example.
15+
- `workflows`: Added `ConfigureConsoleLogging` to enable console log output that composes with the Tilebox OpenTelemetry log exporter.
16+
- `workflows`: Added `WithSpan` and `WithSpanResult` helpers to start spans from the current task execution context without manually passing a tracer.
17+
18+
### Changed
19+
20+
- `workflows`: Correlate context-aware task logs with traces by adding `trace_id`, `span_id`, and `task_id` attributes and recording log messages as span events.
21+
- `examples`: Updated workflow and dataset examples to use context-aware `slog` methods.
1322

1423
## [0.4.0] - 2026-03-06
1524

@@ -81,7 +90,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8190
- Added support for Tilebox Observability, including logging and tracing helpers.
8291
- Added examples for using the library.
8392

84-
[Unreleased]: https://github.com/tilebox/tilebox-go/compare/v0.4.0...HEAD
93+
[Unreleased]: https://github.com/tilebox/tilebox-go/compare/v0.5.0...HEAD
94+
[0.5.0]: https://github.com/tilebox/tilebox-go/compare/v0.4.0...v0.5.0
8595
[0.4.0]: https://github.com/tilebox/tilebox-go/compare/v0.3.2...v0.4.0
8696
[0.3.2]: https://github.com/tilebox/tilebox-go/compare/v0.3.1...v0.3.2
8797
[0.3.1]: https://github.com/tilebox/tilebox-go/compare/v0.3.0...v0.3.1

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ For examples on how to use the library, see the [examples](examples) directory.
4848

4949
### Writing a Task
5050

51-
Here we define a simple task that prints "Hello World!" to the console:
51+
Here we define a simple task that logs "Hello World!":
5252

5353
```go
5454
package helloworld
@@ -65,8 +65,8 @@ type HelloTask struct {
6565
}
6666

6767
// The Execute method isn't needed to submit a task but is required to run a task.
68-
func (t *HelloTask) Execute(context.Context) error {
69-
slog.Info("Hello World!", "Name", t.Name)
68+
func (t *HelloTask) Execute(ctx context.Context) error {
69+
slog.InfoContext(ctx, "Hello World!", slog.String("Name", t.Name))
7070
return nil
7171
}
7272

@@ -96,6 +96,7 @@ type HelloTask struct {
9696

9797
func main() {
9898
ctx := context.Background()
99+
workflows.ConfigureConsoleLogging(slog.LevelInfo)
99100
client := workflows.NewClient()
100101

101102
job, err := client.Jobs.Submit(ctx, "hello-world",
@@ -106,14 +107,16 @@ func main() {
106107
},
107108
)
108109
if err != nil {
109-
slog.Error("Failed to submit job", slog.Any("error", err))
110+
slog.ErrorContext(ctx, "Failed to submit job", slog.Any("error", err))
110111
return
111112
}
112113

113-
slog.Info("Job submitted", slog.String("job_id", job.ID.String()))
114+
slog.InfoContext(ctx, "Job submitted", slog.String("job_id", job.ID.String()))
114115
}
115116
```
116117

118+
`workflows.NewClient()` configures Tilebox OpenTelemetry export when an API key is available. `workflows.ConfigureConsoleLogging` adds console output without replacing the Tilebox exporter, so logs are written to both destinations when both are configured.
119+
117120
### Running a Worker
118121

119122
Here we create a TaskRunner and run a worker that is capable of executing `HelloTask` tasks:
@@ -133,32 +136,32 @@ type HelloTask struct {
133136
}
134137

135138
// The Execute method is required to run a task.
136-
func (t *HelloTask) Execute(context.Context) error {
137-
slog.Info("Hello World!", "Name", t.Name)
139+
func (t *HelloTask) Execute(ctx context.Context) error {
140+
slog.InfoContext(ctx, "Hello World!", slog.String("Name", t.Name))
138141
return nil
139142
}
140143

141144
func main() {
142145
ctx := context.Background()
146+
workflows.ConfigureConsoleLogging(slog.LevelInfo)
143147
client := workflows.NewClient()
144-
148+
145149
runner, err := client.NewTaskRunner(ctx)
146150
if err != nil {
147-
slog.Error("failed to create task runner", slog.Any("error", err))
151+
slog.ErrorContext(ctx, "failed to create task runner", slog.Any("error", err))
148152
return
149153
}
150154

151155
err = runner.RegisterTasks(&HelloTask{})
152156
if err != nil {
153-
slog.Error("failed to register tasks", slog.Any("error", err))
157+
slog.ErrorContext(ctx, "failed to register tasks", slog.Any("error", err))
154158
return
155159
}
156160

157161
runner.RunForever(ctx)
158162
}
159163
```
160164

161-
162165
## License
163166

164167
Distributed under the MIT License (`The MIT License`).

datasets/v1/datapoints.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ func validateDatapoints(datapoints any) error {
335335
if slice.Kind() != reflect.Slice {
336336
return fmt.Errorf("datapoints must be a pointer to a slice, got %v", reflect.TypeOf(datapoints))
337337
}
338-
if slice.Type().Elem().Kind() != reflect.Ptr {
338+
if slice.Type().Elem().Kind() != reflect.Pointer {
339339
return fmt.Errorf("datapoints must be a pointer to a slice of proto.Message, got %v", reflect.TypeOf(datapoints))
340340
}
341341
messageType := reflect.TypeFor[proto.Message]()

examples/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,24 @@ Head over to [Tilebox Console](https://console.tilebox.com/account/api-keys) if
1212
Each example can be run using the following command:
1313

1414
```bash
15-
go run ./<example-folder>
15+
go run ./examples/<example-folder>
1616
```
1717

18-
For example, to run the `workflows/helloworld/submitter` example:
19-
x
18+
For example, to run the `workflows/helloworld` example:
19+
2020
```bash
21-
go run ./workflows/helloworld/submitter
21+
go run ./examples/workflows/helloworld
2222
```
2323

24+
Workflow examples submit a job first, then start a task runner in the same process.
25+
2426
## Workflows examples
2527

2628
- [Hello world](workflows/helloworld): How to submit a task and run it.
29+
- [MapReduce](workflows/mapreduce): How to fan out tasks and submit dependent reduce tasks.
30+
- [Progress](workflows/progress): How to report workflow progress.
2731
- [Protobuf tasks](workflows/protobuf-task): How to use Protobuf tasks.
28-
- [Axiom Observability](workflows/axiom): How to set up tracing and logging for workflows using [Axiom](https://axiom.co/) observability platform.
29-
- [OpenTelemetry Observability](workflows/opentelemetry): How to set up tracing and logging for workflows using [OpenTelemetry](https://opentelemetry.io/).
32+
- [Observability](workflows/observability): How to query Tilebox workflow telemetry and export traces/logs to custom Axiom or OpenTelemetry backends.
3033

3134
## Datasets examples
3235

examples/datasets/create/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func main() {
2626
},
2727
)
2828
if err != nil {
29-
slog.Error("Failed to create dataset", slog.Any("error", err))
29+
slog.ErrorContext(ctx, "Failed to create dataset", slog.Any("error", err))
3030
return
3131
}
3232
slog.InfoContext(ctx, "Created dataset", slog.String("dataset_id", dataset.ID.String()))
@@ -44,7 +44,7 @@ func main() {
4444
},
4545
)
4646
if err != nil {
47-
slog.Error("Failed to update dataset", slog.Any("error", err))
47+
slog.ErrorContext(ctx, "Failed to update dataset", slog.Any("error", err))
4848
return
4949
}
5050

examples/datasets/ingest/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ func main() {
2020
// Select a dataset
2121
dataset, err := client.Datasets.Get(ctx, "open_data.copernicus.sentinel2_msi")
2222
if err != nil {
23-
slog.Error("Failed to get dataset", slog.Any("error", err))
23+
slog.ErrorContext(ctx, "Failed to get dataset", slog.Any("error", err))
2424
return
2525
}
2626

2727
// Create a collection
2828
collection, err := client.Collections.Create(ctx, dataset.ID, "My First Collection")
2929
if err != nil {
30-
slog.Error("Failed to create collection", slog.Any("error", err))
30+
slog.ErrorContext(ctx, "Failed to create collection", slog.Any("error", err))
3131
return
3232
}
3333

@@ -47,16 +47,16 @@ func main() {
4747
// Ingest datapoints
4848
ingestResponse, err := client.Datapoints.Ingest(ctx, collection.ID, &datapoints, false)
4949
if err != nil {
50-
slog.Error("Failed to ingest datapoints", slog.Any("error", err))
50+
slog.ErrorContext(ctx, "Failed to ingest datapoints", slog.Any("error", err))
5151
return
5252
}
53-
slog.Info("Ingested datapoints", slog.Int64("created", ingestResponse.NumCreated))
53+
slog.InfoContext(ctx, "Ingested datapoints", slog.Int64("created", ingestResponse.NumCreated))
5454

5555
// Delete datapoints again
5656
numDeleted, err := client.Datapoints.DeleteIDs(ctx, collection.ID, ingestResponse.DatapointIDs)
5757
if err != nil {
58-
slog.Error("Failed to delete datapoints", slog.Any("error", err))
58+
slog.ErrorContext(ctx, "Failed to delete datapoints", slog.Any("error", err))
5959
return
6060
}
61-
slog.Info("Deleted datapoints", slog.Int64("deleted", numDeleted))
61+
slog.InfoContext(ctx, "Deleted datapoints", slog.Int64("deleted", numDeleted))
6262
}

examples/datasets/query/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ func main() {
2121
// Select a dataset
2222
dataset, err := client.Datasets.Get(ctx, "open_data.copernicus.sentinel2_msi")
2323
if err != nil {
24-
slog.Error("Failed to get dataset", slog.Any("error", err))
24+
slog.ErrorContext(ctx, "Failed to get dataset", slog.Any("error", err))
2525
return
2626
}
2727

2828
// Select a collection
2929
collection, err := client.Collections.Get(ctx, dataset.ID, "S2A_S2MSI1C")
3030
if err != nil {
31-
slog.Error("Failed to get collection", slog.Any("error", err))
31+
slog.ErrorContext(ctx, "Failed to get collection", slog.Any("error", err))
3232
return
3333
}
3434

@@ -52,13 +52,13 @@ func main() {
5252
datasets.WithSpatialExtent(area),
5353
)
5454
if err != nil {
55-
slog.Error("Failed to query datapoints", slog.Any("error", err))
55+
slog.ErrorContext(ctx, "Failed to query datapoints", slog.Any("error", err))
5656
return
5757
}
5858

59-
slog.Info("Found datapoints over Colorado in March 2025", slog.Int("count", len(foundDatapoints)))
59+
slog.InfoContext(ctx, "Found datapoints over Colorado in March 2025", slog.Int("count", len(foundDatapoints)))
6060
if len(foundDatapoints) > 0 {
61-
slog.Info("First datapoint over Colorado",
61+
slog.InfoContext(ctx, "First datapoint over Colorado",
6262
slog.String("id", foundDatapoints[0].GetId().AsUUID().String()),
6363
slog.Time("event time", foundDatapoints[0].GetTime().AsTime()),
6464
slog.Time("ingestion time", foundDatapoints[0].GetIngestionTime().AsTime()),

examples/workflows/axiom/submitter/main.go

Lines changed: 0 additions & 55 deletions
This file was deleted.

0 commit comments

Comments
 (0)