diff --git a/src/stories/mockComponents/App.tsx b/src/stories/mockComponents/App.tsx index e379608..e626aa7 100644 --- a/src/stories/mockComponents/App.tsx +++ b/src/stories/mockComponents/App.tsx @@ -17,18 +17,17 @@ import { NavItemText, } from '@zendeskgarden/react-chrome' import { PALETTE } from '@zendeskgarden/react-theming' - import { ReactComponent as MenuTrayIcon } from '@zendeskgarden/svg-icons/src/16/grid-2x2-stroke.svg' import { ReactComponent as PersonIcon } from '@zendeskgarden/svg-icons/src/16/user-solo-stroke.svg' +import { ReactComponent as ClearIcon } from '@zendeskgarden/svg-icons/src/26/arrow-right-left.svg' import { ReactComponent as ProductIcon } from '@zendeskgarden/svg-icons/src/26/garden.svg' import { ReactComponent as HomeIcon } from '@zendeskgarden/svg-icons/src/26/home-fill.svg' -import { ReactComponent as ClearIcon } from '@zendeskgarden/svg-icons/src/26/arrow-right-left.svg' import { ReactComponent as ZendeskIcon } from '@zendeskgarden/svg-icons/src/26/zendesk.svg' -import { TicketList } from './TicketList' -import { TicketView } from './TicketView' +import type { Operation } from '../../v2/operation' import { mockTickets } from './mockTickets' import { operationManager } from './operationManager' -import type { Operation } from '../../v2/operation' +import { TicketList } from './TicketList' +import { TicketView } from './TicketView' export const App: React.FC = () => { const [selectedTicketId, setSelectedTicketId] = useState(null) @@ -45,15 +44,15 @@ export const App: React.FC = () => { operationName: `ticket-activation`, track: [ { - match: { type: 'component-unmount', metadata: { ticketId: id } }, + match: { type: 'component-unmount', attributes: { ticketId: id } }, interruptWhenSeen: true, }, { - match: { metadata: { ticketId: id } }, - debounceEndWhenSeen: { debounceBy: 1000 }, + match: { attributes: { ticketId: id } }, + debounceEndWhenSeen: { debounceBy: 1_000 }, }, { - match: { metadata: { ticketId: id, visibleState: 'complete' } }, + match: { attributes: { ticketId: id, visibleState: 'complete' } }, requiredToEnd: true, }, ], diff --git a/src/stories/mockComponents/TicketView.tsx b/src/stories/mockComponents/TicketView.tsx index c8f5f77..06869be 100644 --- a/src/stories/mockComponents/TicketView.tsx +++ b/src/stories/mockComponents/TicketView.tsx @@ -9,7 +9,7 @@ import { Paragraph, Span, XXL } from '@zendeskgarden/react-typography' import { ReactComponent as UserIcon } from '@zendeskgarden/svg-icons/src/16/user-solo-stroke.svg' import { VISIBLE_STATE } from '../../main' import { TimingComponent } from '../../v2/element' -import { useCaptureRenderBeaconTask } from '../../v2/hooks' +import { useCaptureRenderBeaconTask, useRenderProcessTrace } from '../../v2/hooks' import { mockTickets } from './mockTickets' import { operationManager } from './operationManager' @@ -34,9 +34,19 @@ export const TicketView: React.FC = ({ cached = false, onLoaded, }) => { + + useRenderProcessTrace({ + operationManager, operationName: 'TicketView', onEnd: (trace) => { + console.log('TicketView trace', trace, "ticketId", ticketId) + }, track: [{ match: { attributes: { ticketId } } }, { //debounce on any event that has the same ticket id + match: { attributes: { ticketId, visibleState: 'complete' } }, //required to end the operation, ticket fully loaded! + requiredToEnd: true, + }] + }, [ticketId]) + useCaptureRenderBeaconTask({ componentName: 'TicketView', - metadata: { ticketId, loading: !cached }, + attributes: { ticketId, loading: !cached }, visibleState: cached ? VISIBLE_STATE.COMPLETE : VISIBLE_STATE.LOADING, operationManager, }) diff --git a/src/v2/__fixtures__/ticket-activation-select-tab-1.json b/src/v2/__fixtures__/ticket-activation-select-tab-1.json index 2df5a2a..39f8d43 100644 --- a/src/v2/__fixtures__/ticket-activation-select-tab-1.json +++ b/src/v2/__fixtures__/ticket-activation-select-tab-1.json @@ -99,7 +99,7 @@ "internalOrder": 1 } }, - "metadata": { + "attributes": { "metricId": "toolbar/tabs" }, "event": { @@ -123,7 +123,7 @@ "internalOrder": 2 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -146,7 +146,7 @@ "internalOrder": 3 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "ticket/load_omnilog_from_new_workspace", "kind": "mark", @@ -167,7 +167,7 @@ "internalOrder": 4 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "ticket.activate", "kind": "mark", @@ -189,7 +189,7 @@ "internalOrder": 5 } }, - "metadata": { + "attributes": { "metricId": "toolbar/profile_menu_open" }, "event": { @@ -212,7 +212,7 @@ "internalOrder": 6 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -266,7 +266,7 @@ "internalOrder": 7 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "long-animation-frame", "kind": "long-animation-frame", @@ -322,7 +322,7 @@ "internalOrder": 8 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "long-animation-frame", "kind": "long-animation-frame", @@ -364,7 +364,7 @@ "internalOrder": 9 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -388,7 +388,7 @@ "internalOrder": 10 } }, - "metadata": { + "attributes": { "metricId": "toolbar/profile_menu_open" }, "event": { @@ -412,7 +412,7 @@ "internalOrder": 11 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -436,7 +436,7 @@ "internalOrder": 12 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -460,7 +460,7 @@ "internalOrder": 13 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -484,7 +484,7 @@ "internalOrder": 14 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -508,7 +508,7 @@ "internalOrder": 15 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -532,7 +532,7 @@ "internalOrder": 16 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -556,7 +556,7 @@ "internalOrder": 17 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -580,7 +580,7 @@ "internalOrder": 18 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -604,7 +604,7 @@ "internalOrder": 19 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -627,7 +627,7 @@ "internalOrder": 20 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -723,7 +723,7 @@ "internalOrder": 21 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -795,7 +795,7 @@ "internalOrder": 22 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -840,7 +840,7 @@ "name": "OmniLog", "startTime": 41709.60000000149, "duration": 30.09999999962747, - "metadata": { + "attributes": { "visibleState": "loading", "renderCount": 0, "omnilogOrder": "asc", @@ -869,7 +869,7 @@ "name": "ConversationPane", "startTime": 41656, "duration": 91.5, - "metadata": { + "attributes": { "visibleState": "loading", "renderCount": 0, "conversationState": "unknown", @@ -916,7 +916,7 @@ "internalOrder": 25 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation", "previousVisibleState": "initial", "visibleState": "loading" @@ -942,7 +942,7 @@ "internalOrder": 26 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "models/conversations/fetch", "kind": "measure", @@ -964,7 +964,7 @@ "internalOrder": 27 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "workspace.contextual_workspace.resolution", "kind": "measure", @@ -985,7 +985,7 @@ "internalOrder": 28 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "element/text-paint", "kind": "element", @@ -1022,7 +1022,7 @@ "internalOrder": 29 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "long-animation-frame", "kind": "long-animation-frame", @@ -1109,7 +1109,7 @@ "internalOrder": 30 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -1133,7 +1133,7 @@ "internalOrder": 31 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -1157,7 +1157,7 @@ "internalOrder": 32 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -1181,7 +1181,7 @@ "internalOrder": 33 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -1205,7 +1205,7 @@ "internalOrder": 34 } }, - "metadata": { + "attributes": { "metricId": "AgentHomeMain/application" }, "event": { @@ -1229,7 +1229,7 @@ "internalOrder": 35 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -1253,7 +1253,7 @@ "internalOrder": 36 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader", "previousVisibleState": "loading", "visibleState": "ready" @@ -1279,7 +1279,7 @@ "internalOrder": 37 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -1303,7 +1303,7 @@ "internalOrder": 38 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -1327,7 +1327,7 @@ "internalOrder": 39 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log" }, "event": { @@ -1351,7 +1351,7 @@ "internalOrder": 40 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -1375,7 +1375,7 @@ "internalOrder": 41 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -1399,7 +1399,7 @@ "internalOrder": 42 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -1423,7 +1423,7 @@ "internalOrder": 43 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -1447,7 +1447,7 @@ "internalOrder": 44 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -1471,7 +1471,7 @@ "internalOrder": 45 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -1495,7 +1495,7 @@ "internalOrder": 46 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -1519,7 +1519,7 @@ "internalOrder": 47 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -1568,7 +1568,7 @@ "internalOrder": 48 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -1640,7 +1640,7 @@ "internalOrder": 49 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -1712,7 +1712,7 @@ "internalOrder": 50 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -1784,7 +1784,7 @@ "internalOrder": 51 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -1826,7 +1826,7 @@ "name": "ConversationPane", "startTime": 41826.20000000112, "duration": 4.299999998882413, - "metadata": { + "attributes": { "visibleState": "loading", "renderCount": 1, "conversationState": "unknown", @@ -1898,7 +1898,7 @@ "internalOrder": 53 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -1970,7 +1970,7 @@ "internalOrder": 54 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2044,7 +2044,7 @@ "internalOrder": 55 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2118,7 +2118,7 @@ "internalOrder": 56 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2192,7 +2192,7 @@ "internalOrder": 57 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2234,7 +2234,7 @@ "name": "OmniLog", "startTime": 41882.40000000037, "duration": 21.300000000745058, - "metadata": { + "attributes": { "visibleState": "loading", "renderCount": 1, "omnilogOrder": "asc", @@ -2268,7 +2268,7 @@ "internalOrder": 59 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "open_ticket/paint/ticket_workspace", "kind": "measure", @@ -2290,7 +2290,7 @@ "internalOrder": 60 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "ticket/open", "kind": "measure", @@ -2312,7 +2312,7 @@ "internalOrder": 61 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "models/ticket/fetch", "kind": "measure", @@ -2333,7 +2333,7 @@ "internalOrder": 62 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "long-animation-frame", "kind": "long-animation-frame", @@ -2450,7 +2450,7 @@ "internalOrder": 63 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -2474,7 +2474,7 @@ "internalOrder": 64 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -2498,7 +2498,7 @@ "internalOrder": 65 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log" }, "event": { @@ -2522,7 +2522,7 @@ "internalOrder": 66 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -2546,7 +2546,7 @@ "internalOrder": 67 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -2570,7 +2570,7 @@ "internalOrder": 68 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -2594,7 +2594,7 @@ "internalOrder": 69 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -2643,7 +2643,7 @@ "internalOrder": 70 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2690,7 +2690,7 @@ "internalOrder": 71 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2761,7 +2761,7 @@ "internalOrder": 72 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2832,7 +2832,7 @@ "internalOrder": 73 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2903,7 +2903,7 @@ "internalOrder": 74 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -2974,7 +2974,7 @@ "internalOrder": 75 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3046,7 +3046,7 @@ "internalOrder": 76 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "workspace.contextual_workspace.resolution", "kind": "measure", @@ -3068,7 +3068,7 @@ "internalOrder": 77 } }, - "metadata": { + "attributes": { "metricId": "toolbar/tabs" }, "event": { @@ -3092,7 +3092,7 @@ "internalOrder": 78 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -3119,7 +3119,7 @@ "internalOrder": 79 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3190,7 +3190,7 @@ "internalOrder": 80 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3270,7 +3270,7 @@ "internalOrder": 81 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3350,7 +3350,7 @@ "internalOrder": 82 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3397,7 +3397,7 @@ "internalOrder": 83 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3468,7 +3468,7 @@ "internalOrder": 84 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/local/query/Groups", "kind": "measure", @@ -3490,7 +3490,7 @@ "internalOrder": 85 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -3514,7 +3514,7 @@ "internalOrder": 86 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -3533,7 +3533,7 @@ "name": "ConversationPane", "startTime": 41979.70000000112, "duration": 20.09999999962747, - "metadata": { + "attributes": { "visibleState": "loading", "renderCount": 3, "conversationState": "unknown", @@ -3605,7 +3605,7 @@ "internalOrder": 88 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3659,7 +3659,7 @@ "internalOrder": 89 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/local/query/groupAgents", "kind": "measure", @@ -3680,7 +3680,7 @@ "internalOrder": 90 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/SkillTypes", "kind": "measure", @@ -3701,7 +3701,7 @@ "internalOrder": 91 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/AgentGraphMostUsedMacrosQuery", "kind": "measure", @@ -3723,7 +3723,7 @@ "internalOrder": 92 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -3747,7 +3747,7 @@ "internalOrder": 93 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -3771,7 +3771,7 @@ "internalOrder": 94 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log" }, "event": { @@ -3795,7 +3795,7 @@ "internalOrder": 95 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -3819,7 +3819,7 @@ "internalOrder": 96 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -3843,7 +3843,7 @@ "internalOrder": 97 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -3866,7 +3866,7 @@ "internalOrder": 98 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3943,7 +3943,7 @@ "internalOrder": 99 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -3988,7 +3988,7 @@ "internalOrder": 100 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4058,7 +4058,7 @@ "internalOrder": 101 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/user", "kind": "measure", @@ -4079,7 +4079,7 @@ "internalOrder": 102 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/chat/query/agentChatProfilesQuery", "kind": "measure", @@ -4101,7 +4101,7 @@ "internalOrder": 103 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -4125,7 +4125,7 @@ "internalOrder": 104 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -4149,7 +4149,7 @@ "internalOrder": 105 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -4173,7 +4173,7 @@ "internalOrder": 106 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -4197,7 +4197,7 @@ "internalOrder": 107 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -4221,7 +4221,7 @@ "internalOrder": 108 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -4245,7 +4245,7 @@ "internalOrder": 109 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -4294,7 +4294,7 @@ "internalOrder": 110 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4340,7 +4340,7 @@ "internalOrder": 111 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4414,7 +4414,7 @@ "internalOrder": 112 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -4463,7 +4463,7 @@ "internalOrder": 113 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4518,7 +4518,7 @@ "internalOrder": 114 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -4541,7 +4541,7 @@ "internalOrder": 115 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/ticket", "kind": "measure", @@ -4562,7 +4562,7 @@ "internalOrder": 116 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4661,7 +4661,7 @@ "internalOrder": 117 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4740,7 +4740,7 @@ "internalOrder": 118 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4794,7 +4794,7 @@ "internalOrder": 119 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/platform-data-graph-gateway/query/ticketPolicyMetrics", "kind": "measure", @@ -4816,7 +4816,7 @@ "internalOrder": 120 } }, - "metadata": { + "attributes": { "metricId": "ticket/agent_collision" }, "event": { @@ -4835,7 +4835,7 @@ "name": "ConversationPane", "startTime": 41656, "duration": 600.8000000007451, - "metadata": { + "attributes": { "previousVisibleState": "loading", "visibleState": "ready", "renderCount": 4, @@ -4878,7 +4878,7 @@ "name": "OmniLog", "startTime": 41709.60000000149, "duration": 552.7999999988824, - "metadata": { + "attributes": { "previousVisibleState": "loading", "visibleState": "complete", "renderCount": 2, @@ -4917,7 +4917,7 @@ "internalOrder": 123 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -4970,7 +4970,7 @@ "name": "OmniLog", "startTime": 42262.40000000037, "duration": 94.70000000111759, - "metadata": { + "attributes": { "visibleState": "complete", "renderCount": 2, "conversationLogPrivacyFilter": "ALL", @@ -5000,7 +5000,7 @@ "name": "ConversationPane", "startTime": 42256.800000000745, "duration": 100.40000000037253, - "metadata": { + "attributes": { "visibleState": "ready", "renderCount": 4, "conversationState": "ENDED", @@ -5042,7 +5042,7 @@ "name": "OmniLog", "startTime": 42374.10000000149, "duration": 17.59999999962747, - "metadata": { + "attributes": { "visibleState": "complete", "renderCount": 3, "conversationLogPrivacyFilter": "ALL", @@ -5077,7 +5077,7 @@ "internalOrder": 127 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "ticket/load_omnilog_from_new_workspace", "kind": "measure", @@ -5098,7 +5098,7 @@ "internalOrder": 128 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/BFFConvoLogQuery", "kind": "measure", @@ -5120,7 +5120,7 @@ "internalOrder": 129 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log", "previousVisibleState": "loading", "visibleState": "ready" @@ -5146,7 +5146,7 @@ "internalOrder": 130 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log", "previousVisibleState": "loading", "visibleState": "ready" @@ -5177,7 +5177,7 @@ "internalOrder": 131 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation", "previousVisibleState": "loading", "visibleState": "ready" @@ -5202,7 +5202,7 @@ "internalOrder": 132 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "long-animation-frame", "kind": "long-animation-frame", @@ -5259,7 +5259,7 @@ "internalOrder": 133 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -5283,7 +5283,7 @@ "internalOrder": 134 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -5307,7 +5307,7 @@ "internalOrder": 135 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -5331,7 +5331,7 @@ "internalOrder": 136 } }, - "metadata": { + "attributes": { "metricId": "toolbar/profile_menu_open" }, "event": { @@ -5355,7 +5355,7 @@ "internalOrder": 137 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -5379,7 +5379,7 @@ "internalOrder": 138 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -5403,7 +5403,7 @@ "internalOrder": 139 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -5427,7 +5427,7 @@ "internalOrder": 140 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log" }, "event": { @@ -5451,7 +5451,7 @@ "internalOrder": 141 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -5475,7 +5475,7 @@ "internalOrder": 142 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -5499,7 +5499,7 @@ "internalOrder": 143 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -5523,7 +5523,7 @@ "internalOrder": 144 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -5546,7 +5546,7 @@ "internalOrder": 145 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "ticket/load_omnilog_from_new_workspace/end", "kind": "mark", @@ -5568,7 +5568,7 @@ "internalOrder": 146 } }, - "metadata": { + "attributes": { "metricId": "toolbar/tabs" }, "event": { @@ -5592,7 +5592,7 @@ "internalOrder": 147 } }, - "metadata": { + "attributes": { "metricId": "ticket/assignee_field_open" }, "event": { @@ -5616,7 +5616,7 @@ "internalOrder": 148 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -5640,7 +5640,7 @@ "internalOrder": 149 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -5664,7 +5664,7 @@ "internalOrder": 150 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -5688,7 +5688,7 @@ "internalOrder": 151 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -5712,7 +5712,7 @@ "internalOrder": 152 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "macro_shortcuts/load_time", "kind": "measure", @@ -5733,7 +5733,7 @@ "internalOrder": 153 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -5830,7 +5830,7 @@ "internalOrder": 154 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -5904,7 +5904,7 @@ "internalOrder": 155 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -5976,7 +5976,7 @@ "internalOrder": 156 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6018,7 +6018,7 @@ "name": "OmniLog", "startTime": 42556.5, "duration": 24.800000000745058, - "metadata": { + "attributes": { "visibleState": "complete", "renderCount": 5, "conversationLogPrivacyFilter": "ALL", @@ -6078,7 +6078,7 @@ "internalOrder": 158 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6132,7 +6132,7 @@ "internalOrder": 159 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -6156,7 +6156,7 @@ "internalOrder": 160 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -6180,7 +6180,7 @@ "internalOrder": 161 } }, - "metadata": { + "attributes": { "metricId": "toolbar/tabs" }, "event": { @@ -6229,7 +6229,7 @@ "internalOrder": 162 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6309,7 +6309,7 @@ "internalOrder": 163 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6384,7 +6384,7 @@ "internalOrder": 164 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6431,7 +6431,7 @@ "internalOrder": 165 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "create_omnicomposer_first_load", "kind": "measure", @@ -6452,7 +6452,7 @@ "internalOrder": 166 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/platform-data-graph-gateway/query/AgentGraphChatAttachmentSettings", "kind": "measure", @@ -6473,7 +6473,7 @@ "internalOrder": 167 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "element/text-paint", "kind": "element", @@ -6511,7 +6511,7 @@ "internalOrder": 168 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -6535,7 +6535,7 @@ "internalOrder": 169 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -6558,7 +6558,7 @@ "internalOrder": 170 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6629,7 +6629,7 @@ "internalOrder": 171 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6700,7 +6700,7 @@ "internalOrder": 172 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "open_ticket/paint/omnilog", "kind": "measure", @@ -6721,7 +6721,7 @@ "internalOrder": 173 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/local/query/user", "kind": "measure", @@ -6742,7 +6742,7 @@ "internalOrder": 174 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/support_remote/query/ticketForm", "kind": "measure", @@ -6763,7 +6763,7 @@ "internalOrder": 175 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -6830,7 +6830,7 @@ "name": "ConversationPane", "startTime": 42629.800000000745, "duration": 25.699999999254942, - "metadata": { + "attributes": { "visibleState": "ready", "renderCount": 6, "conversationState": "ENDED", @@ -6877,7 +6877,7 @@ "internalOrder": 177 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -6901,7 +6901,7 @@ "internalOrder": 178 } }, - "metadata": { + "attributes": { "metricId": "ticket/omniheader" }, "event": { @@ -6925,7 +6925,7 @@ "internalOrder": 179 } }, - "metadata": { + "attributes": { "metricId": "ticket/omnilog/conversation_log" }, "event": { @@ -6949,7 +6949,7 @@ "internalOrder": 180 } }, - "metadata": { + "attributes": { "metricId": "performance/serve/conversation/conversation_log" }, "event": { @@ -6973,7 +6973,7 @@ "internalOrder": 181 } }, - "metadata": { + "attributes": { "metricId": "performance/ticket/activation" }, "event": { @@ -7022,7 +7022,7 @@ "internalOrder": 182 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7076,7 +7076,7 @@ "internalOrder": 183 } }, - "metadata": { + "attributes": { "metricId": "ticket/footer" }, "event": { @@ -7099,7 +7099,7 @@ "internalOrder": 184 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/platform-data-graph-gateway/query/Lotus__IntelligenceSettings", "kind": "measure", @@ -7146,7 +7146,7 @@ "internalOrder": 185 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7192,7 +7192,7 @@ "internalOrder": 186 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/chat/query/customerProfile", "kind": "measure", @@ -7213,7 +7213,7 @@ "internalOrder": 187 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7312,7 +7312,7 @@ "internalOrder": 188 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7361,7 +7361,7 @@ "internalOrder": 189 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/local/query/user", "kind": "measure", @@ -7408,7 +7408,7 @@ "internalOrder": 190 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7457,7 +7457,7 @@ "internalOrder": 191 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -7481,7 +7481,7 @@ "internalOrder": 192 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -7505,7 +7505,7 @@ "internalOrder": 193 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -7528,7 +7528,7 @@ "internalOrder": 194 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7599,7 +7599,7 @@ "internalOrder": 195 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -7623,7 +7623,7 @@ "internalOrder": 196 } }, - "metadata": { + "attributes": { "metricId": "ticket/appSidebar" }, "event": { @@ -7647,7 +7647,7 @@ "internalOrder": 197 } }, - "metadata": { + "attributes": { "metricId": "navBar/apps" }, "event": { @@ -7670,7 +7670,7 @@ "internalOrder": 198 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7766,7 +7766,7 @@ "internalOrder": 199 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7840,7 +7840,7 @@ "internalOrder": 200 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7887,7 +7887,7 @@ "internalOrder": 201 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/local/query/locale", "kind": "measure", @@ -7934,7 +7934,7 @@ "internalOrder": 202 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -7987,7 +7987,7 @@ "internalOrder": 203 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/platform-data-graph-gateway/query/AgentGraphChatComposerShortcuts", "kind": "measure", @@ -8034,7 +8034,7 @@ "internalOrder": 204 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -8087,7 +8087,7 @@ "internalOrder": 205 } }, - "metadata": {}, + "attributes": {}, "event": { "commonName": "graphql/platform-data-graph-gateway/query/Lotus__Brands", "kind": "measure", @@ -8108,7 +8108,7 @@ "internalOrder": 206 } }, - "metadata": { + "attributes": { "lastAction": { "id": "d18a96ca-026d-4b55-ae35-1ea0eedca504", "target": { @@ -8173,7 +8173,7 @@ "serverTiming": [] } ], - "metadata": { + "attributes": { "ticketId": "13024", "origin": "select-tab", "visibleState": "ready", diff --git a/src/v2/constants.ts b/src/v2/constants.ts index eb689f8..1351c77 100644 --- a/src/v2/constants.ts +++ b/src/v2/constants.ts @@ -45,4 +45,4 @@ export const DEFAULT_OBSERVED_ENTRY_TYPES = [ ] /** when present on 'detail' of a performance measure/mark, it will ignore that event */ -export const SKIP_PROCESSING = Symbol.for('SKIP_PROCESSING') +export const SKIP_PROCESSING = '__SKIP_PROCESSING__' diff --git a/src/v2/defaultEventProcessor.ts b/src/v2/defaultEventProcessor.ts index 83d4340..3a149fb 100644 --- a/src/v2/defaultEventProcessor.ts +++ b/src/v2/defaultEventProcessor.ts @@ -15,17 +15,17 @@ export const defaultEventProcessor: EventProcessor = ( } const detail = typeof entry.detail === 'object' && entry.detail - const existingMetadata = - 'metadata' in entry && typeof entry.metadata === 'object' - ? entry.metadata + const existingAttributes = + 'attributes' in entry && typeof entry.attributes === 'object' + ? entry.attributes : {} - const metadata = detail - ? { ...detail, ...existingMetadata } - : existingMetadata + const attributes = detail + ? { ...detail, ...existingAttributes } + : existingAttributes const inputEvent = entry as InputEvent - inputEvent.metadata = metadata + inputEvent.attributes = attributes if (!('operations' in entry) || typeof entry.operations !== 'object') { inputEvent.operations = {} @@ -43,14 +43,14 @@ export const defaultEventProcessor: EventProcessor = ( if (entry.entryType === 'resource' || entry.entryType === 'navigation') { const { commonUrl, query, hash } = getCommonUrlForTracing(entry.name) commonName = commonUrl - metadata.resourceQuery = query - metadata.resourceHash = hash + attributes.resourceQuery = query + attributes.resourceHash = hash if (entry.entryType === 'resource') { const resource = - metadata.resource && - typeof metadata.resource === 'object' && - metadata.resource + attributes.resource && + typeof attributes.resource === 'object' && + attributes.resource const resourceType = resource && typeof resource.type === 'string' && resource.type const statusCode = diff --git a/src/v2/element.tsx b/src/v2/element.tsx index 9ceb7a3..eac09a4 100644 --- a/src/v2/element.tsx +++ b/src/v2/element.tsx @@ -9,14 +9,12 @@ const visuallyHiddenButRenderable: React.CSSProperties = { pointerEvents: 'none', } as const -export const TimingComponent = React.memo(({ name }: { name: string }) => { - return ( -

-   -

- ) -}) +export const TimingComponent = React.memo(({ name }: { name: string }) => ( +

+   +

+)) diff --git a/src/v2/hooks.ts b/src/v2/hooks.ts index f83baf5..3e293c4 100644 --- a/src/v2/hooks.ts +++ b/src/v2/hooks.ts @@ -1,21 +1,21 @@ // TODO: maybe even a HOC/wrapper instead? this way I could ensure to add a hook at beginning and at the end of the component -import { useEffect, useRef } from 'react' +import { type DependencyList, useEffect, useRef } from 'react' import { useOnComponentUnmount } from '../ErrorBoundary' import { VISIBLE_STATE } from './constants' import { OperationManager } from './operation' -import type { InputEvent, Metadata, OperationDefinition } from './types' +import type { Attributes, InputEvent, OperationDefinition } from './types' // TODO: make a getUseCaptureRenderBeaconTask, and provide OperationManager there export const useCaptureRenderBeaconTask = ({ componentName, - metadata: meta, + attributes: attr, error, visibleState = VISIBLE_STATE.COMPLETE, operationManager, }: { componentName: string - metadata: Record + attributes: Record error?: object /** * what is the state of the UX that the user sees as part of this render? @@ -25,10 +25,10 @@ export const useCaptureRenderBeaconTask = ({ operationManager: OperationManager }) => { const renderCountRef = useRef(0) - const metadata: Metadata = { + const attributes: Attributes = { visibleState: error ? VISIBLE_STATE.ERROR : visibleState, renderCount: renderCountRef.current, - ...meta, + ...attr, } renderCountRef.current += 1 const renderStartTask = { @@ -36,7 +36,7 @@ export const useCaptureRenderBeaconTask = ({ name: componentName, startTime: operationManager.performance.now(), duration: 0, - metadata, + attributes, } satisfies InputEvent const nextStateObj = { visibleState, startTime: renderStartTask.startTime } @@ -49,9 +49,9 @@ export const useCaptureRenderBeaconTask = ({ name: componentName, startTime: previousState.startTime, duration: renderStartTask.startTime - previousState.startTime, - metadata: { + attributes: { previousVisibleState: previousState.visibleState, - ...metadata, + ...attributes, }, } satisfies InputEvent) } @@ -84,7 +84,7 @@ export const useCaptureRenderBeaconTask = ({ name: componentName, startTime: renderStartTask.startTime, duration: operationManager.performance.now() - renderStartTask.startTime, - metadata, + attributes, } satisfies InputEvent) }) @@ -97,7 +97,7 @@ export const useCaptureRenderBeaconTask = ({ name: componentName, startTime: operationManager.performance.now(), duration: 0, - metadata, + attributes, event: { ...(errorBoundaryMetadata ? { @@ -121,26 +121,51 @@ export const useCaptureRenderBeaconTask = ({ // records until all required render beacons are settled // (and until the page is interactive? or maybe that's the central manager's job) -export const useCaptureOperationTiming = ( - operationDefinition: OperationDefinition & { +export const useRenderProcessTrace = ( + traceDefinition: OperationDefinition & { // the operation will start once 'active' is true (or undefined) active?: boolean operationManager: OperationManager }, + restartWhenChanged: DependencyList, ) => { - const { active = true, operationManager, operationName } = operationDefinition - // const lastStartedNameRef = useRef(operationDefinition.operationName) - const startedOperationNameRef = useRef() - // only start the operation once (but allow another one to be started if the name changes) - if (!active || startedOperationNameRef.current === operationName) return - operationManager.startOperation(operationDefinition) - startedOperationNameRef.current = operationName + const { active = true, operationManager } = traceDefinition + const activeTraceId = useRef(undefined) + + useEffect(() => { + if (!active) { + return undefined + } + + return () => { + if (activeTraceId.current) { + // TODO: we dont want to throw away the operation if the operation is debouncing! + operationManager.cancelOperation(activeTraceId.current) + activeTraceId.current = undefined + } + } + }, [active]) + + // this will fire when external deps have changed: + // Note: we cannot use useEffect has we need this code to run during the render + // and especially before we call actionLogRef.current.setActive. + const isFirstTrace = useRef(true) + const lastExternalDeps = useRef(restartWhenChanged) + const externalDepsHaveChanged = restartWhenChanged.some( + (value, i) => value !== lastExternalDeps.current[i], + ) + + if (externalDepsHaveChanged || isFirstTrace.current) { + isFirstTrace.current = false + lastExternalDeps.current = restartWhenChanged + activeTraceId.current = operationManager.startOperation(traceDefinition) + } // starts an operation when: // - 'active' is true, // - all requiredToStart timings have been seen - // all metadata from required tasks is merged into the operation metadata - // the resulting task will have metadata that explains why it ended: + // all attributes from required tasks is merged into the operation attributes + // the resulting task will have attributes that explains why it ended: // - 'interrupted' if an interruptWhenSeen task was seen // - 'timeout' if the timeout was reached // - 'error' diff --git a/src/v2/operation.test.ts b/src/v2/operation.test.ts index cb2d500..199188b 100644 --- a/src/v2/operation.test.ts +++ b/src/v2/operation.test.ts @@ -1,5 +1,4 @@ /* eslint-disable jest/no-conditional-in-test */ -import { DEFAULT_DEBOUNCE_MS } from '../constants' import ticketActivationFixtureData from './__fixtures__/ticket-activation-select-tab-1.json' import { DEFAULT_DEBOUNCE_TIME, SKIP_PROCESSING } from './constants' import { type Operation, OperationManager } from './operation' @@ -26,7 +25,7 @@ describe('operation tracking', () => { duration: operation.durationTillInteractive || operation.duration, detail: { [SKIP_PROCESSING]: true, - metadata: operation.metadata, + attributes: operation.attributes, id: operation.id, name: operation.name, events: operation.getEvents().map((event) => ({ @@ -780,7 +779,7 @@ describe('operation tracking', () => { requiredToStart: true, }, { - match: { metadata: { ticketId: id } }, + match: { attributes: { ticketId: id } }, debounceEndWhenSeen: { debounceBy }, }, { @@ -810,7 +809,7 @@ describe('operation tracking', () => { name: `ticket-${state}`, startTime: time, duration: 0, - metadata: { ticketId: id }, + attributes: { ticketId: id }, }, ]) jest.advanceTimersByTime(Math.max(0, time - getTimeNow())) @@ -846,29 +845,29 @@ describe('operation tracking', () => { }) it(`correctly captures the operation's duration`, () => { - const id = '13024' + const ticketId = '13024' const operationName = 'ticket.activation' const lastEventRelativeEndTime = 1_706.599_999_999_627_5 const operationDefinition: OperationDefinition = { operationName, interruptSelf: true, - metadata: { ticketId: id }, + attributes: { ticketId }, waitUntilInteractive: true, startTime: ticketActivationFixtureData.startTime, timeout: 60_000, track: [ - // debounce on anything that has ticketId metadata: - { match: { metadata: { ticketId: id } } }, + // debounce on anything that has ticketId attributes: + { match: { attributes: { ticketId } } }, // any resource fetch should debounce the end of the operation: { match: { type: 'resource' } }, // debounce on element timing sentinels: - { match: { type: 'element', name: `ticket_workspace/${id}` } }, - { match: { type: 'element', name: `omnilog/${id}` } }, + { match: { type: 'element', name: `ticket_workspace/${ticketId}` } }, + { match: { type: 'element', name: `omnilog/${ticketId}` } }, // OmniLog must be rendered in 'complete' state to end the operation: { match: { name: 'OmniLog', - metadata: { ticketId: id, visibleState: 'complete' }, + attributes: { ticketId, visibleState: 'complete' }, }, requiredToEnd: true, }, diff --git a/src/v2/operation.ts b/src/v2/operation.ts index 4827d0e..3f64095 100644 --- a/src/v2/operation.ts +++ b/src/v2/operation.ts @@ -40,6 +40,9 @@ type TimeoutRef = ReturnType /** * Class representing an operation. + * An operation always always starts when the user interacts with the page (e.g. onClick of a button). + * TODO: Maybe it's named UserOperation to make it clear that it's user-initiated? + * TODO: maybe have a Task/Process class with 'entryType' = 'task' */ export class Operation implements PerformanceEntryLike { /** @@ -80,9 +83,9 @@ export class Operation implements PerformanceEntryLike { readonly entryType = 'operation' /** - * Metadata for the operation. + * Attributes (metadata) of the operation. */ - metadata: Record + attributes: Record private readonly events: Event[] = [] getEvents() { @@ -147,7 +150,7 @@ export class Operation implements PerformanceEntryLike { this.name = definition.operationName this.duration = 0 this.durationTillInteractive = 0 - this.metadata = definition.metadata ?? {} + this.attributes = definition.attributes ?? {} const captureInteractive = definition.waitUntilInteractive && manager.expectBlockingTasks ? typeof definition.waitUntilInteractive === 'object' @@ -267,6 +270,7 @@ export class Operation implements PerformanceEntryLike { if (this.state === 'initial' && trackerDefinition.requiredToStart) { this.requiredToStartTrackers.delete(trackerIndex) } + // TODO: should be be removing from 'requiredToEnd' list even when operation is not started yet? i.e. when this.state === 'initial' ? if (this.state === 'started') { if (trackerDefinition.requiredToEnd) { this.requiredToEndTrackers.delete(trackerIndex) @@ -543,7 +547,7 @@ export class Operation implements PerformanceEntryLike { * Finalizes the operation. * @param reason - The reason for finalizing the operation. */ - private finalizeOperation(reason: FinalizationReason, endTime: number): void { + finalizeOperation(reason: FinalizationReason, endTime: number): void { if (this.state === 'started') { const captureInteractive = reason === 'completed' && this.definition.captureInteractive @@ -576,14 +580,14 @@ export class Operation implements PerformanceEntryLike { // waiting for long events for the duration of the interactive debounce: this.maybeFinalizeLater(captureInteractive.debounceLongTasksBy, endTime) - } else { - this.finalize() + return } } else if (this.state === 'waiting-for-interactive') { this.state = reason this.durationTillInteractive = endTime - this.startTime - this.finalize() } + + this.finalize() } private finalize() { @@ -620,7 +624,7 @@ export class Operation implements PerformanceEntryLike { name: this.name, startTime: this.startTime, duration: this.duration, - metadata: { ...this.metadata }, + attributes: { ...this.attributes }, event: { commonName: this.name, kind: OPERATION_ENTRY_TYPE, @@ -639,7 +643,7 @@ export class Operation implements PerformanceEntryLike { name: this.name, startTime: this.startTime, duration: this.durationTillInteractive, - metadata: { ...this.metadata }, + attributes: { ...this.attributes }, event: { commonName: this.name, kind: OPERATION_INTERACTIVE_ENTRY_TYPE, @@ -663,7 +667,7 @@ export class Operation implements PerformanceEntryLike { if (typeof match === 'function') { return match(event) } - const { name, metadata, type } = match + const { name, attributes, type } = match const nameMatches = !name || (typeof name === 'string' @@ -672,15 +676,15 @@ export class Operation implements PerformanceEntryLike { ? name(event.name) : name.test(event.name)) const typeMatches = !type || event.entryType === type - const metadataMatches = - !metadata || + const attributeMatches = + !attributes || Boolean( - event.metadata && - Object.entries(metadata).every( - ([key, value]) => event.metadata?.[key] === value, + event.attributes && + Object.entries(attributes).every( + ([key, value]) => event.attributes?.[key] === value, ), ) - return nameMatches && typeMatches && metadataMatches + return nameMatches && typeMatches && attributeMatches } } @@ -710,7 +714,7 @@ export class OperationManager { /** * The function to preprocess a PerformanceEntry-like object into an Event object. * The resulting Event should have an 'operations' property record, - * which will be updated by the manager with metadata related to the operations that have processed it. + * which will be updated by the manager with attributes related to the operations that have processed it. * This will happen synchronously when running in unbuffered mode, and asynchronously when running in buffered mode. */ private preprocessEvent: EventProcessor @@ -786,7 +790,9 @@ export class OperationManager { this.operations.delete(operation.id) this.ensureObserver() operationDefinition.onDispose?.() - // TODO: post-process operation data - e.g. send a report or spans + if (operationDefinition.autoRestart) { + this.startOperation(operationDefinition) + } } const operation = new Operation({ @@ -801,6 +807,13 @@ export class OperationManager { return operation.id } + cancelOperation(operationName: string): void { + const operation = this.operations.get(operationName) + if (operation) { + operation.finalizeOperation('interrupted', this.performance.now()) + } + } + scheduleBufferFlushIfNeeded() { if ( !this.bufferDuration || @@ -826,7 +839,7 @@ export class OperationManager { } /** - * Processes a performance entry event. + * Ingests a performance entry event by either processing it immediately (sync) or later (async). * @param entry - The performance entry event to track. */ scheduleEventProcessing( @@ -855,11 +868,13 @@ export class OperationManager { return this.processEvent(event) } + // TODO: should this return a list of Events? or a list of operations that processed the event? private processEvent(event: Event): readonly Event[] { const processedEvents: Event[] = [] for (const operation of this.operations.values()) { const processedEvent = operation.processEvent(event) if (processedEvent) { + // TODO: add configurable setOperation(event, operation) // eslint-disable-next-line no-param-reassign event.operations[operation.name] = { id: operation.id, diff --git a/src/v2/types.ts b/src/v2/types.ts index 327bad1..c63028a 100644 --- a/src/v2/types.ts +++ b/src/v2/types.ts @@ -42,9 +42,9 @@ export interface EventMatchCriteria { name?: string | RegExp | ((name: string) => boolean) /** - * Metadata to match against the performance entry. + * Attributes (metadata) to match against the performance entry. */ - metadata?: Metadata + attributes?: Attributes /** * The type of the performance entry to match. @@ -126,9 +126,9 @@ export interface OperationDefinition { }[] /** - * Metadata for the operation. + * Attributes (metadata) for the operation. */ - metadata?: Metadata + attributes?: Attributes /** * Timeout for the operation in ms, after which it should be finalized. @@ -177,6 +177,12 @@ export interface OperationDefinition { * Note that when running in buffered mode, this will execute only after the buffer is flushed. */ onDispose?: () => void + + /** + * Indicates if the operation should be restarted automatically after it ends. + * This means that onEnd and onDispose may be called multiple times. + */ + autoRestart?: boolean } export interface EventMetadata extends Partial { @@ -196,7 +202,7 @@ export interface EventMetadata extends Partial { export type VisibleStates = (typeof VISIBLE_STATE)[keyof typeof VISIBLE_STATE] -export interface Metadata { +export interface Attributes { resource?: { type?: | 'document' @@ -240,14 +246,14 @@ export type EventEntryType = export interface InputEvent extends Omit { readonly entryType: EventEntryType operations?: Record - metadata?: Metadata + attributes?: Attributes event?: EventMetadata } export interface Event extends Omit { readonly entryType: EventEntryType readonly operations: Record - readonly metadata: Metadata + readonly attributes: Attributes readonly event: EventMetadata } @@ -260,11 +266,6 @@ export interface EventOperationRelation { */ id: string - /** - * The name of the operation the event belongs to. - */ - // name: string - /** * Internal order of the event within the operation. */