Sending to a Server
Send local events to a running Bir server with send_events():
from bir import send_events
result = send_events("http://127.0.0.1:8000")
print(result.accepted, result.attempted, result.skipped)
The helper posts local JSONL events to the server, batching them when supported
and otherwise posting to /v1/events one at a time. Complete traces are sent
root-first. It uses only the Python standard library.
SendEventsResult reports how many events were attempted, newly accepted, and
skipped by an idempotent server response. Local events are not removed after
sending. Re-sending a file is safe against a Bir server because event IDs are
idempotent.
Retry behavior
Network errors, timeouts, and HTTP 5xx responses are retried with exponential backoff. HTTP 4xx responses are treated as permanent and raised immediately.
result = send_events(
"http://127.0.0.1:8000",
retries=3,
backoff=1.0,
timeout=10.0,
)
The delay is backoff * 2**attempt. Defaults are two retries, a 0.5-second
backoff, and a 10-second timeout. A healthy send makes one request attempt.
Mark accepted events locally
Pass mark_sent=True to avoid requesting already accepted events on later
sends:
send_events("http://127.0.0.1:8000", mark_sent=True)
send_events("http://127.0.0.1:8000", mark_sent=True) # skips recorded IDs
Accepted IDs are recorded in <trace_path>.sent, such as
.bir/traces.jsonl.sent. The sidecar never modifies trace JSONL or the event
schema. A missing or corrupt sidecar is treated as empty. With the default
mark_sent=False, no local bookkeeping is written.
Upload size-rotated files
By default send_events() uploads only the active trace file, so events stranded
in size-rotated siblings created by configure(max_bytes=...) are left behind.
Pass include_rotated=True to also upload the retained rotated files:
send_events("http://127.0.0.1:8000", include_rotated=True)
Rotated files (traces.jsonl.1 ..) are uploaded oldest-first, followed by the
active file, preserving write-order chronology. Complete traces are still sent
root-first, and events are deduplicated by ID when a rotated file overlaps the
active one, so each event is sent once. mark_sent=True keeps anchoring its
sidecar to the active trace path, so recorded IDs are skipped across the whole
selected file set. The default stays False (active file only), leaving existing
behavior unchanged.
CLI upload
The same operations are available without writing Python:
bir send --server http://127.0.0.1:8000
bir send --include-rotated --server http://127.0.0.1:8000
bir send --mark-sent --server http://127.0.0.1:8000
bir send --retries 3 --backoff 1.0 --timeout 10 \
--server http://127.0.0.1:8000
bir send-experiment .bir/experiments/<name>-<id>.jsonl \
--server http://127.0.0.1:8000
bir send-experiment .bir/experiments/<name>-<id>.jsonl \
--retries 3 --backoff 1.0
bir send exposes the same knobs as send_events(): --mark-sent records
accepted IDs in the <trace_path>.sent sidecar and skips them on later sends,
and --retries (default 2), --backoff (default 0.5), and --timeout
(default 10) tune transient-failure handling. bir send-experiment shares the
same bounded retry behavior: --retries and --backoff. All of these accept
non-negative values only. See
CLI & Environment Config for all commands and
local evals and experiments for the
Python API.
To forward traces to an OpenTelemetry backend instead of a Bir server, use
bir export-otel (or export_traces_to_otlp()); see
CLI & Environment Config.