Investigate
Narrow a failure from the collector’s status to one trace, its logs and the audit findings that cite it.
Prerequisites
Section titled “Prerequisites”- A running collector with telemetry in it. The Quickstart records sample data with
blop demo. - Add
--jsonto any command below when an agent reads the output; How it works describes the document and exit codes.
If queries come back empty, run blop setup doctor --json first. It audits the collector, store, agent recipes and Git state without changing anything, and exits 1 with a fix command for each failing check.
Drill down from status to findings
Section titled “Drill down from status to findings”-
Check the collector and what it stores:
Terminal window blop status --json -
List the services seen in the window:
Terminal window blop services --since 1h --json -
Find the failing traces of one service:
Terminal window blop traces --service checkout --status error --since 15m --json -
Print one trace as a span tree with its correlated logs:
Terminal window blop trace <trace-id> --include logs --json -
Read the logs around it:
Terminal window blop logs --trace-id <trace-id> --severity WARN --json -
Audit the window against a baseline:
Terminal window blop audit --service checkout --since 15m --baseline 1h --json
Every query is read-only and bounded by --limit. blop spans and blop span <span-id> search spans directly when a trace is too broad. blop open <trace-id> shows the same trace in the Inspector.
Find the slowest spans
Section titled “Find the slowest spans”No command sorts by duration, so sort the JSON:
blop spans --since 1h --limit 500 --json | jq -r '.data | sort_by(-.span.duration_ms) | .[:5][] | "\(.span.duration_ms) ms \(.trace_id) \(.span.operation_name)"'Root spans last as long as their trace, so they lead. The sort sees only --limit spans: if .page.truncated is true, raise it. To rank one span’s children, add --parent-operation <name>.
Fail on a regression
Section titled “Fail on a regression”blop audit --service checkout --since 15m --fail-on regression --jsonThe audit is deterministic and calls no model. Its findings are error_trace, new_error_signature, latency_regression (from a baseline delta or an explicit --duration-min), log_burst, incomplete_trace and no_telemetry (a service that stopped sending). Each one carries trace, span or log IDs, current and baseline values, and follow-up commands. The baseline defaults to the window before --since, of equal length.
--fail-on error exits 1 on error-severity findings; --fail-on regression also exits 1 on new error signatures and latency regressions. The default, never, always exits 0. Missing evidence is not a measured zero: a counter nothing reported is unknown.
Follow issues and live events
Section titled “Follow issues and live events”The collector groups failures into issues, each open, muted or fixed.
blop issues --json # open issues, current branch firstblop issue <issue-id> --fix-context # Markdown brief to hand to an agentblop why --last # explain the latest failureblop tail -o ndjson # stream new issues, runs and sessionsblop issues takes --state open|muted|fixed|all and --branch <name>. blop tail is a stream, not one JSON document; when no collector runs it waits and reconnects instead of exiting 3.
The Inspector also has an optional chat over the same evidence. It is off until the collector is started with BLOP_AGENT_API_KEY set; queries and audits never need a model.
Filter, aggregate and count
Section titled “Filter, aggregate and count”| Filter or command | Applies to | Example |
|---|---|---|
--service, --operation | traces, spans, logs (service) | --service checkout |
--status ok|error | traces, spans | --status error |
--duration-min <ms> | traces, audit | --duration-min 500 |
--severity <level> (minimum) | logs | --severity WARN |
--contains <text> | logs | --contains timeout |
--trace-id | spans, logs | --trace-id <trace-id> |
--span-id | logs (for one span, blop span <span-id>) | --span-id <span-id> |
attr.KEY=VALUE (exact, indexed) | traces, spans | attr.http.route=/api/pay |
attrContains.KEY=TEXT (substring) | spans, logs | attrContains.exception.message=timeout |
--since <duration> (default 15m) | every query | --since 2h |
--limit default and cap | traces 20, at most 100; spans and logs 100, at most 500 | --limit 50 |
--cursor <token> | traces, logs: continue a JSON page | --cursor <next> |
blop trace-stats <group> <agg> | traces: count, avg_duration, p95_duration, error_rate | blop trace-stats service error_rate |
blop log-stats <group> | log counts by service, severity, scope or attr.KEY | blop log-stats severity |
blop facets <traces|logs> <field> | distinct values | blop facets traces attribute_values --key http.route |
blop usage | token counts per service, agent or model | blop usage --since all --group-by model |
blop usage sums token counters already on spans; input includes cache reads and writes, output includes reasoning, and cost stays unknown.
Control what applications send
Section titled “Control what applications send”Application capture is opt-in per source: without consent the collector keeps metadata but omits log bodies and custom attributes. Credential keys, authorization headers and cookies are redacted before storage; pattern redaction is best effort, not a guarantee.
-
Preview consent for one source; nothing is saved:
Terminal window blop sources configure --id checkout-api --kind application --capture-log-body --capture-attributes --enable -
Apply the same consent:
Terminal window blop sources configure --id checkout-api --kind application --capture-log-body --capture-attributes --enable --apply -
Print the exporter variables for the app:
Terminal window blop sources env --id checkout-api --service checkoutThey set
blop.source.idandblop.source.kind. Declare these yourself if you configure the exporter by hand:service.namealone does not identify a source. -
Check intake:
Terminal window blop sources check --id checkout-apiIt reports reachability, configuration and accepted counts;
applicationOutcomestaysunknown.
GenAI prompt and output content needs two opt-ins: --capture-gen-ai-content here and the producer’s own switch; --capture-attributes does not grant it. blop sources disable --id checkout-api stops intake and keeps history, but does not stop the app’s SDK; set OTEL_SDK_DISABLED=true in the app for that.
Keep one collector per project
Section titled “Keep one collector per project”blop monitor init --port 27812blop up --detachblop monitor init writes .blop/monitor.json with a loopback address and a fixed port for this project. Pick a port other than 27811; run blop up from the same directory to start that collector. Without --url or --port, query commands use the running collector whose working directory is the closest parent of yours, and refuse an ambiguous choice.
Delete local evidence
Section titled “Delete local evidence”blop delete --scope trace --id <trace-id> # preview, removes nothingblop delete --scope trace --id <trace-id> --yes # deleteSelect one exact trace, session or source ID, never a pattern. The preview shows what would be removed and how many bytes; --yes checks the selection again and deletes it all or nothing. One delete removes at most 250,000 rows: a larger scope exits 2 (scope_too_large), so narrow it with --since 1h. Deletion is logical: database pages, WAL files, exports and backups can still hold the data until they are overwritten or expire.
Check it worked
Section titled “Check it worked”blop audit --fail-on regression --json exits 0 when the window has no error findings or regressions, and 1 with error.code audit_failed when it does. After a fix, reproduce the behaviour and run the same audit again.
- Connect a coding agent: capture the agent’s side of a failure.
- Browser checks: verify a user flow end to end.
- Query command reference.