diff --git a/frontend/console/src/features/traces/TraceDetailItem.tsx b/frontend/console/src/features/traces/TraceDetailItem.tsx index a2ba833a0..e2c9132c2 100644 --- a/frontend/console/src/features/traces/TraceDetailItem.tsx +++ b/frontend/console/src/features/traces/TraceDetailItem.tsx @@ -57,10 +57,10 @@ export const TraceDetailItem: React.FC = ({ return (
  • handleEventClick(event.id)}> - - {icon} - {action} - {eventName} + + {icon} + {action} + {eventName}
    diff --git a/frontend/console/src/features/traces/TraceDetails.tsx b/frontend/console/src/features/traces/TraceDetails.tsx index 968c29d7f..81a81b86a 100644 --- a/frontend/console/src/features/traces/TraceDetails.tsx +++ b/frontend/console/src/features/traces/TraceDetails.tsx @@ -1,9 +1,11 @@ import type React from 'react' +import { useMemo } from 'react' import { useNavigate } from 'react-router-dom' import type { TraceEvent } from '../../api/timeline/use-request-trace-events' import type { Event } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { durationToMillis } from '../../utils' import { TraceDetailItem } from './TraceDetailItem' +import { TraceRulerItem } from './TraceRulerItem' +import { requestStartTime, totalDurationForRequest } from './traces.utils' interface TraceDetailsProps { requestKey: string @@ -14,15 +16,8 @@ interface TraceDetailsProps { export const TraceDetails: React.FC = ({ events, selectedEventId, requestKey }) => { const navigate = useNavigate() - const traceEvents = events.map((event) => event.entry.value as TraceEvent) - const requestStartTime = Math.min(...traceEvents.map((event) => event.timeStamp?.toDate().getTime() ?? 0)) - const requestEndTime = Math.max( - ...traceEvents.map((event) => { - const eventDuration = event.duration ? durationToMillis(event.duration) : 0 - return (event.timeStamp?.toDate().getTime() ?? 0) + eventDuration - }), - ) - const totalEventDuration = requestEndTime - requestStartTime + const startTime = useMemo(() => requestStartTime(events), [events]) + const totalEventDuration = useMemo(() => totalDurationForRequest(events), [events]) const handleEventClick = (eventId: bigint) => { navigate(`/traces/${requestKey}?event_id=${eventId}`) @@ -35,11 +30,15 @@ export const TraceDetails: React.FC = ({ events, selectedEven Total Duration: {totalEventDuration} ms

    - Start Time: {new Date(requestStartTime).toLocaleString()} + Start Time: {new Date(startTime).toLocaleString()}

    -
      +
        +
        + +
        + {events.map((event, index) => { const traceEvent = event.entry.value as TraceEvent const eventDurationMs = (traceEvent.duration?.nanos ?? 0) / 1000000 @@ -51,7 +50,7 @@ export const TraceDetails: React.FC = ({ events, selectedEven traceEvent={traceEvent} eventDurationMs={eventDurationMs} requestDurationMs={totalEventDuration} - requestStartTime={requestStartTime} + requestStartTime={startTime} selectedEventId={selectedEventId} handleEventClick={handleEventClick} /> diff --git a/frontend/console/src/features/traces/TraceGraph.tsx b/frontend/console/src/features/traces/TraceGraph.tsx index 8b61f40c5..8280ceefe 100644 --- a/frontend/console/src/features/traces/TraceGraph.tsx +++ b/frontend/console/src/features/traces/TraceGraph.tsx @@ -10,7 +10,7 @@ import { } from '../../protos/xyz/block/ftl/v1/console/console_pb' import { classNames, durationToMillis } from '../../utils' import { eventBackgroundColor } from '../timeline/timeline.utils' -import { eventBarLeftOffsetPercentage } from './traces.utils' +import { eventBarLeftOffsetPercentage, requestStartTime, totalDurationForRequest } from './traces.utils' const EventBlock = ({ event, @@ -87,22 +87,15 @@ export const TraceGraph = ({ requestKey, selectedEventId }: { requestKey?: strin return } - const traceEvents = events.map((event) => event.entry.value as TraceEvent) - const requestStartTime = Math.min(...traceEvents.map((event) => event.timeStamp?.toDate().getTime() ?? 0)) - const requestEndTime = Math.max( - ...traceEvents.map((event) => { - const eventDuration = event.duration ? durationToMillis(event.duration) : 0 - return (event.timeStamp?.toDate().getTime() ?? 0) + eventDuration - }), - ) - const totalEventDuration = requestEndTime - requestStartTime + const startTime = requestStartTime(events) + const totalEventDuration = totalDurationForRequest(events) return (
        {events.map((c, index) => (
        - +
        ))} diff --git a/frontend/console/src/features/traces/TraceGraphHeader.tsx b/frontend/console/src/features/traces/TraceGraphHeader.tsx index e482aaf99..4fc47b0c4 100644 --- a/frontend/console/src/features/traces/TraceGraphHeader.tsx +++ b/frontend/console/src/features/traces/TraceGraphHeader.tsx @@ -1,27 +1,20 @@ import { Activity03Icon } from 'hugeicons-react' +import { useMemo } from 'react' import { useNavigate } from 'react-router-dom' -import { type TraceEvent, useRequestTraceEvents } from '../../api/timeline/use-request-trace-events' -import { durationToMillis } from '../../utils' +import { useRequestTraceEvents } from '../../api/timeline/use-request-trace-events' +import { totalDurationForRequest } from './traces.utils' export const TraceGraphHeader = ({ requestKey, eventId }: { requestKey?: string; eventId: bigint }) => { const navigate = useNavigate() const requestEvents = useRequestTraceEvents(requestKey) const events = requestEvents.data?.reverse() ?? [] + const totalEventDuration = useMemo(() => totalDurationForRequest(events), [events]) + if (events.length === 0) { return null } - const traceEvents = events.map((event) => event.entry.value as TraceEvent) - const requestStartTime = Math.min(...traceEvents.map((event) => event.timeStamp?.toDate().getTime() ?? 0)) - const requestEndTime = Math.max( - ...traceEvents.map((event) => { - const eventDuration = event.duration ? durationToMillis(event.duration) : 0 - return (event.timeStamp?.toDate().getTime() ?? 0) + eventDuration - }), - ) - const totalEventDuration = requestEndTime - requestStartTime - return (
        diff --git a/frontend/console/src/features/traces/TraceGraphRuler.tsx b/frontend/console/src/features/traces/TraceGraphRuler.tsx new file mode 100644 index 000000000..6511ee719 --- /dev/null +++ b/frontend/console/src/features/traces/TraceGraphRuler.tsx @@ -0,0 +1,18 @@ +export const TraceGraphRuler = ({ duration }: { duration: number }) => { + const tickInterval = duration / 4 + const ticks = Array.from({ length: 5 }, (_, i) => ({ + value: Math.round(i * tickInterval), + position: `${(i * 100) / 4}%`, + })) + + return ( +
        + {ticks.map((tick, index) => ( +
        + {tick.value}ms + +
        + ))} +
        + ) +} diff --git a/frontend/console/src/features/traces/TraceRulerItem.tsx b/frontend/console/src/features/traces/TraceRulerItem.tsx new file mode 100644 index 000000000..8c8778f79 --- /dev/null +++ b/frontend/console/src/features/traces/TraceRulerItem.tsx @@ -0,0 +1,13 @@ +import { TraceGraphRuler } from './TraceGraphRuler' + +export const TraceRulerItem = ({ duration }: { duration: number }) => { + return ( +
      • + +
        + +
        + +
      • + ) +} diff --git a/frontend/console/src/features/traces/details/TraceDetailsAsyncCall.tsx b/frontend/console/src/features/traces/details/TraceDetailsAsyncCall.tsx index bbc5d7108..ca3fac299 100644 --- a/frontend/console/src/features/traces/details/TraceDetailsAsyncCall.tsx +++ b/frontend/console/src/features/traces/details/TraceDetailsAsyncCall.tsx @@ -3,10 +3,12 @@ import { CodeBlock } from '../../../components/CodeBlock' import type { AsyncExecuteEvent, Event } from '../../../protos/xyz/block/ftl/v1/console/console_pb' import { formatDuration } from '../../../utils/date.utils' import { DeploymentCard } from '../../deployments/DeploymentCard' +import { asyncEventTypeString } from '../../timeline/timeline.utils' import { refString } from '../../verbs/verb.utils' export const TraceDetailsAsyncCall = ({ event }: { event: Event }) => { const asyncCall = event.entry.value as AsyncExecuteEvent + return ( <> Async Call Details @@ -21,14 +23,17 @@ export const TraceDetailsAsyncCall = ({ event }: { event: Event }) => {
          +
        • + +
        • +
        • + +
        • {asyncCall.requestKey && (
        • )} -
        • - -
        • {asyncCall.verbRef && (
        • diff --git a/frontend/console/src/features/traces/details/TraceDetailsPubsubPublish.tsx b/frontend/console/src/features/traces/details/TraceDetailsPubsubPublish.tsx index 8fc4af688..7e059a659 100644 --- a/frontend/console/src/features/traces/details/TraceDetailsPubsubPublish.tsx +++ b/frontend/console/src/features/traces/details/TraceDetailsPubsubPublish.tsx @@ -9,8 +9,14 @@ export const TraceDetailsPubsubPublish = ({ event }: { event: Event }) => { const pubsubPublish = event.entry.value as PubSubPublishEvent return ( <> - Pubsub Publish Details + PubSub Publish Details + {pubsubPublish.request && ( + <> +

          Request

          + + + )} {pubsubPublish.error && ( <>

          Error

          @@ -21,22 +27,20 @@ export const TraceDetailsPubsubPublish = ({ event }: { event: Event }) => {
            - {pubsubPublish.requestKey && ( -
          • - -
          • - )} +
          • + +
          • - {pubsubPublish.verbRef && ( + {pubsubPublish.requestKey && (
          • - +
          • )} {pubsubPublish.verbRef && (
          • - +
          • )}
          diff --git a/frontend/console/src/features/traces/traces.utils.ts b/frontend/console/src/features/traces/traces.utils.ts index ca91033de..59e3767d8 100644 --- a/frontend/console/src/features/traces/traces.utils.ts +++ b/frontend/console/src/features/traces/traces.utils.ts @@ -1,5 +1,6 @@ +import type { TraceEvent } from '../../api/timeline/use-request-trace-events' import type { Event } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { compareTimestamps } from '../../utils' +import { compareTimestamps, durationToMillis } from '../../utils' export const eventBarLeftOffsetPercentage = (event: Event, requestStartTime: number, requestDurationMs: number) => { if (!event.timeStamp) { @@ -37,3 +38,19 @@ export const groupEventsByRequestKey = (events: Event[]): Record { + const traceEvents = events.map((event) => event.entry.value as TraceEvent) + return Math.min(...traceEvents.map((event) => event.timeStamp?.toDate().getTime() ?? 0)) +} + +export const totalDurationForRequest = (events: Event[]): number => { + const traceEvents = events.map((event) => event.entry.value as TraceEvent) + const requestEndTime = Math.max( + ...traceEvents.map((event) => { + const eventDuration = event.duration ? durationToMillis(event.duration) : 0 + return (event.timeStamp?.toDate().getTime() ?? 0) + eventDuration + }), + ) + return requestEndTime - requestStartTime(events) +}