Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 19 additions & 61 deletions AGENTS.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions docs/src/fragments/commands/span.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ sentry span list abc123def456abc123def456abc12345
sentry span list -c next
```

### Filter by project in a trace

```bash
# Show only spans from one project within a trace
sentry span list my-org/cli-server/abc123def456abc123def456abc12345

# Or use --query to filter by project
sentry span list abc123def456abc123def456abc12345 -q "project:cli-server"

# Multiple projects at once
sentry span list abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
```

### View spans

```bash
Expand Down
16 changes: 16 additions & 0 deletions docs/src/fragments/commands/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ sentry trace view abc123def456abc123def456abc12345 -w
sentry trace view PROJ-123
```

### Cross-project traces

```bash
# Filter trace view to one project's spans
sentry trace view my-org/cli-server/abc123def456abc123def456abc12345

# Full trace across all projects (default)
sentry trace view my-org/abc123def456abc123def456abc12345

# Filter trace logs by project
sentry trace logs my-org/cli-server/abc123def456abc123def456abc12345

# Multiple projects via --query
sentry trace logs abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
```

### View trace logs

```bash
Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ View distributed traces

- `sentry trace list <org/project>` — List recent traces in a project
- `sentry trace view <org/project/trace-id...>` — View details of a specific trace
- `sentry trace logs <org/trace-id...>` — View logs associated with a trace
- `sentry trace logs <org/project/trace-id...>` — View logs associated with a trace

→ Full flags and examples: `references/trace.md`

Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ List logs from a project

**Flags:**
- `-n, --limit <value> - Number of log entries (1-1000) - (default: "100")`
- `-q, --query <value> - Filter query (Sentry search syntax)`
- `-q, --query <value> - Filter query (e.g., "level:error", "project:backend", "project:[a,b]")`
- `-f, --follow <value> - Stream logs (optionally specify poll interval in seconds)`
- `-t, --period <value> - Time range: "7d", "2026-03-01..2026-04-01", ">=2026-03-01"`
- `-s, --sort <value> - Sort order: "newest" (default) or "oldest" - (default: "newest")`
Expand Down
11 changes: 10 additions & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/span.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ List spans in a project or trace

**Flags:**
- `-n, --limit <value> - Number of spans (<=1000) - (default: "25")`
- `-q, --query <value> - Filter spans (e.g., "op:db", "duration:>100ms", "project:backend")`
- `-q, --query <value> - Filter spans (e.g., "op:db", "project:backend", "project:[cli,api]")`
- `-s, --sort <value> - Sort order: date, duration - (default: "date")`
- `-t, --period <value> - Time range: "7d", "2026-03-01..2026-04-01", ">=2026-03-01" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
Expand Down Expand Up @@ -54,6 +54,15 @@ sentry span list abc123def456abc123def456abc12345

# Paginate through results
sentry span list -c next

# Show only spans from one project within a trace
sentry span list my-org/cli-server/abc123def456abc123def456abc12345

# Or use --query to filter by project
sentry span list abc123def456abc123def456abc12345 -q "project:cli-server"

# Multiple projects at once
sentry span list abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
```

### `sentry span view <trace-id/span-id...>`
Expand Down
16 changes: 14 additions & 2 deletions plugins/sentry-cli/skills/sentry-cli/references/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,29 @@ sentry trace view abc123def456abc123def456abc12345 -w

# Auto-recover from an issue short ID
sentry trace view PROJ-123

# Filter trace view to one project's spans
sentry trace view my-org/cli-server/abc123def456abc123def456abc12345

# Full trace across all projects (default)
sentry trace view my-org/abc123def456abc123def456abc12345

# Filter trace logs by project
sentry trace logs my-org/cli-server/abc123def456abc123def456abc12345

