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
1 change: 1 addition & 0 deletions plugins/sentry-cli/skills/sentry-cli/references/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ View details of a specific trace

**Flags:**
- `-w, --web - Open in browser`
- `--full - Fetch full span attributes (auto-enabled with --json)`
- `--spans <value> - Span tree depth limit (number, "all" for unlimited, "no" to disable) - (default: "3")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`

Expand Down
70 changes: 10 additions & 60 deletions src/commands/span/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
* View detailed information about one or more spans within a trace.
*/

import pLimit from "p-limit";

import type { SentryContext } from "../../context.js";
import type { TraceItemDetail } from "../../lib/api-client.js";
import {
attributesToDict,
fetchMultiSpanDetails,
getDetailedTrace,
getSpanDetails,
REDUNDANT_DETAIL_ATTRS,
type TraceItemDetail,
} from "../../lib/api-client.js";
import { spansFlag } from "../../lib/arg-parsing.js";
import { buildCommand } from "../../lib/command.js";
Expand Down Expand Up @@ -45,27 +43,6 @@ import {

const log = logger.withTag("span.view");

/** Concurrency limit for parallel span detail fetches */
const SPAN_DETAIL_CONCURRENCY = 5;

/**
* Convert a trace-items attribute array into a key-value dict,
* filtering out attributes already shown in the standard span fields
* and EAP storage internals (tags[], precise timestamps, etc.).
*/
function attributesToDict(
attributes: TraceItemDetail["attributes"]
): Record<string, unknown> {
return Object.fromEntries(
attributes
.filter(
(a) =>
!(REDUNDANT_DETAIL_ATTRS.has(a.name) || a.name.startsWith("tags["))
)
.map((a) => [a.name, a.value])
);
}

type ViewFlags = {
readonly json: boolean;
readonly spans: number;
Expand Down Expand Up @@ -288,39 +265,6 @@ function formatSpanViewHuman(data: SpanViewData): string {
return parts.join("");
}

/**
* Fetch full attribute details for found spans in parallel.
*
* Uses p-limit to cap concurrency and avoid overwhelming the API.
* Failures for individual spans are logged as warnings — the command
* still returns the basic span data from the trace tree.
*/
async function fetchSpanDetails(
results: SpanResult[],
org: string,
project: string,
traceId: string
): Promise<Map<string, TraceItemDetail>> {
const limit = pLimit(SPAN_DETAIL_CONCURRENCY);
const details = new Map<string, TraceItemDetail>();

await limit.map(results, async (r) => {
try {
const detail = await getSpanDetails(
org,
r.span.project_slug || project,
r.spanId,
traceId
);
details.set(r.spanId, detail);
} catch {
log.warn(`Could not fetch details for span ${r.spanId}`);
}
});

return details;
}

/**
* Transform span view data for JSON output.
* Applies `--fields` filtering per element.
Expand Down Expand Up @@ -423,7 +367,13 @@ export const viewCommand = buildCommand({

// Fetch full attribute details for each found span in parallel.
// Uses the trace-items detail endpoint which returns ALL attributes.
const details = await fetchSpanDetails(results, org, project, traceId);
const details = await fetchMultiSpanDetails(
results.map((r) => ({
span_id: r.spanId,
project_slug: r.span.project_slug,
})),
{ org, fallbackProject: project, traceId }
);

yield new CommandOutput({
results,
Expand Down
Loading
Loading