otel
stableOpenTelemetry tracing and metrics primitives: generate trace/span IDs, build W3C traceparent headers, create and serialize OTLP spans and metrics as JSON.
use plugin otel::{generate_trace_id, generate_span_id, build_traceparent, …} Functions (17)
- generate_trace_id Generate a random 32-char hex trace ID
- generate_span_id Generate a random 16-char hex span ID
- build_traceparent Build a W3C traceparent header string
- parse_traceparent Parse a traceparent header into parts
- create_span Create a span data table
- add_attribute Add a key-value attribute to a span
- add_event Add a timed event to a span
- spans_to_json Serialize spans to OTLP JSON format
- set_span_status Set the status code of a span
- end_span Record the end timestamp of a span
- create_counter Create a monotonic counter metric
- counter_add Increment a counter by a non-negative delta
- create_gauge Create a gauge metric
- gauge_set Set the current value of a gauge
- create_histogram Create a histogram metric with default buckets
- histogram_record Record a value into a histogram
- metrics_to_json Serialize metrics to OTLP JSON format
Overview
otel provides the low-level building blocks for OpenTelemetry tracing and
metrics without pulling in an SDK. Spans and metrics are plain tables, not opaque
handles, so you create one, transform it through a series of pure functions
(add_attribute, add_event, end_span, counter_add, …) that each return an
updated copy, and finally serialize a collection of them to OTLP JSON. There is
no global tracer state to manage — you own the tables and decide when to flush
them. Use it when you want to emit W3C-compliant trace context and OTLP payloads
to a collector from a Zolo service or job.
The mental model has two halves. For tracing: generate a trace_id and
span_id, build a span with create_span, enrich it with attributes/events,
mark its status and end time, then hand a table of spans to spans_to_json. For
metrics: create a counter, gauge, or histogram, feed it values with the matching
*_add / *_set / *_record function, then serialize with metrics_to_json.
All timestamps are nanoseconds since the Unix epoch.
Common patterns
Trace an operation end-to-end and serialize it to OTLP JSON:
use plugin otel::{generate_trace_id, generate_span_id, create_span, add_attribute, set_span_status, end_span, spans_to_json}
let trace_id = generate_trace_id()
let span_id = generate_span_id()
let span = create_span("http.request", trace_id, span_id, "", 1000000, 0)
let span = add_attribute(span, "http.method", "GET")
let span = set_span_status(span, "ok", "")
let span = end_span(span, 2500000)
print(spans_to_json(#{1: span}))
Propagate trace context across a service boundary with a traceparent header:
use plugin otel::{generate_trace_id, generate_span_id, build_traceparent, parse_traceparent}
let tp = build_traceparent(generate_trace_id(), generate_span_id(), true)
print("send header traceparent: {tp}")
let ctx = parse_traceparent(tp)
print("incoming trace: {ctx["trace_id"]}, sampled flags: {ctx["flags"]}")
Collect a few metrics and export them in one OTLP payload:
use plugin otel::{create_counter, counter_add, create_histogram, histogram_record, metrics_to_json}
let requests = counter_add(create_counter("http.requests", "Total requests", "1"), 1.0)
let latency = histogram_record(create_histogram("http.duration", "Latency", "ms"), 42.0)
print(metrics_to_json(#{1: requests, 2: latency}))
Generate a random 32-char hex trace ID
Generates a cryptographically random 128-bit trace ID encoded as a 32-character lowercase hex string, as required by the W3C Trace Context specification.
use plugin otel::{generate_trace_id}
let trace_id = generate_trace_id()
print("trace: {trace_id}")
Generate a random 16-char hex span ID
Generates a cryptographically random 64-bit span ID encoded as a 16-character lowercase hex string.
use plugin otel::{generate_trace_id, generate_span_id}
let trace_id = generate_trace_id()
let span_id = generate_span_id()
Build a W3C traceparent header string
Builds a W3C traceparent header value in the format "00-{trace_id}-{span_id}-{flags}". sampled defaults to true (flags = "01"). Pass this value as the traceparent HTTP header when making downstream requests.
use plugin otel::{generate_trace_id, generate_span_id, build_traceparent}
let tp = build_traceparent(generate_trace_id(), generate_span_id(), true)
print("traceparent: {tp}")
Parse a traceparent header into parts
Parses a traceparent header string into {version, trace_id, span_id, flags}. Errors if the string does not contain exactly four dash-separated parts.
use plugin otel::{parse_traceparent}
let parts = parse_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
print("trace: {parts["trace_id"]}")
print("sampled: {parts["flags"]}")
Use it to continue an incoming trace by reusing its trace_id as the parent of a
new local span:
use plugin otel::{parse_traceparent, generate_span_id, create_span}
let ctx = parse_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
let child = create_span("handler", ctx["trace_id"], generate_span_id(), ctx["span_id"], 0, 0)
print("parent span: {ctx["span_id"]}")
Create a span data table
Creates a span data table with the given name, IDs, and nanosecond timestamps. parent_span_id is optional (pass "" for a root span). The returned table has attributes and events as empty lists ready to be populated.
use plugin otel::{generate_trace_id, generate_span_id, create_span}
let trace_id = generate_trace_id()
let span_id = generate_span_id()
let span = create_span("http.request", trace_id, span_id, "", 1000000, 0)
Add a key-value attribute to a span
Returns a new span table with the given key-value attribute appended to the attributes list. Attributes can be strings, numbers, or booleans and map to the OTLP attribute format on serialization.
use plugin otel::{create_span, add_attribute}
let span = create_span("db.query", trace_id, span_id, "", 0, 0)
let span = add_attribute(span, "db.system", "postgresql")
let span = add_attribute(span, "db.statement", "SELECT * FROM users")
Attribute values are not limited to strings — numbers and booleans serialize to
their OTLP intValue / doubleValue / boolValue forms:
use plugin otel::{add_attribute}
let span = add_attribute(span, "http.status_code", 200)
let span = add_attribute(span, "cache.hit", false)
Add a timed event to a span
Returns a new span table with a timed event appended to the events list. Each event has a name, timeUnixNano, and optional attributes table. Use for recording notable moments within a span's lifetime.
use plugin otel::{add_event}
let span = add_event(span, "cache.miss", 1500000, #{})
let span = add_event(span, "db.query.start", 1600000, #{})
Serialize spans to OTLP JSON format
Serializes a table of span tables to a pretty-printed OTLP JSON string in the resourceSpans format, ready to POST to an OpenTelemetry collector's /v1/traces endpoint.
use plugin otel::{spans_to_json, end_span}
let span = end_span(span, 2000000)
let json = spans_to_json(#{1: span})
print(json)
Set the status code of a span
Returns a new span table with the status field set. code must be "ok", "error", or "unset". message is optional and used with "error" to provide context.
use plugin otel::{set_span_status}
let span = set_span_status(span, "error", "connection refused")
Record the end timestamp of a span
Returns a new span table with endTimeUnixNano set to end_ns. Call this when the operation being traced completes.
use plugin otel::{end_span}
let span = end_span(span, 9999999)
Create a monotonic counter metric
Creates a monotonic counter metric table. description and unit are optional strings. The counter starts at 0.0 and can only be incremented via counter_add.
use plugin otel::{create_counter, counter_add}
let req_count = create_counter("http.requests", "Total HTTP requests", "1")
let req_count = counter_add(req_count, 1.0)
Increment a counter by a non-negative delta
Returns a new counter table with value incremented by delta. delta must be non-negative — counters are monotonically increasing by definition.
use plugin otel::{counter_add}
let counter = counter_add(counter, 5.0)
print("total: {counter["value"]}")
Create a gauge metric
Creates a gauge metric table that records an instantaneous value. description and unit are optional. Use for metrics that can go up or down, like memory usage or queue depth.
use plugin otel::{create_gauge, gauge_set}
let mem_gauge = create_gauge("process.memory", "RSS memory", "bytes")
let mem_gauge = gauge_set(mem_gauge, 52428800.0)
Set the current value of a gauge
Returns a new gauge table with value set to the given float. Unlike a counter, this replaces the previous value rather than accumulating.
use plugin otel::{gauge_set}
let gauge = gauge_set(gauge, 1024.0)
print("current: {gauge["value"]}")
Create a histogram metric with default buckets
Creates a histogram metric table with default OTel bucket boundaries [0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000]. Tracks count, sum, min, max, and per-bucket counts.
use plugin otel::{create_histogram, histogram_record}
let latency = create_histogram("http.duration", "Request latency", "ms")
let latency = histogram_record(latency, 42.0)
let latency = histogram_record(latency, 153.0)
Record a value into a histogram
Returns a new histogram table with value recorded: increments count, adds to sum, updates min/max, and increments the appropriate bucket counter.
use plugin otel::{histogram_record}
let h = histogram_record(histogram, 78.5)
print("count: {h["count"]} sum: {h["sum"]}")
Recording a series of values accumulates the running totals so you can read back
count, sum, min, and max at any point:
use plugin otel::{create_histogram, histogram_record}
let h = create_histogram("http.duration", "Latency", "ms")
let h = histogram_record(h, 12.0)
let h = histogram_record(h, 305.0)
let h = histogram_record(h, 87.0)
print("min: {h["min"]} max: {h["max"]} sum: {h["sum"]}")
Serialize metrics to OTLP JSON format
Serializes a table of metric tables (counters, gauges, histograms) to a pretty-printed OTLP JSON string in the resourceMetrics format, ready to POST to an OpenTelemetry collector's /v1/metrics endpoint.
use plugin otel::{create_counter, counter_add, create_gauge, gauge_set, metrics_to_json}
let requests = counter_add(create_counter("requests", "", "1"), 42.0)
let memory = gauge_set(create_gauge("memory", "", "bytes"), 1048576.0)
let json = metrics_to_json(#{1: requests, 2: memory})
print(json)