Skip to content

Commit 5650d06

Browse files
committed
improve buckets
1 parent a7b333e commit 5650d06

File tree

5 files changed

+99
-56
lines changed

5 files changed

+99
-56
lines changed

packages/services/api/src/modules/operations/module.graphql.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,11 @@ export default gql`
447447
"""
448448
The time bucket for the data
449449
"""
450-
timeBucket: DateTime!
450+
timeBucketStart: DateTime!
451+
"""
452+
The end of the time bucket for the data
453+
"""
454+
timeBucketEnd: DateTime!
451455
"""
452456
Total amount of ok traces in the bucket.
453457
"""

packages/services/api/src/modules/operations/providers/traces.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ export class Traces {
241241
const d = getBucketUnitAndCountNew(startDate, endDate);
242242

243243
const [countStr, unit] = d.candidate.name.split(' ');
244+
244245
const bucketStepFunctionName = {
245246
MINUTE: 'addMinutes',
246247
HOUR: 'addHours',
@@ -269,7 +270,8 @@ export class Traces {
269270
WHERE "system"."numbers"."number" < ${String(d.buckets)}
270271
)
271272
SELECT
272-
replaceOne(concat(toDateTime64("time_bucket_list"."time_bucket", 9, 'UTC'), 'Z'), ' ', 'T') AS "timeBucket"
273+
replaceOne(concat(toDateTime64("time_bucket_list"."time_bucket", 9, 'UTC'), 'Z'), ' ', 'T') AS "timeBucketStart"
274+
, replaceOne(concat(toDateTime64("t"."time_bucket_end", 9, 'UTC'), 'Z'), ' ', 'T') AS "timeBucketEnd"
273275
, coalesce("t"."ok_count_total", 0) as "okCountTotal"
274276
, coalesce("t"."error_count_total", 0) as "errorCountTotal"
275277
, coalesce("t"."ok_count_filtered", 0) as "okCountFiltered"
@@ -279,7 +281,8 @@ export class Traces {
279281
LEFT JOIN
280282
(
281283
SELECT
282-
toStartOfInterval("timestamp", INTERVAL ${sql.raw(d.candidate.name)}) AS "time_bucket"
284+
toStartOfInterval("timestamp", INTERVAL ${sql.raw(d.candidate.name)}) AS "time_bucket_start"
285+
, toStartOfInterval("timestamp", INTERVAL ${sql.raw(d.candidate.name)}) + INTERVAL ${sql.raw(d.candidate.name)} - INTERVAL 1 SECOND AS "time_bucket_end"
283286
, sumIf(1, "graphql_error_count" = 0) AS "ok_count_total"
284287
, sumIf(1, "graphql_error_count" != 0) AS "error_count_total"
285288
, sumIf(1, "graphql_error_count" = 0 ${filterSQLFragment}) AS "ok_count_filtered"
@@ -291,9 +294,10 @@ export class Traces {
291294
AND "otel_traces_normalized"."timestamp" >= toDateTime(${formatDate(startDate)}, 'UTC')
292295
AND "otel_traces_normalized"."timestamp" <= toDateTime(${formatDate(endDate)}, 'UTC')
293296
GROUP BY
294-
"time_bucket"
297+
"time_bucket_start"
298+
, "time_bucket_end"
295299
) AS "t"
296-
ON "t"."time_bucket" = "time_bucket_list"."time_bucket"
300+
ON "t"."time_bucket_start" = "time_bucket_list"."time_bucket"
297301
`,
298302
queryId: `trace_status_breakdown_for_target_id_`,
299303
timeout: 10_000,
@@ -467,7 +471,8 @@ function buildTraceFilterSQLConditions(filter: TraceFilter, skipPeriod: boolean)
467471
const IntFromString = z.string().transform(value => parseInt(value, 10));
468472

