Skip to content

Commit

Permalink
ui/tracez: display aggregated data on children spans
Browse files Browse the repository at this point in the history
Spans record aggregated metadata on their children span. This PR returns
that data in the debug UI snapshot response, and renders it on the span
details page.

Release note: None
Informs: None
  • Loading branch information
benbardin committed Mar 10, 2023
1 parent b0e5507 commit fced789
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 72 deletions.
15 changes: 15 additions & 0 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -7212,6 +7212,7 @@ the tracing UI.
| processed_tags | [SpanTag](#cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.server.serverpb.SpanTag) | repeated | | [reserved](#support-status) |
| current | [bool](#cockroach.server.serverpb.GetTracingSnapshotResponse-bool) | | current is set if the span is still alive (i.e. still present in the active spans registry). | [reserved](#support-status) |
| current_recording_mode | [cockroach.util.tracing.tracingpb.RecordingMode](#cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.util.tracing.tracingpb.RecordingMode) | | current_recording_mode represents the span's current recording mode. This is not set if current == false. | [reserved](#support-status) |
| children_metadata | [NamedOperationMetadata](#cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.server.serverpb.NamedOperationMetadata) | repeated | | [reserved](#support-status) |



Expand Down Expand Up @@ -7255,6 +7256,20 @@ of the tracing UI.



<a name="cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.server.serverpb.NamedOperationMetadata"></a>
#### NamedOperationMetadata



| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| name | [string](#cockroach.server.serverpb.GetTracingSnapshotResponse-string) | | | [reserved](#support-status) |
| metadata | [cockroach.util.tracing.tracingpb.OperationMetadata](#cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.util.tracing.tracingpb.OperationMetadata) | | | [reserved](#support-status) |





<a name="cockroach.server.serverpb.GetTracingSnapshotResponse-cockroach.server.serverpb.TracingSnapshot.StacksEntry"></a>
#### TracingSnapshot.StacksEntry

Expand Down
15 changes: 13 additions & 2 deletions pkg/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4175,8 +4175,18 @@ func (s *adminServer) GetTracingSnapshot(

for i, s := range spansList.Spans {
tags := make([]*serverpb.SpanTag, len(s.Tags))
for j, t := range s.Tags {
tags[j] = getSpanTag(t)
for j, tag := range s.Tags {
tags[j] = getSpanTag(tag)
}
childrenMetadata := make([]*serverpb.NamedOperationMetadata, len(s.ChildrenMetadata))

j := 0
for name, cm := range s.ChildrenMetadata {
childrenMetadata[j] = &serverpb.NamedOperationMetadata{
Name: name,
Metadata: cm,
}
j++
}

spans[i] = &serverpb.TracingSpan{
Expand All @@ -4189,6 +4199,7 @@ func (s *adminServer) GetTracingSnapshot(
ProcessedTags: tags,
Current: s.Current,
CurrentRecordingMode: s.CurrentRecordingMode.ToProto(),
ChildrenMetadata: childrenMetadata,
}
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/server/serverpb/admin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import "roachpb/metadata.proto";
import "roachpb/data.proto";
import "ts/catalog/chart_catalog.proto";
import "util/metric/metric.proto";
import "util/tracing/tracingpb/recorded_span.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
Expand Down Expand Up @@ -1396,6 +1397,11 @@ message TracingSnapshot {
map<string, string> stacks = 4;
}

message NamedOperationMetadata {
string name = 1;
util.tracing.tracingpb.OperationMetadata metadata = 2 [(gogoproto.nullable) = false];
}

// TracingSpan represents a span, in a form slightly processed for the use of
// the tracing UI.
message TracingSpan {
Expand All @@ -1412,6 +1418,7 @@ message TracingSpan {
// current_recording_mode represents the span's current recording mode. This is
// not set if current == false.
util.tracing.tracingpb.RecordingMode current_recording_mode = 9;
repeated NamedOperationMetadata children_metadata = 10;
}

// SpanTag represents a tag on a tracing span, in a form processed for the use
Expand Down
2 changes: 2 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/api/tracezApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export type GetTracingSnapshotRequest =
export type GetTracingSnapshotResponse =
cockroach.server.serverpb.GetTracingSnapshotResponse;

export type NamedOperationMetadata =
cockroach.server.serverpb.INamedOperationMetadata;
export type Span = cockroach.server.serverpb.ITracingSpan;
export type Snapshot = cockroach.server.serverpb.ITracingSnapshot;

Expand Down
21 changes: 21 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/tracez/snapshot.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@
justify-content: right;
}

.metadata-icon-cell {
padding: 8px 4px 4px 0px;
margin: 0px;
display: flex;
justify-content: right;
}

.metadata-name-cell {
padding: 4px 0px 4px 0px; // For vertical, override default of 16px.
}

.table-cell-time {
padding: 4px 16px; // For vertical, override default of 16px.
display: flex;
Expand Down Expand Up @@ -106,6 +117,16 @@
padding-top: 1px;
}

.icon-hollow-green {
fill: none;
height: 10px;
width: 10px;
padding-top: 2px;
stroke: green;
stroke-width: 4px;
clip-path: circle(4px at 5px 6px);
}

.icon-gray {
fill: lightgray;
height: 10px;
Expand Down
161 changes: 98 additions & 63 deletions pkg/ui/workspaces/cluster-ui/src/tracez/snapshot/spanComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import "antd/lib/switch/style";
import Long from "long";
import { Button } from "src/button";
import { useHistory } from "react-router-dom";
import { SpanMetadataTable } from "./spanMetadataTable";

const cx = classNames.bind(styles);

Expand Down Expand Up @@ -60,6 +61,10 @@ const SpanStatus: React.FC<{
.finally(() => {
setRecordingInFlight(false);
});
// Objects in hook dependencies use referential equality, not value
// equality. To force a hook refresh on span, explicitly mark spanID. But,
// having done that, there's no need to include the span, as it's purely a
// function of that input.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
nodeID,
Expand Down Expand Up @@ -108,8 +113,6 @@ const SpanStatus: React.FC<{

export const SpanComponent: React.FC<{
snapshot: GetTracingSnapshotResponse;
sort: SortSetting;
changeSortSetting: (_: SortSetting) => void;
spanDetailsURL: (_: Long) => string;
span: Span;

Expand All @@ -126,8 +129,6 @@ export const SpanComponent: React.FC<{
}> = props => {
const {
snapshot,
sort,
changeSortSetting,
span,
spanDetailsURL,
snapshotError,
Expand All @@ -139,11 +140,16 @@ export const SpanComponent: React.FC<{
const snapshotID = snapshot?.snapshot.snapshot_id;
const spans = snapshot?.snapshot.spans;
const spanID = span?.span_id;

const childFilteredSnapshot = useMemo(() => {
return {
...snapshot?.snapshot,
spans: spans?.filter(s => s.parent_span_id.equals(spanID)),
};
// Objects in hook dependencies use referential equality, not value
// equality. To force a hook refresh, explicitly mark nodeID, snapshotID,
// and spanID. But, having done that, there's no need to include the
// snapshot and spans, as they're purely a function of those inputs.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodeID, snapshotID, spanID]);

Expand All @@ -152,21 +158,37 @@ export const SpanComponent: React.FC<{
...snapshot?.snapshot,
spans: spans?.filter(s => s.span_id.equals(span.parent_span_id)),
};
// Objects in hook dependencies use referential equality, not value
// equality. To force a hook refresh, explicitly mark nodeID, snapshotID,
// and spanID. But, having done that, there's no need to include the
// snapshot and spans, as they're purely a function of those inputs.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodeID, snapshotID, spanID]);

const snapshotTime = useMemo(() => {
return TimestampToMoment(snapshot?.snapshot.captured_at);
// Objects in hook dependencies use referential equality, not value
// equality. To force a hook refresh, explicitly mark nodeID and
// snapshotID. But, having done that, there's no need to include the
// snapshot, as it's purely a function of those inputs.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodeID, snapshotID]);

const startTime = useMemo(
() => {
return TimestampToMoment(span?.start);
},
// Objects in hook dependencies use referential equality, not value
// equality. To force a hook refresh, explicitly mark nodeID, snapshotID
// and spanID. But, having done that, there's no need to include the
// span, as it's purely a function of those inputs.
// eslint-disable-next-line react-hooks/exhaustive-deps
[nodeID, snapshotID, spanID],
);
const [childSpanSortSetting, setChildSpanSortSetting] =
useState<SortSetting>();

const childrenMetadata = span?.children_metadata;

const history = useHistory();
return (
Expand All @@ -187,67 +209,80 @@ export const SpanComponent: React.FC<{
View Raw Trace
</Button>
</div>
<section className={cx("span-section", "span-snapshot-columns")}>
<div className={cx("span-snapshot-column")}>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>Snapshot Time (UTC)</div>
{snapshotTime.format("YYYY-MM-DD HH:mm:ss.SSS")}
</div>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>Start Time (UTC)</div>
{startTime.format("YYYY-MM-DD HH:mm:ss.SSS")}
</div>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>Duration</div>
{formatDurationHours(moment.duration(snapshotTime.diff(startTime)))}
</div>
<SpanStatus
span={span}
setTraceRecordingType={setTraceRecordingType}
nodeID={nodeID}
/>
</div>
<div>
<div className={cx("span-snapshot-key", "span-snapshot-key-value")}>
Tags
</div>
{span && <TagCell span={span} defaultExpanded={true} />}
</div>
</section>
{parentFilteredSnapshot.spans?.length > 0 && (
<section className={cx("span-section")}>
<h3 className={commonStyles("base-heading")}>Parent Span</h3>
<Loading
loading={snapshotLoading}
page={"snapshots"}
error={snapshotError}
render={() => (
<SpanTable
snapshot={parentFilteredSnapshot}
spanDetailsURL={spanDetailsURL}
/>

<Loading
loading={snapshotLoading}
page={"snapshots"}
error={snapshotError}
render={() => (
<div>
<section className={cx("span-section", "span-snapshot-columns")}>
<div className={cx("span-snapshot-column")}>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>
Snapshot Time (UTC)
</div>
{snapshotTime.format("YYYY-MM-DD HH:mm:ss.SSS")}
</div>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>
Start Time (UTC)
</div>
{startTime.format("YYYY-MM-DD HH:mm:ss.SSS")}
</div>
<div className={cx("span-snapshot-key-value")}>
<div className={cx("span-snapshot-key")}>Duration</div>
{formatDurationHours(
moment.duration(snapshotTime.diff(startTime)),
)}
</div>
<SpanStatus
span={span}
setTraceRecordingType={setTraceRecordingType}
nodeID={nodeID}
/>
</div>
<div>
<div
className={cx("span-snapshot-key", "span-snapshot-key-value")}
>
Tags
</div>
{span && <TagCell span={span} defaultExpanded={true} />}
</div>
</section>
{parentFilteredSnapshot.spans?.length > 0 && (
<section className={cx("span-section")}>
<h3 className={commonStyles("base-heading")}>Parent Span</h3>
<SpanTable
snapshot={parentFilteredSnapshot}
spanDetailsURL={spanDetailsURL}
/>
</section>
)}
/>
</section>
)}
{childFilteredSnapshot.spans?.length > 0 && (
<section className={cx("span-section")}>
<h3 className={commonStyles("base-heading")}>Child Spans</h3>
<Loading
loading={snapshotLoading}
page={"snapshots"}
error={snapshotError}
render={() => (
<SpanTable
snapshot={childFilteredSnapshot}
setSort={changeSortSetting}
sort={sort}
spanDetailsURL={spanDetailsURL}
/>
{childrenMetadata?.length > 0 && (
<section className={cx("span-section")}>
<h3 className={commonStyles("base-heading")}>
Aggregated Child Span Metadata
</h3>

<SpanMetadataTable childrenMetadata={childrenMetadata} />
</section>
)}
/>
</section>
)}
{childFilteredSnapshot.spans?.length > 0 && (
<section className={cx("span-section")}>
<h3 className={commonStyles("base-heading")}>Child Spans</h3>
<SpanTable
snapshot={childFilteredSnapshot}
setSort={setChildSpanSortSetting}
sort={childSpanSortSetting}
spanDetailsURL={spanDetailsURL}
/>
</section>
)}
</div>
)}
/>
<div className={cx("bottom-padding")} />
</div>
);
Expand Down
Loading

0 comments on commit fced789

Please sign in to comment.