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
36 changes: 36 additions & 0 deletions javascript/sentry-conventions/src/op.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ export const UI_TASK = 'ui.task';
*/
export const UI_RESOLVE = 'ui.resolve';

/**
* Rendering of a UI component.
*/
export const UI_RENDER = 'ui.render';

/**
Expand Down Expand Up @@ -198,26 +201,59 @@ export const UI_WEBVITAL_LCP = 'ui.webvital.lcp';
*/
export const UI_WEBVITAL_CLS = 'ui.webvital.cls';

/**
* A user interaction that starts a new trace segment and collects the work it triggers.
*/
export const UI_ACTION = 'ui.action';

/**
* A click that starts a new idle span, collecting work the interaction triggers.
*/
export const UI_ACTION_CLICK = 'ui.action.click';

/**
* Legacy React UI operation. Prefer ui.mount, ui.render, and ui.update.
*/
export const UI_REACT = 'ui.react';

/**
* Legacy React component mount. Prefer ui.mount.
*/
export const UI_REACT_MOUNT = 'ui.react.mount';

/**
* Legacy React component render. Prefer ui.render.
*/
export const UI_REACT_RENDER = 'ui.react.render';

/**
* Legacy React component update. Prefer ui.update.
*/
export const UI_REACT_UPDATE = 'ui.react.update';

/**
* Legacy Vue UI operation. Prefer ui.mount, ui.render, ui.update, and ui.unmount.
*/
export const UI_VUE = 'ui.vue';

/**
* Legacy Svelte UI operation. Prefer ui.mount and ui.update.
*/
export const UI_SVELTE = 'ui.svelte';

/**
* Legacy Angular UI operation. Prefer ui.mount.
*/
export const UI_ANGULAR = 'ui.angular';

/**
* Legacy Ember UI operation. Prefer ui.mount, ui.render, ui.task, and ui.resolve.
*/
export const UI_EMBER = 'ui.ember';

/**
* Legacy Livewire UI operation. Prefer ui.mount, ui.render, and ui.update.
*/
export const UI_LIVEWIRE = 'ui.livewire';