469473
const TraceStatusBreakdownBucket = z.object({
470-
timeBucket: z.string(),
474+
timeBucketStart: z.string(),
475+
timeBucketEnd: z.string(),
471476
okCountTotal: IntFromString,
472477
errorCountTotal: IntFromString,
473478
okCountFiltered: IntFromString,

packages/web/app/src/lib/urql.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ export const urqlClient = createClient({
7878
MetadataAttribute: noKey,
7979
RateLimit: noKey,
8080
DeprecatedSchemaExplorer: noKey,
81+
TraceStatusBreakdownBucket: noKey,
82+
FilterStringOption: noKey,
83+
FilterBooleanOption: noKey,
84+
TracesFilterOptions: noKey,
8185
},
8286
globalIDs: ['SuccessfulSchemaCheck', 'FailedSchemaCheck'],
8387
}),

packages/web/app/src/pages/target-trace.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,6 @@ function SpanNode(props: SpanNodeProps) {
672672
>
673673
<div className="z-20">
674674
<ExceptionTeaser
675-
key={event.id}
676675
type={String(event.attributes['exception.type'] ?? '')}
677676
message={String(event.attributes['exception.message'] ?? '')}
678677
stacktrace={String(event.attributes['exception.stacktrace'] ?? '')}
@@ -700,7 +699,7 @@ function SpanNode(props: SpanNodeProps) {
700699
const isLastChild = i === arr.length - 1;
701700
return (
702701
<SpanNode
703-
key={span.id}
702+
key={uchildSpan.id}
704703
span={childSpan}
705704
highlightedServiceName={props.highlightedServiceName}
706705
leftPanelWidth={props.leftPanelWidth}

packages/web/app/src/pages/target-traces.tsx

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
SidebarInset,
4141
SidebarProvider,
4242
} from '@/components/ui/sidebar';
43+
import { Skeleton } from '@/components/ui/skeleton';
4344
import { Spinner } from '@/components/ui/spinner';
4445
import {
4546
Table,
@@ -92,7 +93,8 @@ const chartConfig = {
9293

9394
const Traffic_TracesStatusBreakdownBucketFragment = graphql(`
9495
fragment Traffic_TracesStatusBreakdownBucketFragment on TraceStatusBreakdownBucket {
95-
timeBucket
96+
timeBucketStart
97+
timeBucketEnd
9698
okCountTotal
9799
errorCountTotal
98100
okCountFiltered
@@ -110,7 +112,8 @@ const TrafficBucketDiagram = memo(function Traffic(props: TrafficProps) {
110112
ok: b.okCountFiltered,
111113
error: b.errorCountFiltered,
112114
remaining: b.okCountTotal + b.errorCountTotal - b.okCountFiltered - b.errorCountFiltered,
113-
timeBucket: b.timeBucket.substring(0, 10),
115+
timeBucketStart: b.timeBucketStart,
116+
timeBucketEnd: b.timeBucketEnd,
114117
}));
115118
const [refAreaLeft, setRefAreaLeft] = useState<string | null>(null);
116119
const [refAreaRight, setRefAreaRight] = useState<string | null>(null);
@@ -168,6 +171,17 @@ const TrafficBucketDiagram = memo(function Traffic(props: TrafficProps) {
168171
setIsSelecting(false);
169172
}, [refAreaLeft, refAreaRight, navigate]);
170173

174+
function formatDate(str: string) {
175+
return new Date(str).toLocaleDateString('en-US', {
176+
year: 'numeric',
177+
month: 'short', // e.g., "Sep"
178+
day: 'numeric',
179+
hour: '2-digit',
180+
minute: '2-digit',
181+
hour12: false, // 24-hour format; set true for AM/PM
182+
});
183+
}
184+
171185
return (
172186
<ChartContainer
173187
config={chartConfig}
@@ -183,29 +197,29 @@ const TrafficBucketDiagram = memo(function Traffic(props: TrafficProps) {
183197
data={data}
184198
>
185199
<XAxis
186-
dataKey="timeBucket"
200+
dataKey="timeBucketStart"
187201
tickLine={false}
188202
axisLine={false}
189203
tickMargin={8}
190204
minTickGap={32}
191-
tickFormatter={value => {
192-
const date = new Date(value);
193-
return date.toLocaleDateString('en-US', {
194-
month: 'short',
195-
day: 'numeric',
196-
});
205+
tickFormatter={date => {
206+
return formatDate(date);
197207
}}
198208
/>
199209
<ChartTooltip
200210
content={
201211
<ChartTooltipContent
202212
className="w-[150px]"
203-
labelFormatter={value => {
204-
return new Date(value).toLocaleDateString('en-US', {
205-
month: 'short',
206-
day: 'numeric',
207-
year: 'numeric',
208-
});
213+
labelFormatter={(_, data) => {
214+
const payload = data[0]?.payload;
215+
216+
if (!payload) {
217+
return null;
218+
}
219+
220+
return (
221+
formatDate(payload.timeBucketStart) + ' - ' + formatDate(payload.timeBucketEnd)
222+
);
209223
}}
210224
/>
211225
}
@@ -948,38 +962,55 @@ function SelectedTraceSheet(props: SelectedTraceSheetProps) {
948962

949963
return (
950964
<SheetContent className="border-l border-gray-800 bg-black p-0 text-white md:max-w-[50%]">
951-
{trace && (
952-
<SheetHeader className="relative border-b border-gray-800 p-4">
953-
<div className="flex items-center justify-between">
954-
<SheetTitle className="text-lg font-medium text-white">
955-
{trace?.operationName ?? <span className="text-gray-400">{'<unknown>'}</span>}
956-
<span className="text-muted-foreground ml-2 font-mono font-normal">
957-
{trace.id.substring(0, 4)}
965+
<SheetHeader className="relative border-b border-gray-800 p-4">
966+
<div className="flex items-center justify-between">
967+
<SheetTitle className="text-lg font-medium text-white">
968+
{trace ? (
969+
<>
970+
{trace.operationName ?? <span className="text-gray-400">{'<unknown>'}</span>}
971+
<span className="text-muted-foreground ml-2 font-mono font-normal">
972+
{trace.id.substring(0, 4)}
973+
</span>
974+
</>
975+
) : (
976+
<Skeleton className="inline-block h-5 w-[260px]" />
977+
)}
978+
</SheetTitle>
979+
</div>
980+
<SheetDescription className="mt-1 text-xs text-gray-400">
981+
Trace ID:{' '}
982+
{trace?.id ? (
983+
<>
984+
<span className="font-mono"> {trace.id}</span>
985+
<CopyIconButton value={trace.id} label="Copy Trace ID" />
986+
</>
987+
) : (
988+
<Skeleton className="inline-block h-4 w-[200px]" />
989+
)}
990+
</SheetDescription>
991+
<div className="mt-2 flex items-center gap-3 text-xs">
992+
{trace ? (
993+
<>
994+
<div className="flex items-center gap-1">
995+
<Clock className="size-3 text-gray-400" />
996+
<span className="text-gray-300">{formatNanoseconds(BigInt(trace.duration))}</span>
997+
</div>
998+
<Badge
999+
variant="outline"
1000+
className={cn(
1001+
'rounded-sm border-0 px-1 font-medium uppercase',
1002+
trace.success ? 'bg-green-900/30 text-green-400' : 'bg-red-900/30 text-red-400',
1003+
)}
1004+
>
1005+
{trace.success ? 'Ok' : 'Error'}
1006+
</Badge>
1007+
<span className="font-mono uppercase text-gray-300">
1008+
{trace ? formatDate(trace.timestamp, 'MMM dd HH:mm:ss') : null}
9581009
</span>
959-
</SheetTitle>
960-
</div>
961-
<SheetDescription className="mt-1 text-xs text-gray-400">
962-
Trace ID: <span className="font-mono">{trace.id}</span>
963-
<CopyIconButton value={trace.id} label="Copy Trace ID" />
964-
</SheetDescription>
965-
<div className="mt-2 flex items-center gap-3 text-xs">
966-
<div className="flex items-center gap-1">
967-
<Clock className="size-3 text-gray-400" />
968-
<span className="text-gray-300">{formatNanoseconds(BigInt(trace.duration))}</span>
969-
</div>
970-
<Badge
971-
variant="outline"
972-
className={cn(
973-
'rounded-sm border-0 px-1 font-medium uppercase',
974-
trace.success ? 'bg-green-900/30 text-green-400' : 'bg-red-900/30 text-red-400',
975-
)}
976-
>
977-
{trace.success ? 'Ok' : 'Error'}
978-
</Badge>
979-
<span className="font-mono uppercase text-gray-300">
980-
{formatDate(trace.timestamp, 'MMM dd HH:mm:ss')}
981-
</span>
982-
</div>
1010+
</>
1011+
) : (
1012+
<Skeleton className="inline-block h-4 w-[150px]" />
1013+
)}
9831014
<Button asChild variant="outline" size="sm">
9841015
<Link
9851016
to="/$organizationSlug/$projectSlug/$targetSlug/trace/$traceId"
@@ -995,8 +1026,8 @@ function SelectedTraceSheet(props: SelectedTraceSheetProps) {
9951026
Full Trace
9961027
</Link>
9971028
</Button>
998-
</SheetHeader>
999-
)}
1029+
</div>
1030+
</SheetHeader>
10001031
{trace && (
10011032
<ImportedTraceSheet
10021033
activeSpanId={null}

0 commit comments

Comments
 (0)