Docs menu

Export and leaving

Export is on for every plan, including when the organization is capped or a payment has failed, and it costs nothing. It needs a key with the read or full scope; an ingest key cannot read.

Export a project

An export streams for as long as the project is large, and it can fail after it has started: the status line is long gone by then, so the engine reports the outcome in HTTP trailers at the end of the stream. Treat the file as usable only when the stream ended with X-Traza-Export-Complete: true. This script does that check and exits non-zero on every other outcome:

#!/usr/bin/env bash
# export.sh: export a project into spans.ndjson; exit 0 only for the whole dataset.
set -u -o pipefail

if ! curl --fail --show-error --silent \
    -H 'TE: trailers' \
    -H "Authorization: Bearer $TRAZA_API_KEY" \
    -D headers.txt -o spans.ndjson \
    'https://api.trazadb.com/v1/export'; then
  echo 'export: the transfer failed (curl says why above); spans.ndjson is not the whole dataset' >&2
  exit 1
fi

if ! grep -qiE '^x-traza-export-complete:[[:space:]]*true[[:space:]]*$' headers.txt; then
  echo 'export: no "X-Traza-Export-Complete: true" trailer; spans.ndjson is not the whole dataset' >&2
  exit 1
fi

count=$(grep -iE '^x-traza-export-count:' headers.txt | tr -dc '0-9')
lines=$(wc -l < spans.ndjson | tr -dc '0-9')
if [ "$count" != "$lines" ]; then
  echo "export: the trailer counts ${count:-no} spans but spans.ndjson holds $lines lines" >&2
  exit 1
fi

echo "export: complete, $lines spans in spans.ndjson"

What each part guards against:

  • TE: trailers asks for the trailers. Over HTTP/1.1 the gateway delivers them only to a client that sent this header (HTTP/2 clients always get them). Without it a complete export arrives with no trailer to check, and an incomplete one is cut off mid-stream.
  • --fail --show-error makes curl exit non-zero on an error status, such as 401 for a wrong key or 403 for an ingest-only key, and print why; --silent alone would hide it. A stream cut after it started also exits non-zero.
  • X-Traza-Export-Complete: true is the only proof of completeness. A false means the stream failed after it started; a missing trailer means the outcome is unknown. Either way, export again.
  • X-Traza-Export-Count is the number of spans the engine sent, checked against the lines in the file.

The body is one span per line in the engine’s native JSON. Export takes the same filters as GET /v1/spans (service, time window, attributes, session), added as query parameters, and is unbounded unless you pass limit. It is a snapshot: spans that arrive while it runs are not in it.

What an export does not contain

  • Large attribute values. The engine moves a string attribute over 256 KiB out of its span at ingest and leaves a {"$payload": "sha256/…", "bytes": …, "preview": "…"} reference in its place. The export carries the reference, not the bytes; fetch them with GET /v1/payloads/sha256/{hex} and put the text back in place of the reference before you import.
  • Annotations. They are a separate surface: read them with GET /v1/annotations.
  • Run fuse policies. They are configuration rather than data: list them with GET /v1/fuses/policies and create them again.

Into your own traza-server

Import only after export.sh reported the export complete. The exported lines are what the native ingest route reads; this script posts them in batches of 1,000 and stops at the first batch the server refuses:

#!/usr/bin/env bash
# import.sh: post spans.ndjson to a self-hosted traza-server in batches.
set -eu -o pipefail
shopt -s nullglob

batches=$(mktemp -d)
trap 'rm -rf "$batches"' EXIT
split -l 1000 spans.ndjson "$batches/batch-"

for f in "$batches"/batch-*; do
  jq -s . "$f" | curl --fail --show-error --silent \
    -H "Authorization: Bearer $TRAZA_TOKEN" -H 'Content-Type: application/json' \
    --data-binary @- 'http://localhost:8080/v1/spans' > /dev/null
done
echo 'import: every batch accepted'

Running it again after a failure is safe: a span is keyed by its tenant, trace id and span id, so posting it again replaces it rather than adding a copy.

Each line carries its $tenant, so the spans land under the same tenant name on your server; strip it with jq -s 'map(del(."$tenant"))' in place of jq -s . to use the default tenant instead.

Deleting a project

Deleting a project in the console revokes its keys, pauses its ingest, erases its tenant on its cell and keeps the erasure’s id, so the receipt can be checked. Export first: deletion cannot be undone.