Skip to content

Commit f01ac2e

Browse files
Workflow tracing to tilebox observability backend (#37)
* Automatically export workflow traces to tilebox * Update task span attributes * context.logger for task execute methods * Query job logs and traces * to_pandas conversion for job logs/traces
1 parent 064ae7e commit f01ac2e

36 files changed

Lines changed: 2816 additions & 1393 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ coverage.xml
1717
test-report.xml
1818

1919
*.egg-info/
20+
21+
.amp/

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repos:
99
hooks:
1010
- id: sync-with-uv
1111
- repo: https://github.com/charliermarsh/ruff-pre-commit
12-
rev: v0.15.7
12+
rev: v0.15.12
1313
hooks:
1414
- id: ruff-check
1515
args: [--fix, --exit-non-zero-on-fix]

CHANGELOG.md

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

88
## [Unreleased]
99

10+
## [0.52.0]
11+
12+
### Added
13+
14+
- `tilebox-workflows`: Added automatic OpenTelemetry-based logging and tracing instrumentation to a Tilebox
15+
observability backend.
16+
- `tilebox-workflows`: Added `ExecutionContext.logger` and `ExecutionContext.tracer` access for task implementations to
17+
emit structured logs and custom spans during task execution.
18+
- `tilebox-workflows`: Added `JobClient.query_logs()` and `JobClient.query_spans()` to fetch telemetry for a job, with
19+
list-like results that can be converted to pandas DataFrames via `to_pandas()`.
20+
1021
## [0.51.0] - 2026-04-07
1122

1223
### Changed
@@ -357,7 +368,8 @@ the first client that does not cache data (since it's already on the local file
357368
- Released under the [MIT](https://opensource.org/license/mit) license.
358369
- Released packages: `tilebox-datasets`, `tilebox-workflows`, `tilebox-storage`, `tilebox-grpc`
359370

360-
[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.51.0...HEAD
371+
[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.52.0...HEAD
372+
[0.52.0]: https://github.com/tilebox/tilebox-python/compare/v0.51.0...v0.52.0
361373
[0.51.0]: https://github.com/tilebox/tilebox-python/compare/v0.50.1...v0.51.0
362374
[0.50.1]: https://github.com/tilebox/tilebox-python/compare/v0.50.0...v0.50.1
363375
[0.50.0]: https://github.com/tilebox/tilebox-python/compare/v0.49.0...v0.50.0

tilebox-datasets/tests/data/datapoint.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
from tests.query.pagination import paginations
3333
from tests.query.time_interval import i64_datetimes
3434
from tilebox.datasets.data.datapoint import AnyMessage, IngestResponse, QueryResultPage, RepeatedAny
35-
from tilebox.datasets.datasets.v1 import core_pb2
36-
from tilebox.datasets.query.time_interval import datetime_to_timestamp
3735

3836

3937
@composite
@@ -138,8 +136,7 @@ def example_pandas_datapoints(draw: DrawFn) -> pd.DataFrame:
138136
@composite
139137
def anys(draw: DrawFn, generated_fields: bool = False, missing_fields: bool = False) -> AnyMessage:
140138
"""A hypothesis strategy for generating random Any messages"""
141-
# we need a random byte string here, but let's actually use a valid protobuf message, in this
142-
# case because its easy to generate let's use a DatapointMetadata message
139+
# we need a random byte string here, but let's actually use a valid protobuf message
143140
datapoint = draw(example_datapoints(generated_fields, missing_fields))
144141
return AnyMessage(example_dataset_type_url(), datapoint.SerializeToString())
145142

@@ -158,14 +155,6 @@ def repeated_anys(
158155
return RepeatedAny(example_dataset_type_url(), [dp.SerializeToString() for dp in datapoints])
159156

160157

161-
@composite
162-
def datapoint_metadata_messages(draw: DrawFn) -> core_pb2.DatapointMetadata:
163-
event_time = datetime_to_timestamp(draw(i64_datetimes))
164-
ingestion_time = datetime_to_timestamp(draw(i64_datetimes))
165-
data_point_id = str(draw(uuids(version=4)))
166-
return core_pb2.DatapointMetadata(event_time=event_time, ingestion_time=ingestion_time, id=data_point_id)
167-
168-
169158
@composite
170159
def query_result_pages(
171160
draw: DrawFn, empty_next_page: bool | None = None, generated_fields: bool = True, missing_fields: bool = False

tilebox-datasets/tilebox/datasets/buf/validate/validate_pb2.py

Lines changed: 217 additions & 213 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tilebox-datasets/tilebox/datasets/buf/validate/validate_pb2.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ class BoolRules(_message.Message):
388388
def __init__(self, const: bool = ..., example: _Optional[_Iterable[bool]] = ...) -> None: ...
389389

390390
class StringRules(_message.Message):
391-
__slots__ = ("const", "len", "min_len", "max_len", "len_bytes", "min_bytes", "max_bytes", "pattern", "prefix", "suffix", "contains", "not_contains", "not_in", "email", "hostname", "ip", "ipv4", "ipv6", "uri", "uri_ref", "address", "uuid", "tuuid", "ip_with_prefixlen", "ipv4_with_prefixlen", "ipv6_with_prefixlen", "ip_prefix", "ipv4_prefix", "ipv6_prefix", "host_and_port", "ulid", "well_known_regex", "strict", "example")
391+
__slots__ = ("const", "len", "min_len", "max_len", "len_bytes", "min_bytes", "max_bytes", "pattern", "prefix", "suffix", "contains", "not_contains", "not_in", "email", "hostname", "ip", "ipv4", "ipv6", "uri", "uri_ref", "address", "uuid", "tuuid", "ip_with_prefixlen", "ipv4_with_prefixlen", "ipv6_with_prefixlen", "ip_prefix", "ipv4_prefix", "ipv6_prefix", "host_and_port", "ulid", "protobuf_fqn", "protobuf_dot_fqn", "well_known_regex", "strict", "example")
392392
Extensions: _python_message._ExtensionDict
393393
CONST_FIELD_NUMBER: _ClassVar[int]
394394
LEN_FIELD_NUMBER: _ClassVar[int]
@@ -422,6 +422,8 @@ class StringRules(_message.Message):
422422
IPV6_PREFIX_FIELD_NUMBER: _ClassVar[int]
423423
HOST_AND_PORT_FIELD_NUMBER: _ClassVar[int]
424424
ULID_FIELD_NUMBER: _ClassVar[int]
425+
PROTOBUF_FQN_FIELD_NUMBER: _ClassVar[int]
426+
PROTOBUF_DOT_FQN_FIELD_NUMBER: _ClassVar[int]
425427
WELL_KNOWN_REGEX_FIELD_NUMBER: _ClassVar[int]
426428
STRICT_FIELD_NUMBER: _ClassVar[int]
427429
EXAMPLE_FIELD_NUMBER: _ClassVar[int]
@@ -456,10 +458,12 @@ class StringRules(_message.Message):
456458
ipv6_prefix: bool
457459
host_and_port: bool
458460
ulid: bool
461+
protobuf_fqn: bool
462+
protobuf_dot_fqn: bool
459463
well_known_regex: KnownRegex
460464
strict: bool
461465
example: _containers.RepeatedScalarFieldContainer[str]
462-
def __init__(self, const: _Optional[str] = ..., len: _Optional[int] = ..., min_len: _Optional[int] = ..., max_len: _Optional[int] = ..., len_bytes: _Optional[int] = ..., min_bytes: _Optional[int] = ..., max_bytes: _Optional[int] = ..., pattern: _Optional[str] = ..., prefix: _Optional[str] = ..., suffix: _Optional[str] = ..., contains: _Optional[str] = ..., not_contains: _Optional[str] = ..., not_in: _Optional[_Iterable[str]] = ..., email: bool = ..., hostname: bool = ..., ip: bool = ..., ipv4: bool = ..., ipv6: bool = ..., uri: bool = ..., uri_ref: bool = ..., address: bool = ..., uuid: bool = ..., tuuid: bool = ..., ip_with_prefixlen: bool = ..., ipv4_with_prefixlen: bool = ..., ipv6_with_prefixlen: bool = ..., ip_prefix: bool = ..., ipv4_prefix: bool = ..., ipv6_prefix: bool = ..., host_and_port: bool = ..., ulid: bool = ..., well_known_regex: _Optional[_Union[KnownRegex, str]] = ..., strict: bool = ..., example: _Optional[_Iterable[str]] = ..., **kwargs) -> None: ...
466+
def __init__(self, const: _Optional[str] = ..., len: _Optional[int] = ..., min_len: _Optional[int] = ..., max_len: _Optional[int] = ..., len_bytes: _Optional[int] = ..., min_bytes: _Optional[int] = ..., max_bytes: _Optional[int] = ..., pattern: _Optional[str] = ..., prefix: _Optional[str] = ..., suffix: _Optional[str] = ..., contains: _Optional[str] = ..., not_contains: _Optional[str] = ..., not_in: _Optional[_Iterable[str]] = ..., email: bool = ..., hostname: bool = ..., ip: bool = ..., ipv4: bool = ..., ipv6: bool = ..., uri: bool = ..., uri_ref: bool = ..., address: bool = ..., uuid: bool = ..., tuuid: bool = ..., ip_with_prefixlen: bool = ..., ipv4_with_prefixlen: bool = ..., ipv6_with_prefixlen: bool = ..., ip_prefix: bool = ..., ipv4_prefix: bool = ..., ipv6_prefix: bool = ..., host_and_port: bool = ..., ulid: bool = ..., protobuf_fqn: bool = ..., protobuf_dot_fqn: bool = ..., well_known_regex: _Optional[_Union[KnownRegex, str]] = ..., strict: bool = ..., example: _Optional[_Iterable[str]] = ..., **kwargs) -> None: ...
463467

464468
class BytesRules(_message.Message):
465469
__slots__ = ("const", "len", "min_len", "max_len", "pattern", "prefix", "suffix", "contains", "not_in", "ip", "ipv4", "ipv6", "uuid", "example")

tilebox-datasets/tilebox/datasets/datasets/v1/core_pb2.py

Lines changed: 17 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)