// Path: model/op/database.json
Expand Down
53 changes: 53 additions & 0 deletions model/description/ui.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"brief": "UI",
"operations": [
{
"name": "UI component",
"brief": "Mounting, updating, rendering, unmounting, or resolving a UI component.",
"ops": ["ui.mount", "ui.update", "ui.render", "ui.unmount", "ui.resolve", "ui"],
"templates": ["{{ui.component_name}}"],
"examples": ["HomeButton", "UserProfile"]
},
{
"name": "Main UI thread blocked",
"brief": "A long task or long animation frame on the main UI thread.",
"ops": ["ui.long_task", "ui.long_animation_frame"],
"templates": ["{{code.function.name}}", "{{browser.script.invoker}}"],
"examples": ["handleClick", "Window.requestAnimationFrame"]
},
{
"name": "UI action",
"brief": "A user interaction that starts a new trace segment, described by the route it occurred on.",
"ops": ["ui.action", "ui.action.click"],
"templates": [
"{{router.navigation.route.id}}",
"{{url.template}}",
"{{http.route}}",
"{{url.path}}",
"{{url.full}}"
],
"examples": ["UserProfile", "/users/:id", "/users/123", "http://example.com/users/123"]
},
{
"name": "UI interaction",
"brief": "A user interaction measured via Interaction to Next Paint (INP) or the Event Timing API, described by the element the user interacted with.",
"ops": ["ui.interaction.click", "ui.interaction.hover", "ui.interaction.drag", "ui.interaction.press"],
"templates": ["{{browser.web_vital.inp.target}}", "{{ui.component_name}}"],
"examples": ["body > div#app > button.submit", "HomeButton"]
},
{
"name": "Largest contentful paint",
"brief": "A Largest Contentful Paint (LCP) web vital measurement.",
"ops": ["ui.webvital.lcp"],
"templates": ["{{browser.web_vital.lcp.element}}", "{{ui.component_name}}"],
"examples": ["body > div#app > div#container > div", "HeroImage"]
},
{
"name": "Layout shift",
"brief": "A Cumulative Layout Shift (CLS) web vital measurement, described by the first of its layout shift sources.",
"ops": ["ui.webvital.cls"],
"templates": ["{{browser.web_vital.cls.source.1}}", "{{ui.component_name}}"],
"examples": ["body > div#app", "HeroImage"]
}
]
}
133 changes: 133 additions & 0 deletions model/name/ui.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{
"brief": "UI",
"operations": [
{
"name": "Component mount",
"brief": "Mounting of a UI component or application (e.g. initial render/bootstrap).",
"is_in_otel": false,
"ops": ["ui.mount"],
"templates": ["{{ui.component_name}}", "Component mount"],
"examples": ["HomeButton", "Component mount"]
},
{
"name": "Component update",
"brief": "Updating of an already-mounted UI component.",
"is_in_otel": false,
"ops": ["ui.update"],
"templates": ["{{ui.component_name}}", "Component update"],
"examples": ["HomeButton", "Component update"]
},
{
"name": "Component render",
"brief": "Rendering of a UI component.",
"is_in_otel": false,
"ops": ["ui.render"],
"templates": ["{{ui.component_name}}", "Component render"],
"examples": ["HomeButton", "Component render"]
},
{
"name": "Component unmount",
"brief": "Unmounting/teardown of a UI component.",
"is_in_otel": false,
"ops": ["ui.unmount"],
"templates": ["{{ui.component_name}}", "Component unmount"],
"examples": ["HomeButton", "Component unmount"]
},
{
"name": "Component resolve",
"brief": "Looking up which component a name in a template refers to, before that component renders.",
"is_in_otel": false,
"ops": ["ui.resolve"],
"templates": ["{{ui.component_name}}", "Component resolve"],
"examples": ["HomeButton", "Component resolve"]
},
{
"name": "UI task",
"brief": "A task taken on the main UI thread, such as an Ember runloop queue.",
"is_in_otel": false,
"ops": ["ui.task"],
"templates": ["UI task"],
"examples": ["UI task"]
},
{
"name": "Main UI thread blocked",
"brief": "A long task or long animation frame on the main UI thread.",
"is_in_otel": false,
"ops": ["ui.long_task", "ui.long_animation_frame"],
"templates": ["Main UI thread blocked"],
"examples": ["Main UI thread blocked"]
},
{
"name": "UI action",
"brief": "A user interaction that starts a new trace segment and collects the work it triggers.",
"is_in_otel": false,
"ops": ["ui.action"],
"templates": ["{{router.navigation.route.id}}", "{{url.template}}", "{{http.route}}", "UI action"],
"examples": ["UserProfile", "/users/:id", "/home", "UI action"]
},
{
"name": "Click action",
"brief": "A click that starts a new idle span, named after the current route.",
"is_in_otel": false,
"ops": ["ui.action.click"],
"templates": ["{{router.navigation.route.id}}", "{{url.template}}", "{{http.route}}", "Click"],
"examples": ["UserProfile", "/users/:id", "/home", "Click"]
},
{
"name": "Click interaction",
"brief": "A click interaction measured via Interaction to Next Paint (INP) or the Event Timing API.",
"is_in_otel": false,
"ops": ["ui.interaction.click"],
"templates": ["{{ui.component_name}}", "Click"],
"examples": ["HomeButton", "Click"]
},
{
"name": "Hover interaction",
"brief": "A hover interaction measured via Interaction to Next Paint (INP).",
"is_in_otel": false,
"ops": ["ui.interaction.hover"],
"templates": ["{{ui.component_name}}", "Hover"],
"examples": ["HomeButton", "Hover"]
},
{
"name": "Drag interaction",
"brief": "A drag interaction measured via Interaction to Next Paint (INP).",
"is_in_otel": false,
"ops": ["ui.interaction.drag"],
"templates": ["{{ui.component_name}}", "Drag"],
"examples": ["HomeButton", "Drag"]
},
{
"name": "Key press interaction",
"brief": "A key press interaction measured via Interaction to Next Paint (INP).",
"is_in_otel": false,
"ops": ["ui.interaction.press"],
"templates": ["{{ui.component_name}}", "Key press"],
"examples": ["SearchInput", "Key press"]
},
{
"name": "Largest contentful paint",
"brief": "A Largest Contentful Paint (LCP) web vital measurement.",
"is_in_otel": false,
"ops": ["ui.webvital.lcp"],
"templates": ["{{ui.component_name}}", "Largest contentful paint"],
"examples": ["HeroImage", "Largest contentful paint"]
},
{
"name": "Layout shift",
"brief": "A Cumulative Layout Shift (CLS) web vital measurement.",
"is_in_otel": false,
"ops": ["ui.webvital.cls"],
"templates": ["{{ui.component_name}}", "Layout shift"],
"examples": ["HeroImage", "Layout shift"]
},
{
"name": "UI",
"brief": "A generic UI operation. Prefer the more specific ui.mount, ui.render, ui.update, and ui.unmount ops.",
"is_in_otel": false,
"ops": ["ui"],
"templates": ["{{ui.component_name}}", "UI"],
"examples": ["HomeButton", "UI"]
}
]
}
36 changes: 24 additions & 12 deletions model/op/browser.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@
"description": "Looking up which component a name in a template refers to, before that component renders. Ember does this for every component it renders. The render itself is a separate `ui.render` span."
},
{
"name": "ui.render"
"name": "ui.render",
"description": "Rendering of a UI component."
},
{
"name": "ui.mount",
Expand Down Expand Up @@ -173,37 +174,48 @@
"description": "A Cumulative Layout Shift (CLS) web vital measurement."
},
{
"name": "ui.action"
"name": "ui.action",
"description": "A user interaction that starts a new trace segment and collects the work it triggers."
},
{
"name": "ui.action.click"
"name": "ui.action.click",
"description": "A click that starts a new idle span, collecting work the interaction triggers."
},
{
"name": "ui.react"
"name": "ui.react",
"description": "Legacy React UI operation. Prefer ui.mount, ui.render, and ui.update."
},
{
"name": "ui.react.mount"
"name": "ui.react.mount",
"description": "Legacy React component mount. Prefer ui.mount."
},
{
"name": "ui.react.render"
"name": "ui.react.render",
"description": "Legacy React component render. Prefer ui.render."
},
{
"name": "ui.react.update"
"name": "ui.react.update",
"description": "Legacy React component update. Prefer ui.update."
},
{
"name": "ui.vue"
"name": "ui.vue",
"description": "Legacy Vue UI operation. Prefer ui.mount, ui.render, ui.update, and ui.unmount."
},
{
"name": "ui.svelte"
"name": "ui.svelte",
"description": "Legacy Svelte UI operation. Prefer ui.mount and ui.update."
},
{
"name": "ui.angular"
"name": "ui.angular",
"description": "Legacy Angular UI operation. Prefer ui.mount."
},
{
"name": "ui.ember"
"name": "ui.ember",
"description": "Legacy Ember UI operation. Prefer ui.mount, ui.render, ui.task, and ui.resolve."
},
{
"name": "ui.livewire"
"name": "ui.livewire",
"description": "Legacy Livewire UI operation. Prefer ui.mount, ui.render, and ui.update."
}
]
}
3 changes: 2 additions & 1 deletion model/op/mobile.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"description": "The time it takes to download an over-the-air (OTA) update."
},
{
"name": "ui"
"name": "ui",
"description": "Operations related to mobile UI."
},
{
"name": "navigation"
Expand Down
12 changes: 12 additions & 0 deletions rust/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub const UI_TASK: &str = "ui.task";
/// Looking up which component a name in a template refers to, before that component renders. Ember does this for every component it renders. The render itself is a separate `ui.render` span.
pub const UI_RESOLVE: &str = "ui.resolve";

/// Rendering of a UI component.
pub const UI_RENDER: &str = "ui.render";

/// Mounting of a UI component or application (e.g. initial render/bootstrap).
Expand Down Expand Up @@ -128,26 +129,37 @@ pub const UI_WEBVITAL_LCP: &str = "ui.webvital.lcp";
/// A Cumulative Layout Shift (CLS) web vital measurement.
pub const UI_WEBVITAL_CLS: &str = "ui.webvital.cls";

/// A user interaction that starts a new trace segment and collects the work it triggers.
pub const UI_ACTION: &str = "ui.action";

/// A click that starts a new idle span, collecting work the interaction triggers.
pub const UI_ACTION_CLICK: &str = "ui.action.click";

/// Legacy React UI operation. Prefer ui.mount, ui.render, and ui.update.
pub const UI_REACT: &str = "ui.react";

/// Legacy React component mount. Prefer ui.mount.
pub const UI_REACT_MOUNT: &str = "ui.react.mount";

/// Legacy React component render. Prefer ui.render.
pub const UI_REACT_RENDER: &str = "ui.react.render";

/// Legacy React component update. Prefer ui.update.
pub const UI_REACT_UPDATE: &str = "ui.react.update";

/// Legacy Vue UI operation. Prefer ui.mount, ui.render, ui.update, and ui.unmount.
pub const UI_VUE: &str = "ui.vue";

/// Legacy Svelte UI operation. Prefer ui.mount and ui.update.
pub const UI_SVELTE: &str = "ui.svelte";

/// Legacy Angular UI operation. Prefer ui.mount.
pub const UI_ANGULAR: &str = "ui.angular";

/// Legacy Ember UI operation. Prefer ui.mount, ui.render, ui.task, and ui.resolve.
pub const UI_EMBER: &str = "ui.ember";

/// Legacy Livewire UI operation. Prefer ui.mount, ui.render, and ui.update.
pub const UI_LIVEWIRE: &str = "ui.livewire";

// Path: model/op/database.json
Expand Down
11 changes: 11 additions & 0 deletions scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,14 @@ export function fileNameToAttributeKey(fileName: string): string {
.replaceAll('__', '.')
.replace('[key]', '<key>');
}

/**
* Returns the dynamic-suffix key that a concrete attribute key could be an instance of,
* e.g. `browser.web_vital.cls.source.1` -> `browser.web_vital.cls.source.<key>`.
*
* Returns undefined for keys without a separator, which cannot carry a suffix.
*/
export function attributeKeyToDynamicSuffixKey(key: string): string | undefined {
const lastSeparator = key.lastIndexOf('.');
return lastSeparator === -1 ? undefined : `${key.slice(0, lastSeparator)}.<key>`;
}
Loading
Loading