This document covers the best practices on using user actions in code and documenting them for the dashboard. User actions come with only a name and a timestamp. They are best used when you care about a sequence—which actions happen in what order. If you don't care about the order, you should be using histograms (likely enumerated histograms).
Often, you want both user actions and histogram logging in your code. They enable different analyses. They're complementary.
Google has policies restricting what data can be collected and for what purpose. Googlers, see go/uma-privacy#principles to verify your desired user action adheres to those policies.
Generally you should call base::RecordAction()
, which is defined in user_metrics.h.
Prefer to emit at the highest level reasonable, closest to the code that handles the UI interaction. Emitting deep in implementation code can cause problems because that code may get reused (and thus called more times in more places) or may get called fewer times (due to caching for example). In cases like this, the logged user action would no longer correspond to a meaningful action performed by the user.
Generally a logged user action should correspond to a single event. Thus, the logging should probably only appear in a single place in the code. If the same user action needs to be logged in multiple places, consider whether you should be using different user action names for these separate call paths.
In rare cases, the same user action can be recorded in multiple places as long as only one of the places can be reached. This may be necessary if the user action is logged in platform-specific code or if one implementation is being replaced with another. When recording an action in multiple places, use a compile-time constant of appropriate scope that can be referenced everywhere. Using inline strings in multiple places can lead to errors if you ever need to revise the name and you update one location but forget another.
Due to the practices about when and how often to emit a user action, actions should not be emitted often enough to cause efficiency issues. (If actions are emitted often enough to cause a problem, they're not being emitted at appropriate times. See advice below.)
A user action should be tied to a single event, such as a user doing something or a user seeing something new. Each meaningful unit should cause one emit. For example, showing the history page is a meaningful unit; querying the history database—which might need to be queried multiple times to fill the page—is probably not a meaningful unit for most use cases.
A meaningful user action should usually cause only one emit. This advice is mainly because user action sequences are easier to analyze without redudancy.
For example, if the browser already has a “Back” user action, avoid adding a “BackViaKeyboardShortcut” user action—it‘s mostly redundant—unless it’s necessary because you care about how the different types of Back button work in sequences of user actions. If you don't care about how BackViaKeyboardShortcut works in a sequence and only want to count them or determine the breakdown of keyboard-shortcut backs versus all backs, use a histogram.
Again, choose an appropriately-sized meaningful unit. For example, emit “DragScrolled” for a whole scroll action. Don't emit this action every time the user pauses scrolling if the user remains in the process of scrolling (mouse button still down). However, if you need to understand the sequence of partial scrolls, emitting this for each scroll pause is acceptable.
As another example, you may want to emit “FocusOmnibox” (upon focus), “OmniboxEditInProgress” (upon first keystroke), and “OmniboxUse” (upon going somewhere) but skip “OmniboxKeystroke”. That's probably more detailed than you need.
It‘s okay to emit user actions such as “ShowTranslateInfobar” or “DisplayedImageLinkContextMenu”. Remember to mark them as not_user_triggered and please try to make sure they’re not excessive. For example, don‘t emit “ShowedSecureIconNextToOmnibox” because that’s likely to appear on most pages. That said, if you need ShowedSecureIconNextToOmnibox logged in order to analyze a sequence of user actions that include it, go ahead.
Test your user actions using chrome://user-actions
. Make sure they're being emitted when you expect and not emitted otherwise.
If this is a general UI surface, please try to check every platform. In particular, check Windows (Views-based platforms), Android phone (yet other UI wrapper code), Android tablet (often triggers look-alike but different menus), and iOS (yet more different UI wrapper code).
Also, check that your new user action is not mostly redundant in light of existing user actions (see advice above) and not emitted excessively (see advice above).
In addition to testing interactively, unit tests can check the number of times a user action was emitted. See user_action_tester.h for details.
See also chrome://metrics-internals
(docs) for more thorough manual testing if needed.
If you have entries that need to be updated to match code, you can use ActionSuffixReader to read and verify the expected values in a unit test. This prevents a mismatch between code and action data from slipping through CQ.
For an example, see BrowserUserEducationServiceTest.CheckFeaturePromoActions.
The top of go/uma-guide has good advice on how to go about analyzing and interpreting the results of UMA data uploaded by users. If you‘re reading this page, you’ve probably just finished adding a user action to the Chromium source code and you‘re waiting for users to update their version of Chrome to a version that includes your code. In this case, the best advice is to remind you that users who update frequently or quickly are biased. Best take the initial statistics with a grain of salt; they’re probably mostly right but not entirely so.
When changing the semantics of a user action (when it's emitted), make it into a new user action with a new name. Otherwise the dashboard will mix two different interpretations of the data and make no sense.
Document user actions in actions.xml. There is also a Google-internal version of the file for user actions that exist in Google-internal codebases. Confidential actions are added only to Chrome code, not Chromium code.
If possible, please add the actions.xml description in the same changelist in which you add the user-action-emitting code. This has several benefits. One, it sometimes happens that the actions.xml reviewer has questions or concerns about the user action description that reveal problems with interpretation of the data and call for a different recording strategy. Two, it allows the user action reviewer to easily review the emission code to see if it comports with these best practices, and to look for other errors.
User action descriptions should be understandable to someone not familiar with your feature. Please add a sentence or two of background if necessary.
It's a good practice to note caveats associated with your user actions in this section, such as which platforms are supported (if the set of supported platforms is surprising, such as a desktop feature that happens to not be logged on Mac).
User action descriptions should clearly state when the action is emitted.
Each user action needs owners, who are the current expert on the metric. Owners are responsible for answering questions about the metric, handling any maintenance tasks, and deprecating the metric if it has outlived its usefulness. If you are using a metric heavily and understand it intimately, feel free to add yourself as an owner. @chromium.org email addresses are preferred.
The primary owner must be an individual, who is ultimately responsible for the metric. It‘s a best practice to list multiple owners, which makes it less likely that maintenance tasks will slip through the cracks. This is important because the metrics team may file bugs related to user actions, and such bugs need to be triaged by someone familiar with the metric. If an appropriate mailing list or team email is available, it’s a good idea to list it as a secondary owner.
not_user_triggered="true"
when appropriateactions.xml allows you to annotate an action as not_user_triggered="true"
. Use it when appropriate. For example, showing a notification is not user triggered. However, please remember: Before adding something marked as not_user_triggered="true"
, consider whether you need to analyze sequences of actions. If not, please use a histogram to count these events instead.
Do not delete actions from actions.xml. Instead, mark unused user actions as obsolete, annotating them with the associated date or milestone in the obsolete tag entry.
If the user action is being replaced by a new version:
Note in the <obsolete>
message the name of the replacement action.
Make sure the descriptions of the original and replacement user actions are different. It's never appropriate for them to be identical. Either the old description was wrong, and it should be revised to explain what it actually measured, or the old user action was measuring something not as useful as the replacement, in which case the new user action is measuring something different and needs to have a new description.
A changelist that marks a user action as obsolete should be reviewed by all current owners.
Deleting user action entries would be bad if someone accidentally reused your old user action name. If this happened, new data would be corrupted by whatever old data was still coming in. It‘s also useful to keep obsolete user action descriptions in actions.xml. That way, someone searching for a user action to answer a particular question can learn if there was a user action at some point that did so—even if it isn’t active now.