Skip to content

Add trace-level routing filters for project logs - #26

Open
Spencer Seale (spencerseale) wants to merge 2 commits into
mainfrom
feat/logs-root-span-name-filter
Open

Add trace-level routing filters for project logs#26
Spencer Seale (spencerseale) wants to merge 2 commits into
mainfrom
feat/logs-root-span-name-filter

Conversation

@spencerseale

Copy link
Copy Markdown

Summary

  • Adds --logs-include-root-span-name / --logs-exclude-root-span-name. They're exact complements, so a paired run splits one source project across two destinations with every span landing in exactly one.
  • Child spans don't carry their root's name, so a prepass collects matching root_span_ids and the stream routes by those — whole traces stay intact.

Reviewer notes

  • --dry-run now runs the prepass (full scan of source logs) and reports match counts. Say the word if that should be opt-in.
  • Prepass ignores --created-after/--created-before on purpose: traces straddle boundaries, partial id sets misroute.
  • Matched ids held in memory, ~150 bytes/trace. Fine to low millions.
  • Routing is client-side, so a paired split is two full passes.
  • Filter is persisted in the checkpoint; resuming with a changed value is rejected.

Validation

  • Tests added — tests/unit/test_logs_root_span_filter.py (11)
  • uv run pytest — 334 passed, 3 skipped
  • uv run ruff check — no new findings

🤖 Generated with Claude Code

Adds --logs-include-root-span-name and --logs-exclude-root-span-name. The two
are exact complements, so a paired run splits one source project across two
destinations with every span landing in exactly one of them.

Child spans don't carry their root's name, so a one-time BTQL prepass collects
the root_span_id of every matching span; the streaming loop then routes by
root_span_id, keeping whole traces intact.

Notes:
- The prepass ignores --created-after/--created-before on purpose; traces can
  straddle a boundary and a partial id set would misroute spans.
- Matched ids are held in memory (~150 bytes/trace).
- Routing is client-side, so each run pages through all spans.
- --dry-run now runs the prepass too, making it a full scan of source logs.
- The filter is persisted in the checkpoint; resuming with a changed or
  dropped value is rejected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The prepass counted a match as top-level when span_id == root_span_id. Under
OTel-style ingestion root_span_id holds the 16-byte trace id while span_id is
an 8-byte span id, so they never match and every span looked nested.

Effect was cosmetic but misleading: the dry-run table reported 0 top-level
matches on real OTel data and the run logged a spurious warning about an
over-broad split. Routing itself was always correct since it groups by
root_span_id.

Now keyed off an empty span_parents, which holds for both ingestion shapes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant