Skip to content

feat: add sentry span command group for AI-agent trace debugging #391

@betegon

Description

@betegon

Context

AI agents (Junior, Warden) need to self-diagnose by reviewing Sentry traces. Today sentry trace view --json dumps the full nested span tree (up to 10,000 spans) — unusable for an LLM context window. There's no way to list/filter/drill-into individual spans. We need span as a first-class entity with list and view subcommands.

Agent Self-Diagnosis Workflow

# 1. Overview: what happened in this trace?
sentry trace view <trace-id> --json

# 2. List + filter spans
sentry span list <trace-id> --json -q "op:db.* duration:>100ms" --sort duration

# 3. Drill into specific spans (multiple IDs, like log view)
sentry span view <span-id> <span-id> --trace <trace-id> --json

# 4. Correlated logs (already exists)
sentry trace logs <trace-id> --json

sentry span list

Follows existing target resolution pattern used across the CLI.

Usage:

sentry span list <trace-id>                  # auto-detect org/project
sentry span list <org>/<project> <trace-id>  # explicit org and project
sentry span list <project> <trace-id>        # find project across all orgs

Flags:

Flag Type Default Description
--query / -q string Filter spans locally (Sentry-style syntax: op:db.* duration:>100ms)
--limit / -n number 50 Max spans (1–10000)
--sort / -s time | duration time Chronological or slowest-first
--json boolean Injected by buildCommand
--fields string[] Injected by buildCommand
--fresh / -f boolean false Bypass cache

Query syntax (client-side filtering on fetched trace data):

  • op:db.query — exact op match
  • op:db.* or op:db — prefix match (db matches db.query, db.redis)
  • duration:>100ms — min duration filter
  • duration:<1s — max duration filter
  • project:backend — filter by project slug
  • Multiple terms AND together: op:db duration:>100ms

JSON output:

{
  "data": [
    { "span_id": "abc123", "parent_span_id": "...", "op": "db.query",
      "description": "SELECT * FROM users", "duration_ms": 1823,
      "start_timestamp": 1710000000.123, "project_slug": "backend",
      "transaction": "GET /api/users", "depth": 2, "child_count": 0 }
  ],
  "hasMore": false,
  "totalSpans": 8432,
  "matchedSpans": 42
}

Human output: Table with columns Span ID | Op | Description | Duration | Depth.


sentry span view <span-id> [<span-id>...] --trace <trace-id>

Follows log view multi-ID pattern. Span ID is the primary entity (positional). --trace is a required flag (existing convention from log list --trace).

Usage:

sentry span view <span-id> --trace <trace-id>                              # single, auto-detect
sentry span view <span-id> <span-id> --trace <trace-id>                    # multiple
sentry span view <org>/<project> <span-id> --trace <trace-id>              # explicit target
sentry span view <project> <span-id> --trace <trace-id>                    # project search

Flags:

Flag Type Default Description
--trace string (required) Trace ID (validated via validateTraceId)
--spans number 3 Children depth (reuse spansFlag)
--web / -w boolean false Open trace in browser
--json boolean Injected
--fields string[] Injected
--fresh / -f boolean false Bypass cache

Key feature: ancestor chain. When viewing a span, show the full path from root → parent → this span, so the agent sees the full context of where this span sits in the trace.

Human output (single span):

## Span `abc123def456ab01`

Op:          db.query
Description: SELECT * FROM users WHERE id = ?
Duration:    1.82s
Project:     backend
Transaction: GET /api/users
Started:     2024-01-15 10:30:45

### Trace Context

Trace ID:    ed29abc871c4475b...

### Ancestors (root → this span)

http.server — GET /api/users  (2.4s)
  └─ middleware — authMiddleware  (45ms)
     └─ handler — getUserHandler  (1.9s)
        └─ ▶ db.query — SELECT * FROM users  (1.82s)

### Children (3)

├─ db.query — SELECT * FROM user_settings  (245ms)
├─ cache.get — user:preferences:123  (12ms)
└─ http.client — POST analytics.example.com/event  (89ms)

For multiple spans: separate with --- dividers (same as log view).

JSON output (multiple spans):

[
  {
    "span_id": "abc123",
    "op": "db.query",
    "description": "SELECT * FROM users WHERE id = ?",
    "duration_ms": 1823,
    "start_timestamp": 1710000000.123,
    "project_slug": "backend",
    "transaction": "GET /api/users",
    "depth": 2,
    "ancestors": [
      { "span_id": "root1", "op": "http.server", "description": "GET /api/users" },
      { "span_id": "mid1", "op": "middleware", "description": "authMiddleware" }
    ],
    "children": [
      { "span_id": "child1", "op": "db.query", "description": "SELECT...", "duration_ms": 245 }
    ]
  }
]

Design Decisions

  • Standard target resolution[<org>/<project>] or <project> positional, same as trace view, event view, etc.
  • --trace as flag on span view — matches existing log list --trace convention; keeps span ID as the primary entity
  • Query-based filtering over dash-dash flags-q "op:db duration:>100ms" instead of --op db --min-duration 100; minimizes flag proliferation
  • Ancestor chain in span view — agents need to understand WHERE in the trace a span lives, not just the span in isolation
  • Multiple span IDs — follows log view pattern; agents often want to compare a few spans
  • Return-based output — uses the new OutputConfig / CommandOutput pattern from recent refactor

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions