Skip to content

otel

stable

OpenTelemetry 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, …}
17 functions Observability
/ filter jk navigate Esc clear
Functions (17)
  1. generate_trace_id Generate a random 32-char hex trace ID
  2. generate_span_id Generate a random 16-char hex span ID
  3. build_traceparent Build a W3C traceparent header string
  4. parse_traceparent Parse a traceparent header into parts
  5. create_span Create a span data table
  6. add_attribute Add a key-value attribute to a span
  7. add_event Add a timed event to a span
  8. spans_to_json Serialize spans to OTLP JSON format
  9. set_span_status Set the status code of a span
  10. end_span Record the end timestamp of a span
  11. create_counter Create a monotonic counter metric
  12. counter_add Increment a counter by a non-negative delta
  13. create_gauge Create a gauge metric
  14. gauge_set Set the current value of a gauge
  15. create_histogram Create a histogram metric with default buckets
  16. histogram_record Record a value into a histogram
  17. 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)
enespt-br