blob: ab54e54b61040e153a4e63eef29e1823a1b35ccb [file] [log] [blame] [view]
Danil Somsikovacf184f2023-11-20 15:17:081# Visual logging
2
3The goal of this project is to improve the logging of user interactions in
Kateryna Prokopenko94742f62023-12-18 09:21:494DevTools. The current UMA logging is unreliable and inconsistent. This can lead to
Danil Somsikovacf184f2023-11-20 15:17:085incorrect conclusions about how users are interacting with the product.
6
7We want to be able to understand how users are interacting with DevTools so that
8we can improve the product. This includes understanding what users are seeing,
9what they are interacting with, and how they are using different features.
10
Benedikt Meurer71006b72023-12-18 08:05:5011To turn on the logging, you need to pass `--enable-features=DevToolsVeLogging`
12as command line flag to Chrome.
13
Danil Somsikovacf184f2023-11-20 15:17:0814## General approach
15
16We log impressions and interactions for a subtree of the actual DevTools
17DOM tree. The logging is based on an HTML attribute and an arbitrary context
18value. The context value provides more information about the
19element that is being logged.
20
21The trickiest questions are what elements to log and how to describe them.
22There are no hard rules here, we log what we think is helpful to understand user
23behavior. Here are several rules of thumb:
24
251. Most of the interactive elements should be logged.
261. Elements that only appear under certain conditions should be logged.
271. Container elements that help distinguish common UI elements should be logged.
28
29We describe loggable elements as a combination of visual element type and
30context value. Visual element type is a value of `VisualElements` enum defined
31in `front_end/ui/visual_logging/LoggingConfig.ts`. Context could be an arbitrary
32string, its 32 bit hash is logged. No taxonomy is perfect and we are not
33trying to create one here, we are trying to make it practical though.
34
351. Visual element types try to describe function, not appearance. For instance,
36we have `Toggle` and `Action` as visual elements types, not `Checkbox` and
37`Button`. This is important for keeping user actions trackable across longer time
38frames, even if the appearance of UI elements change.
391. We also try to have visual element types describe classes of elements, and
40context describe a specific instance. For example, we log the Elements panel as
41a `Panel` visual element with `elements` as a context, not as `ElementsPanel`.
42This not only helps to keep the number of visual element types reasonable, but
43also makes it easier to compare relative performance of similar UI elements.
441. We log visual elements as a tree, so it is redundant to include location
45information in either visual element type or context. For example, `TreeOutline`
46inside `styles` `Pane` inside `elements` `Panel` is unambiguous by itself,
47without any extra qualifiers.
48
49## API
50
51In most cases, it is enough to put the `jslog` attribute on a corresponding HTML
52element. Theres a number of fluent builder functions exported
53`front_end/ui/visual_logging/visual_logging.ts` to build the attribute value.
54These are all bound versions of `LoggingConfig.makeConfigStringBuilder` and are
55used in the legacy UI as:
56
57```
Wolfgang Beyer6d0bdde2024-01-31 10:49:0958this.element.setAttribute('jslog', `${VisualLogging.panel(context)}`);
Danil Somsikovacf184f2023-11-20 15:17:0859```
60
61or
62
63```
Wolfgang Beyer6d0bdde2024-01-31 10:49:0964button.element.setAttribute('jslog', `${VisualLogging.dropDown('rendering-emulations')
65 .track({click: true})}`);
Danil Somsikovacf184f2023-11-20 15:17:0866```
67
68In LitHTML, the usage is:
69
70```
Wolfgang Beyer6d0bdde2024-01-31 10:49:0971Lit.html`<td jslog=${VisualLogging.tableCell(/* context */ col.id)
72 .track({click: true})}>
Danil Somsikovacf184f2023-11-20 15:17:0873```
74
75### `jslog` Builder API
76
77The `track()` method generates a `track:` clause and specifies exactly what needs to
78be logged for the visual elements. If not invoked, only impressions are logged for
79this element. Called with tracking options, an object with the following boolean properties:
80* `click`: Whether to track clicks.
81* `dblclick`: Whether to track double clicks.
82* `hover`: Whether to track hover events.
83* `drag`: Whether to track drag events.
84* `change`: Whether to track change events.
85* `keydown`: Whether to track keydown events. This property can be boolean or string.
86If a string is provided, it will be used as the key code to track. Otherwise, all keydown
87events will be tracked.
88
Wolfgang Beyer6d0bdde2024-01-31 10:49:0989The builder function accepts a `context` parameter, which sets the context for the visual
90logging element. The context can be a string or a number. If a string is given, it is be
91first considered to refer to a context provider (see below). If no context provider is
92registered with this name, SHA-1 hash is computed and the first 32 bits
Kateryna Prokopenko94742f62023-12-18 09:21:4993(little endian) is logged. Number will be logged as is.
Danil Somsikovacf184f2023-11-20 15:17:0894
95The `parent()` method sets the custom parent provider for the visual logging element
96(see below). If not invoked, the parent visual element is taken from a DOM tree structure.
97
98### Context and parent providers
99
100Context providers are used to generate context value in a runtime. It is used
101for both impressions and events. This is useful when relevant information is not
102reflected in the DOM, for example when a pseudo-element is used to render a tree item
103disclosure triangle or canvas is used to draw network waterfall. When logging
104impressions, context may indicate if or how many elements are present (0 or 1 for
105having a disclosure triangle or not; number of tracks in the waterfall). When
106logging events, context could identify what was clicked (1 for disclosure
107triangle, 0 for tree item itself, sequence number of a waterfall track).
108
109As this only logs a single number, it's not enough for more complex cases,
110like diverse tracks in the Performance panel canvas, or hosted menu. For these
111scenarios, see the section below.
112
113To register a context provider, call `VisualLogging.registerContextProvider`.
114First argument is a provider name that is later used as an argument in the
115`jslog` builder `context()` method. Second is a function that takes an Element or Event
116and returns a number. For a disclosure triangle, this is as follows:
117
118```
119function disclosureTriangleLoggingContextProvider(
120 e: VisualLogging.Loggable|Event): Promise<number|undefined> {
121 if (e instanceof Element) {
122 return Promise.resolve(e.classList.contains('parent') ? 1 : 0);
123 }
124 if (e instanceof MouseEvent && e.currentTarget instanceof Node) {
125 const treeElement = TreeElement.getTreeElementBylistItemNode(e.currentTarget);
126 if (treeElement) {
127 return Promise.resolve(treeElement.isEventWithinDisclosureTriangle(e) ? 1 : 0);
128 }
129 }
130 return Promise.resolve(undefined);
131}
132
133
134VisualLogging.registerContextProvider('disclosureTriangle',
135 disclosureTriangleLoggingContextProvider);
136
137listItemNode.setAttribute('jslog', `${VisualLogging.treeItem()
138 .track({click: true}).context('disclosureTriangle')}`);
139```
140
141Similarly parent provides are used to specify parent visual elements in
142runtime. This should be used rarely because, most of the time, DOM hierarchy is enough
143to identify the parent. However, sometimes, markup doesn’t reflect the logical
144structure, for example, when a legacy tree outline has children in an `<ol>` element, which is a
145sibling of `<li>` that specifies the parent. In this case, you can do the following:
146
147```
148function loggingParentProvider(e: Element): Element|undefined {
149 const treeElement = TreeElement.getTreeElementBylistItemNode(e);
150 return treeElement?.parent?.listItemElement;
151}
152
153VisualLogging.registerParentProvider('parentTreeItem',
154 loggingParentProvider);
155
156this.listItemNode.setAttribute(
157 'jslog',
158 `${VisualLogging.treeItem().track({click: true}).parent('parentTreeItem')}`);
159```
160
161### Logging beyond DOM
162
163Some DevTools UI is not represented in DOM, such as tracks in the Performance
164panel or native menus. To log these, the visual logging library provides an
165imperative API. Use it rarely, when no other options are
166available because it requires manual orchestration and is subject to the same issues
167as UMA histogram logging.
168
169First, identify the TypeScript type that corresponds/ to the element that
170needs to be logged. For example, `ContextMenuDescriptor` is used to log native
171menu items. This type needs to be added to the `Loggable` type definition in
172`front_end/ui/visual_logging/Loggable.ts`.
173
174
175Then call `registerLoggable` with the corresponding JavaScript
176object, config string in the same format as the `jslog` attribute would have,
177and an optional parent JavaScript object. For a native menu item, this is:
178
179
180```
181VisualLogging.registerLoggable(descriptor, `${VisualLogging.action()
182 .track({click: true}).context(descriptor.jslogContext)}`,
183 parent || descriptors);
184```
185
186This only registers the element and doesn’t log anything yet. To log
187impressions, explicitly call `VisualLogging.logImpressions`.
188Similarly to log click, call `VisualLogging.logClick`.
189
Danil Somsikova05fa1c2023-11-21 11:02:34190## Debugging
191
192You may find it useful to see which UI elements are annotated and how the tree
193structure look like. To do that, call `setVeDebuggingEnabled(true)` in DevTools
194on DevTools. This will add red outline to each visual element and will show the
195details of logging config for an element and all its ancestors on hover.
Benedikt Meurer71006b72023-12-18 08:05:50196
197**Note:** This will only work if you invoked Chrome with the command line flag
198`--enable-features=DevToolsVeLogging`. Otherwise you won't see any red lines.
Jack Franklin8c5a6392024-06-07 10:38:03199
200You can also run `setVeDebugLoggingEnabled(true)` in DevTools on DevTools. This
201will cause each VE log to be also logged to the DevTools on DevTools console.
202They will also be stored in the global variable `veDebugEventsLog`.