# Multiple projects via --query
sentry trace logs abc123def456abc123def456abc12345 -q "project:[cli-server,api]"
```

### `sentry trace logs <org/trace-id...>`
### `sentry trace logs <org/project/trace-id...>`

View logs associated with a trace

**Flags:**
- `-w, --web - Open trace in browser`
- `-t, --period <value> - Time range: "7d", "2026-03-01..2026-04-01", ">=2026-03-01" - (default: "14d")`
- `-n, --limit <value> - Number of log entries (<=1000) - (default: "100")`
- `-q, --query <value> - Additional filter query (Sentry search syntax)`
- `-q, --query <value> - Filter query (e.g., "level:error", "project:backend", "project:[a,b]")`
- `-s, --sort <value> - Sort order: "newest" (default) or "oldest" - (default: "newest")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`

Expand Down
56 changes: 47 additions & 9 deletions src/commands/log/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {
listLogs,
listTraceLogs,
} from "../../lib/api-client.js";
import { parseLogSort, validateLimit } from "../../lib/arg-parsing.js";
import {
buildProjectQuery,
parseLogSort,
validateLimit,
} from "../../lib/arg-parsing.js";
import {
AuthError,
stringifyUnknown,
Expand Down Expand Up @@ -94,7 +98,7 @@ const DEFAULT_POLL_INTERVAL = 2;
const COMMAND_NAME = "log list";

/** Usage hint for trace mode error messages */
const TRACE_USAGE_HINT = "sentry log list [<org>/]<trace-id>";
const TRACE_USAGE_HINT = "sentry log list [<org>/[<project>/]]<trace-id>";

/** Default time period for trace-logs queries */
const DEFAULT_TRACE_PERIOD = "14d";
Expand Down Expand Up @@ -440,21 +444,33 @@ async function* yieldTraceFollowItems<T extends LogLike>(
}
}

/** Options for {@link executeTraceSingleFetch}. */
type TraceFetchOptions = {
flags: ListFlags;
timeRange: TimeRange;
/** Project slug for API-level filtering (from org/project/trace-id syntax) */
projectFilter?: string;
};

/**
* Execute a single fetch of trace-filtered logs (non-streaming, trace mode).
* Uses the dedicated trace-logs endpoint which is org-scoped.
*
* When `projectFilter` is provided, `project:{slug}` is prepended to the query
* for API-level filtering, and the hint includes a copy-pasteable unfiltered command.
*
* Returns the fetched logs, trace ID, and a human-readable hint.
* The caller (via the output config) handles rendering to stdout.
*/
async function executeTraceSingleFetch(
org: string,
traceId: string,
flags: ListFlags,
timeRange: TimeRange
options: TraceFetchOptions
): Promise<FetchResult> {
const { flags, timeRange, projectFilter } = options;
const query = buildProjectQuery(flags.query, projectFilter);
const logs = await listTraceLogs(org, traceId, {
query: flags.query,
query,
limit: flags.limit,
...timeRangeToApiParams(timeRange),
sort: flags.sort,
Expand All @@ -478,9 +494,17 @@ async function executeTraceSingleFetch(
const countText = `Showing ${logs.length} log${logs.length === 1 ? "" : "s"} for trace ${traceId}.`;
const tip = hasMore ? " Use --limit to show more." : "";

// Build hint with real values for easy copy-paste
let hint = `${countText}${tip}`;
if (projectFilter) {
hint += `\nFiltered to project '${projectFilter}'. Full trace logs: sentry log list ${org}/${traceId}`;
} else {
hint += `\nFilter by project: sentry log list ${org}/<project>/${traceId}`;
}

return {
result: { logs, traceId, hasMore },
hint: `${countText}${tip}`,
hint,
};
}

Expand Down Expand Up @@ -648,7 +672,8 @@ export const listCommand = buildListCommand(
query: {
kind: "parsed",
parse: String,
brief: "Filter query (Sentry search syntax)",
brief:
'Filter query (e.g., "level:error", "project:backend", "project:[a,b]")',
optional: true,
},
follow: {
Expand Down Expand Up @@ -717,6 +742,14 @@ export const listCommand = buildListCommand(
cwd,
TRACE_USAGE_HINT
);

// Capture explicit project for API-level filtering
const projectFilter =
parsed.parsed.type === "explicit" ? parsed.parsed.project : undefined;

// Prepend project filter to the query when user explicitly specified a project
const traceQuery = buildProjectQuery(flags.query, projectFilter);

if (flags.follow) {
// Banner (suppressed in JSON mode)
writeFollowBanner(
Expand All @@ -735,7 +768,7 @@ export const listCommand = buildListCommand(
?.abortSignal,
fetch: (statsPeriod) =>
listTraceLogs(org, traceId, {
query: flags.query,
query: traceQuery,
limit: flags.limit,
statsPeriod,
}),
Expand Down Expand Up @@ -772,7 +805,12 @@ export const listCommand = buildListCommand(
message: `Fetching logs (up to ${flags.limit})...`,
json: flags.json,
},
() => executeTraceSingleFetch(org, traceId, flags, timeRange)
() =>
executeTraceSingleFetch(org, traceId, {
flags,
timeRange,
projectFilter,
})
);
yield new CommandOutput(result);
return { hint };
Expand Down
2 changes: 1 addition & 1 deletion src/commands/span/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ export const listCommand = buildListCommand("span", {
kind: "parsed",
parse: String,
brief:
'Filter spans (e.g., "op:db", "duration:>100ms", "project:backend")',
'Filter spans (e.g., "op:db", "project:backend", "project:[cli,api]")',
optional: true,
},
sort: {
Expand Down
48 changes: 36 additions & 12 deletions src/commands/trace/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

import type { SentryContext } from "../../context.js";
import { type LogSortDirection, listTraceLogs } from "../../lib/api-client.js";
import { parseLogSort, validateLimit } from "../../lib/arg-parsing.js";
import {
buildProjectQuery,
parseLogSort,
validateLimit,
} from "../../lib/arg-parsing.js";
import { openInBrowser } from "../../lib/browser.js";
import { buildCommand } from "../../lib/command.js";
import { filterFields } from "../../lib/formatters/json.js";
Expand Down Expand Up @@ -79,7 +83,7 @@ function formatTraceLogsHuman(data: TraceLogsData): string {
const DEFAULT_PERIOD = "14d";

/** Usage hint shown in error messages */
const USAGE_HINT = "sentry trace logs [<org>/]<trace-id>";
const USAGE_HINT = "sentry trace logs [<org>/[<project>/]]<trace-id>";

/**
* Parse --limit flag, delegating range validation to shared utility.
Expand All @@ -93,15 +97,17 @@ export const logsCommand = buildCommand({
brief: "View logs associated with a trace",
fullDescription:
"View logs associated with a specific distributed trace.\n\n" +
"Uses the dedicated trace-logs endpoint, which is org-scoped and\n" +
"automatically queries all projects — no project flag needed.\n\n" +
"Target specification:\n" +
" sentry trace logs <trace-id> # auto-detect org\n" +
" sentry trace logs <org>/<trace-id> # explicit org\n\n" +
" sentry trace logs <trace-id> # auto-detect org\n" +
" sentry trace logs <org>/<trace-id> # explicit org\n" +
" sentry trace logs <org>/<project>/<trace-id> # filter to project\n\n" +
"When a project is specified, only logs from that project are shown.\n" +
"Use --query 'project:[a,b]' to filter to multiple projects.\n\n" +
"The trace ID is the 32-character hexadecimal identifier.\n\n" +
"Examples:\n" +
" sentry trace logs abc123def456abc123def456abc123de\n" +
" sentry trace logs myorg/abc123def456abc123def456abc123de\n" +
" sentry trace logs myorg/backend/abc123def456abc123def456abc123de\n" +
" sentry trace logs --period 7d abc123def456abc123def456abc123de\n" +
" sentry trace logs --json abc123def456abc123def456abc123de",
},
Expand All @@ -119,8 +125,9 @@ export const logsCommand = buildCommand({
positional: {
kind: "array",
parameter: {
placeholder: "org/trace-id",
brief: "[<org>/]<trace-id> - Optional org and required trace ID",
placeholder: "org/project/trace-id",
brief:
"[<org>/[<project>/]]<trace-id> - Optional org/project and required trace ID",
parse: String,
},
},
Expand All @@ -145,7 +152,8 @@ export const logsCommand = buildCommand({
query: {
kind: "parsed",
parse: String,
brief: "Additional filter query (Sentry search syntax)",
brief:
'Filter query (e.g., "level:error", "project:backend", "project:[a,b]")',
optional: true,
},
sort: {
Expand All @@ -170,15 +178,21 @@ export const logsCommand = buildCommand({
const { cwd } = this;
const timeRange = parsePeriod(flags.period);

// Parse and resolve org/trace-id
// Parse and resolve org/trace-id (project captured for filtering)
const parsed = parseTraceTarget(args, USAGE_HINT);
warnIfNormalized(parsed, "trace.logs");
const { traceId, org } = await resolveTraceOrg(parsed, cwd, USAGE_HINT);
const projectFilter =
parsed.type === "explicit" ? parsed.project : undefined;

if (flags.web) {
await openInBrowser(buildTraceUrl(org, traceId), "trace");
return;
}

// Prepend project filter to the query when user explicitly specified a project
const query = buildProjectQuery(flags.query, projectFilter);

const logs = await withProgress(
{
message: `Fetching trace logs (up to ${flags.limit})...`,
Expand All @@ -188,7 +202,7 @@ export const logsCommand = buildCommand({
listTraceLogs(org, traceId, {
...timeRangeToApiParams(timeRange),
limit: flags.limit,
query: flags.query,
query,
sort: flags.sort,
})
);
Expand All @@ -199,11 +213,21 @@ export const logsCommand = buildCommand({
`No logs found for trace ${traceId} in the last ${flags.period}.\n\n` +
`Try a longer period: sentry trace logs --period 30d ${traceId}`;

return yield new CommandOutput({
yield new CommandOutput({
logs,
traceId,
hasMore,
emptyMessage,
});

// Build hint with real values for easy copy-paste
if (projectFilter) {
return {
hint: `Filtered to project '${projectFilter}'. Full trace logs: sentry trace logs ${org}/${traceId}`,
};
}
return {
hint: `Filter by project: sentry trace logs ${org}/<project>/${traceId}`,
};
},
});
Loading
Loading