Skip to content

Getting data out, and bringing it in

Getting data out, and bringing it in

telemetryd export --since 24h > dump.ndjson
telemetryd import --file dump.ndjson --url http://other-host:4319

Neither command reads the other side's storage. Both work through the Loki API telemetryd serves and the OTLP endpoint it accepts, so the same pair covers moving to a new instance, pulling production onto a laptop, and leaving telemetryd altogether.

Moving between two instances

telemetryd export --url http://old:4319 --to http://new:4319 --signal logs
telemetryd export --url http://old:4319 --to http://new:4319 --signal traces
telemetryd export --url http://old:4319 --to http://new:4319 --signal metrics

No file in between, and the highest fidelity available: records are read through telemetryd's own export endpoint rather than re-derived from a query language, so all three signals come across as they were stored.

This is the direction to use when both ends are telemetryd. import --from is for when the source is not.

Pulling an incident onto your machine

The case this gets used for weekly. Point --from at production and write into a local instance you can throw away afterwards:

telemetryd import --from https://logs.internal --from-token "$PROD_TOKEN" \
  --since 6h --query '{app="checkout"}'

Query it offline as often as you like, without putting load on production and without production being reachable.

Import into a fresh data directory. telemetryd is append-only with no deduplication, so importing an overlapping range twice stores the records twice. For the debugging case a throwaway directory is what you wanted anyway — and it is also how you tell two sources apart, which is why there is no --label for stamping provenance. Where you do want them in one store, deployment_environment is already a stream label; if your telemetry does not carry one, that is worth fixing at the source rather than at the import.

Retention will refuse before it deletes

Ingest applies no age limit, so old records go in perfectly well — and then the reaper removes anything past retention.logs, possibly while the import is still running.

import therefore checks the destination's retention against the range and refuses rather than producing an import that appears to succeed and silently leaves nothing behind. Raise retention.logs there, or say --allow-expiring if that really is what you meant.

The format

One OTLP request per line. NDJSON streams, survives being cut in half, and is what every other backend already ingests — so dump.ndjson is not a telemetryd file, it is a file anything speaking OTLP can read.

Which is also why import --file is one line at a time rather than one parse: a 4 GB dump should not need 4 GB of memory.

Progress

stdout is data, stderr is progress. That is what lets a live meter run while the output goes somewhere else:

telemetryd export --since 7d | gzip > week.ndjson.gz
--progress
auto (default) a live meter on a terminal, periodic lines when it is not one
tty force the meter
plain one line every few seconds — a log file, not a redraw
json NDJSON events on stderr, for a program that is watching
none silence

auto is what makes this behave under systemd, in CI and in a pipe without anyone passing a flag.

The json form ends with a done or failed event, and both carry high_water_nanos — the oldest record reached. A transfer that dies at 60% tells you exactly where to start again.

All three signals

telemetryd export --signal traces  --since 24h > traces.ndjson
telemetryd export --signal metrics --since 24h > metrics.ndjson

There are two paths underneath, and which one runs depends on what you asked for.

Against telemetryd, export reads records straight from the store and encodes them — no query language in the middle, so what comes out is what was stored, and it is one request per window rather than one per trace.

Against a foreign backend, or when you pass a --query to take a subset, it goes through the read APIs instead:

telemetryd import --from https://tempo.internal --signal traces --since 6h

Traces cost N+1 requests per window — search enumerates the window, then each id is fetched for its spans — so this is much slower than the native path. Correct, though, which is what matters when the source is not a telemetryd.

Metrics come through remote read, not a range query:

telemetryd import --from https://prometheus.internal --signal metrics --since 24h

A range query would return points on the step grid rather than the samples that were stored — shrinking the step gives more points from the same samples, not more fidelity. Remote read returns the stored samples with their own timestamps.

It prints a warning every run, and means it: Prometheus documents remote read as outside its stable API, "subject to change even between non-major version releases". If a source upgrade breaks this, have it send OTLP to telemetryd instead — that path is stable and already works.

An export file names its signal in every line, so import --file routes each line to the right endpoint by content. A file holding all three just works, and pointing a traces file at the wrong flag is not a mistake you can make.