Query telemetry
Query workflow logs and spans for a job from Python or Go.
Tilebox stores logs and spans for each workflow job. Use the jobs client to query that telemetry from notebooks, scripts, or automated diagnostics.
Query job logs
query_logs() returns a LogRecords list. Pagination is handled automatically.
from tilebox.workflows import Client
client = Client()
job = client.jobs().find("019e07b1-916b-0630-f3ba-f1c33235d174")
logs = client.jobs().query_logs(job)
for record in logs:
print(record.time, record.severity_text, record.body)
print(record.attributes)package main
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/tilebox/tilebox-go/workflows/v1"
)
func main() {
ctx := context.Background()
client := workflows.NewClient()
jobID := uuid.MustParse("019e07b1-916b-0630-f3ba-f1c33235d174")
for record, err := range client.Jobs.QueryLogs(
ctx,
jobID,
workflows.WithSortDirection(workflows.Ascending),
) {
if err != nil {
slog.ErrorContext(ctx, "failed to query job logs", slog.Any("error", err))
return
}
fmt.Println(record.Time.Format(time.RFC3339Nano), record.Level, record.Body)
fmt.Println(record.Attributes)
}
}Each log record includes:
timeseverity_numberandseverity_textbodytrace_idandspan_idattributesrunner_attributes
As pandas DataFrame
Use to_pandas() to convert log records to a pandas DataFrame.
Python
logs_df = client.jobs().query_logs(job).to_pandas()
logs_df[["time", "severity_text", "body"]]
Query job spans
query_spans() returns a Spans list. Pagination is handled automatically.
spans = client.jobs().query_spans(job.id)
for span in spans:
print(span.name, span.status_code, span.duration)
print(span.attributes)package main
import (
"context"
"fmt"
"log/slog"
"github.com/google/uuid"
"github.com/tilebox/tilebox-go/workflows/v1"
)
func main() {
ctx := context.Background()
client := workflows.NewClient()
jobID := uuid.MustParse("019e07b1-916b-0630-f3ba-f1c33235d174")
for span, err := range client.Jobs.QuerySpans(
ctx,
jobID,
workflows.WithSortDirection(workflows.Ascending),
) {
if err != nil {
slog.ErrorContext(ctx, "failed to query job spans", slog.Any("error", err))
return
}
fmt.Println(span.Name, span.StatusCode, span.Duration())
fmt.Println(span.Attributes)
}
}Each span includes:
start_timeandend_timedurationtrace_id,span_id, andparent_span_idnamestatus_codeandstatus_messageattributesrunner_attributesevents
As pandas DataFrame
Use to_pandas() to convert spans to a pandas DataFrame.
Python
spans_df = client.jobs().query_spans(job).to_pandas()
slow_spans = spans_df.sort_values("duration", ascending=False).head(10)
slow_spans[["name", "duration", "start_time"]]
Nested attributes, runner_attributes, and events stay as Python objects in DataFrame columns. Span DataFrames include a computed duration column.