123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/ntp_snippets/content_suggestions_metrics.h"
- #include <cmath>
- #include <string>
- #include <type_traits>
- #include "base/metrics/histogram.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/user_metrics.h"
- #include "base/strings/stringprintf.h"
- namespace ntp_snippets {
- namespace metrics {
- namespace {
- // Keep in sync with MAX_SUGGESTIONS_PER_SECTION in NewTabPageUma.java.
- const int kMaxSuggestionsPerCategory = 20;
- const int kMaxSuggestionsTotal = 50;
- const int kMaxCategories = 10;
- const char kHistogramCountOnNtpOpenedIfVisible[] =
- "NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible";
- const char kHistogramSectionCountOnNtpOpened[] =
- "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened";
- const char kHistogramShown[] = "NewTabPage.ContentSuggestions.Shown";
- const char kHistogramShownAge[] = "NewTabPage.ContentSuggestions.ShownAge";
- const char kHistogramShownScore[] =
- "NewTabPage.ContentSuggestions.ShownScoreNormalized";
- const char kHistogramOpened[] = "NewTabPage.ContentSuggestions.Opened";
- const char kHistogramOpenedAge[] = "NewTabPage.ContentSuggestions.OpenedAge";
- const char kHistogramOpenedCategoryIndex[] =
- "NewTabPage.ContentSuggestions.OpenedCategoryIndex";
- const char kHistogramOpenedScore[] =
- "NewTabPage.ContentSuggestions.OpenedScoreNormalized";
- const char kHistogramOpenDisposition[] =
- "NewTabPage.ContentSuggestions.OpenDisposition";
- const char kHistogramMenuOpened[] = "NewTabPage.ContentSuggestions.MenuOpened";
- const char kHistogramMenuOpenedAge[] =
- "NewTabPage.ContentSuggestions.MenuOpenedAge";
- const char kHistogramMenuOpenedScore[] =
- "NewTabPage.ContentSuggestions.MenuOpenedScoreNormalized";
- const char kHistogramDismissedUnvisited[] =
- "NewTabPage.ContentSuggestions.DismissedUnvisited";
- const char kHistogramDismissedVisited[] =
- "NewTabPage.ContentSuggestions.DismissedVisited";
- const char kHistogramArticlesUsageTimeLocal[] =
- "NewTabPage.ContentSuggestions.UsageTimeLocal";
- const char kHistogramVisitDuration[] =
- "NewTabPage.ContentSuggestions.VisitDuration";
- const char kHistogramMoreButtonShown[] =
- "NewTabPage.ContentSuggestions.MoreButtonShown";
- const char kHistogramMoreButtonClicked[] =
- "NewTabPage.ContentSuggestions.MoreButtonClicked";
- const char kHistogramMovedUpCategoryNewIndex[] =
- "NewTabPage.ContentSuggestions.MovedUpCategoryNewIndex";
- const char kHistogramCategoryDismissed[] =
- "NewTabPage.ContentSuggestions.CategoryDismissed";
- const char kHistogramTimeSinceSuggestionFetched[] =
- "NewTabPage.ContentSuggestions.TimeSinceSuggestionFetched";
- // Histograms related to prefetching.
- const char kHistogramPrefetchedArticleOpenedWhenOffline[] =
- "NewTabPage.ContentSuggestions.Opened.Articles.Prefetched.Offline";
- // NewTabPage.ContentSuggestions.CountOnNtpOpenedIfVisible.Articles.\
- // Prefetched.Offline2 and
- // NewTabPage.ContentSuggestions.Shown.Articles.Prefetched.Offline2 are recorded
- // in Java to avoid race condition.
- const char kPerCategoryHistogramFormat[] = "%s.%s";
- // This mostly corresponds to the KnownCategories enum, but it is contiguous
- // and contains exactly the values to be recorded in UMA. Don't remove or
- // reorder elements, only add new ones at the end (before COUNT), and keep in
- // sync with ContentSuggestionsCategory in histograms.xml.
- enum class HistogramCategories {
- EXPERIMENTAL,
- RECENT_TABS_DEPRECATED,
- DOWNLOADS_DEPRECATED,
- BOOKMARKS_DEPRECATED,
- PHYSICAL_WEB_PAGES_DEPRECATED,
- FOREIGN_TABS_DEPRECATED,
- ARTICLES,
- READING_LIST,
- CONTEXTUAL,
- // Insert new values here!
- COUNT
- };
- HistogramCategories GetHistogramCategory(Category category) {
- static_assert(
- std::is_same<decltype(category.id()),
- typename std::underlying_type<KnownCategories>::type>::value,
- "KnownCategories must have the same underlying type as category.id()");
- // Note: Since the underlying type of KnownCategories is int, it's legal to
- // cast from int to KnownCategories, even if the given value isn't listed in
- // the enumeration. The switch still makes sure that all known values are
- // listed here.
- auto known_category = static_cast<KnownCategories>(category.id());
- switch (known_category) {
- case KnownCategories::ARTICLES:
- return HistogramCategories::ARTICLES;
- case KnownCategories::READING_LIST:
- return HistogramCategories::READING_LIST;
- case KnownCategories::BOOKMARKS_DEPRECATED:
- return HistogramCategories::BOOKMARKS_DEPRECATED;
- case KnownCategories::DOWNLOADS_DEPRECATED:
- return HistogramCategories::DOWNLOADS_DEPRECATED;
- case KnownCategories::FOREIGN_TABS_DEPRECATED:
- return HistogramCategories::FOREIGN_TABS_DEPRECATED;
- case KnownCategories::RECENT_TABS_DEPRECATED:
- case KnownCategories::PHYSICAL_WEB_PAGES_DEPRECATED:
- case KnownCategories::LOCAL_CATEGORIES_COUNT:
- case KnownCategories::REMOTE_CATEGORIES_OFFSET:
- NOTREACHED();
- return HistogramCategories::COUNT;
- }
- // All other (unknown) categories go into a single "Experimental" bucket.
- return HistogramCategories::EXPERIMENTAL;
- }
- // Each suffix here should correspond to an entry under histogram suffix
- // ContentSuggestionCategory in histograms.xml.
- std::string GetCategorySuffix(Category category) {
- HistogramCategories histogram_category = GetHistogramCategory(category);
- switch (histogram_category) {
- case HistogramCategories::ARTICLES:
- return "Articles";
- case HistogramCategories::EXPERIMENTAL:
- return "Experimental";
- case HistogramCategories::READING_LIST:
- return "ReadingList";
- case HistogramCategories::CONTEXTUAL:
- return "Contextual";
- case HistogramCategories::BOOKMARKS_DEPRECATED:
- case HistogramCategories::DOWNLOADS_DEPRECATED:
- case HistogramCategories::FOREIGN_TABS_DEPRECATED:
- case HistogramCategories::RECENT_TABS_DEPRECATED:
- case HistogramCategories::PHYSICAL_WEB_PAGES_DEPRECATED:
- case HistogramCategories::COUNT:
- NOTREACHED();
- break;
- }
- return std::string();
- }
- std::string GetCategoryHistogramName(const char* base_name, Category category) {
- return base::StringPrintf(kPerCategoryHistogramFormat, base_name,
- GetCategorySuffix(category).c_str());
- }
- // This corresponds to UMA_HISTOGRAM_CUSTOM_TIMES (with min/max appropriate
- // for the age of suggestions) for use with dynamic histogram names.
- void UmaHistogramAge(const std::string& name, const base::TimeDelta& value) {
- base::Histogram::FactoryTimeGet(
- name, base::Seconds(1), base::Days(7), 100,
- base::HistogramBase::kUmaTargetedHistogramFlag)
- ->AddTime(value);
- }
- void LogCategoryHistogramPosition(const char* base_name,
- Category category,
- int position,
- int max_position) {
- std::string name = GetCategoryHistogramName(base_name, category);
- // Since the histogram name is dynamic, we can't use the regular macro.
- base::UmaHistogramExactLinear(name, position, max_position);
- }
- void LogCategoryHistogramAge(const char* base_name,
- Category category,
- const base::TimeDelta& value) {
- std::string name = GetCategoryHistogramName(base_name, category);
- // Since the histogram name is dynamic, we can't use the regular macro.
- UmaHistogramAge(name, value);
- }
- void LogCategoryHistogramScore(const char* base_name,
- Category category,
- float score) {
- std::string name = GetCategoryHistogramName(base_name, category);
- // Scores are typically reported in a range of (0,1]. As UMA does not support
- // floats, we put them on a discrete scale of [1,10]. We keep the extra bucket
- // 11 for unexpected over-flows as we want to distinguish them from scores
- // close to 1. For instance, the discrete value 1 represents score values
- // within (0.0, 0.1].
- base::UmaHistogramExactLinear(name, ceil(score * 10), 11);
- }
- // Records ContentSuggestions usage. Therefore the day is sliced into 20min
- // buckets. Depending on the current local time the count of the corresponding
- // bucket is increased.
- void RecordContentSuggestionsUsage() {
- const int kBucketSizeMins = 20;
- const int kNumBuckets = 24 * 60 / kBucketSizeMins;
- base::Time::Exploded now_exploded;
- base::Time::Now().LocalExplode(&now_exploded);
- int bucket = (now_exploded.hour * 60 + now_exploded.minute) / kBucketSizeMins;
- const char* kWeekdayNames[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"};
- std::string histogram_name(
- base::StringPrintf("%s.%s", kHistogramArticlesUsageTimeLocal,
- kWeekdayNames[now_exploded.day_of_week]));
- base::UmaHistogramExactLinear(histogram_name, bucket, kNumBuckets);
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramArticlesUsageTimeLocal, bucket,
- kNumBuckets);
- base::RecordAction(
- base::UserMetricsAction("NewTabPage_ContentSuggestions_ArticlesUsage"));
- }
- } // namespace
- void OnPageShown(const std::vector<Category>& categories,
- const std::vector<int>& suggestions_per_category,
- const std::vector<bool>& is_category_visible) {
- DCHECK_EQ(categories.size(), suggestions_per_category.size());
- DCHECK_EQ(categories.size(), is_category_visible.size());
- int suggestions_total = 0;
- int visible_categories_count = 0;
- for (size_t i = 0; i < categories.size(); ++i) {
- if (is_category_visible[i]) {
- LogCategoryHistogramPosition(kHistogramCountOnNtpOpenedIfVisible,
- categories[i], suggestions_per_category[i],
- kMaxSuggestionsPerCategory);
- suggestions_total += suggestions_per_category[i];
- ++visible_categories_count;
- }
- }
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramCountOnNtpOpenedIfVisible,
- suggestions_total, kMaxSuggestionsTotal);
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramSectionCountOnNtpOpened,
- visible_categories_count, kMaxCategories);
- }
- void OnSuggestionShown(int global_position,
- Category category,
- int position_in_category,
- base::Time publish_date,
- float score,
- base::Time fetch_date) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramShown, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramPosition(kHistogramShown, category, position_in_category,
- kMaxSuggestionsPerCategory);
- base::TimeDelta age = base::Time::Now() - publish_date;
- LogCategoryHistogramAge(kHistogramShownAge, category, age);
- LogCategoryHistogramScore(kHistogramShownScore, category, score);
- if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
- // Records the time since the fetch time of the displayed snippet.
- UMA_HISTOGRAM_CUSTOM_TIMES(kHistogramTimeSinceSuggestionFetched,
- base::Time::Now() - fetch_date, base::Seconds(1),
- base::Days(7),
- /*bucket_count=*/100);
- }
- // TODO(markusheintz): Discuss whether the code below should be moved into a
- // separate method called OnSuggestionsListShown.
- // When the first of the articles suggestions is shown, then we count this as
- // a single usage of content suggestions.
- if (category.IsKnownCategory(KnownCategories::ARTICLES) &&
- position_in_category == 0) {
- RecordContentSuggestionsUsage();
- }
- }
- void OnSuggestionOpened(int global_position,
- Category category,
- int category_index,
- int position_in_category,
- base::Time publish_date,
- float score,
- WindowOpenDisposition disposition,
- bool is_prefetched,
- bool is_offline) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpenedCategoryIndex, category_index,
- kMaxCategories);
- LogCategoryHistogramPosition(kHistogramOpenedCategoryIndex, category,
- category_index, kMaxCategories);
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpened, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramPosition(kHistogramOpened, category, position_in_category,
- kMaxSuggestionsPerCategory);
- base::TimeDelta age = base::Time::Now() - publish_date;
- LogCategoryHistogramAge(kHistogramOpenedAge, category, age);
- LogCategoryHistogramScore(kHistogramOpenedScore, category, score);
- // We use WindowOpenDisposition::MAX_VALUE + 1 for |value_max| since MAX_VALUE
- // itself is a valid (and used) enum value.
- UMA_HISTOGRAM_EXACT_LINEAR(
- kHistogramOpenDisposition, static_cast<int>(disposition),
- static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
- base::UmaHistogramExactLinear(
- GetCategoryHistogramName(kHistogramOpenDisposition, category),
- static_cast<int>(disposition),
- static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
- if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
- RecordContentSuggestionsUsage();
- if (is_offline && is_prefetched) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramPrefetchedArticleOpenedWhenOffline,
- position_in_category,
- kMaxSuggestionsPerCategory);
- }
- }
- base::RecordAction(base::UserMetricsAction("Suggestions.Content.Opened"));
- }
- void OnSuggestionMenuOpened(int global_position,
- Category category,
- int position_in_category,
- base::Time publish_date,
- float score) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramMenuOpened, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramPosition(kHistogramMenuOpened, category,
- position_in_category,
- kMaxSuggestionsPerCategory);
- base::TimeDelta age = base::Time::Now() - publish_date;
- LogCategoryHistogramAge(kHistogramMenuOpenedAge, category, age);
- LogCategoryHistogramScore(kHistogramMenuOpenedScore, category, score);
- }
- void OnSuggestionDismissed(int global_position,
- Category category,
- int position_in_category,
- bool visited) {
- if (visited) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedVisited, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramPosition(kHistogramDismissedVisited, category,
- position_in_category,
- kMaxSuggestionsPerCategory);
- } else {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedUnvisited, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramPosition(kHistogramDismissedUnvisited, category,
- position_in_category,
- kMaxSuggestionsPerCategory);
- }
- }
- void OnSuggestionTargetVisited(Category category, base::TimeDelta visit_time) {
- std::string name =
- GetCategoryHistogramName(kHistogramVisitDuration, category);
- base::UmaHistogramLongTimes(name, visit_time);
- }
- void OnCategoryMovedUp(int new_index) {
- UMA_HISTOGRAM_EXACT_LINEAR(kHistogramMovedUpCategoryNewIndex, new_index,
- kMaxCategories);
- }
- void OnMoreButtonShown(Category category, int position) {
- // The "more" card can appear in addition to the actual suggestions, so add
- // one extra bucket to this histogram.
- LogCategoryHistogramPosition(kHistogramMoreButtonShown, category, position,
- kMaxSuggestionsPerCategory + 1);
- }
- void OnMoreButtonClicked(Category category, int position) {
- // The "more" card can appear in addition to the actual suggestions, so add
- // one extra bucket to this histogram.
- LogCategoryHistogramPosition(kHistogramMoreButtonClicked, category, position,
- kMaxSuggestionsPerCategory + 1);
- }
- void OnCategoryDismissed(Category category) {
- UMA_HISTOGRAM_ENUMERATION(kHistogramCategoryDismissed,
- GetHistogramCategory(category),
- HistogramCategories::COUNT);
- }
- void RecordRemoteSuggestionsProviderState(bool enabled) {
- UMA_HISTOGRAM_BOOLEAN(
- "NewTabPage.ContentSuggestions.Preferences.RemoteSuggestions", enabled);
- }
- void RecordContentSuggestionDismissed() {
- base::RecordAction(base::UserMetricsAction("Suggestions.Content.Dismissed"));
- }
- void RecordCategoryDismissed() {
- base::RecordAction(base::UserMetricsAction("Suggestions.Category.Dismissed"));
- }
- void RecordFetchAction() {
- base::RecordAction(base::UserMetricsAction("Suggestions.Category.Fetch"));
- }
- } // namespace metrics
- } // namespace ntp_snippets
|