// Copyright 2017 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/feature_engagement/internal/stats.h" #include #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" #include "components/feature_engagement/public/feature_list.h" namespace feature_engagement { namespace stats { namespace { // Histogram suffixes for database metrics, must match the ones in // histograms.xml. const char kEventStoreSuffix[] = "EventStore"; const char kAvailabilityStoreSuffix[] = "AvailabilityStore"; // A shadow histogram across all features. Also the base name for the suffix // based feature specific histograms; for example for IPH_MyFun, it would be: // InProductHelp.ShouldTriggerHelpUI.IPH_MyFun. const char kShouldTriggerHelpUIHistogram[] = "InProductHelp.ShouldTriggerHelpUI"; // Helper function to log a TriggerHelpUIResult. void LogTriggerHelpUIResult(const std::string& name, TriggerHelpUIResult result) { // Must not use histograms macros here because we pass in the histogram name. base::UmaHistogramEnumeration(name, result, TriggerHelpUIResult::COUNT); base::UmaHistogramEnumeration(kShouldTriggerHelpUIHistogram, result, TriggerHelpUIResult::COUNT); } } // namespace std::string ToDbHistogramSuffix(StoreType type) { switch (type) { case StoreType::EVENTS_STORE: return std::string(kEventStoreSuffix); case StoreType::AVAILABILITY_STORE: return std::string(kAvailabilityStoreSuffix); default: NOTREACHED(); return std::string(); } } void RecordNotifyEvent(const std::string& event_name, const Configuration* config, bool is_model_ready) { DCHECK(!event_name.empty()); DCHECK(config); // Find which feature this event belongs to. const Configuration::ConfigMap& features = config->GetRegisteredFeatureConfigs(); std::string feature_name; for (const auto& element : features) { const std::string fname = element.first; const FeatureConfig& feature_config = element.second; // Track used event separately. if (feature_config.used.name == event_name) { feature_name = fname; DCHECK(!feature_name.empty()); std::string used_event_action = "InProductHelp.NotifyUsedEvent."; used_event_action.append(feature_name); base::RecordComputedAction(used_event_action); break; } // Find if the |event_name| matches any configuration. for (const auto& event : feature_config.event_configs) { if (event.name == event_name) { feature_name = fname; break; } } if (feature_config.trigger.name == event_name) { feature_name = fname; break; } } // Do nothing if no events in the configuration matches the |event_name|. if (feature_name.empty()) return; std::string event_action = "InProductHelp.NotifyEvent."; event_action.append(feature_name); base::RecordComputedAction(event_action); std::string event_histogram = "InProductHelp.NotifyEventReadyState."; event_histogram.append(feature_name); base::UmaHistogramBoolean(event_histogram, is_model_ready); } void RecordShouldTriggerHelpUI(const base::Feature& feature, const FeatureConfig& feature_config, const ConditionValidator::Result& result) { // Records the user action. std::string name = std::string(kShouldTriggerHelpUIHistogram) .append(".") .append(feature.name); base::RecordComputedAction(name); // Total count histogram, used to compute the percentage of each failure type, // in addition to a user action for whether the result was to trigger or not. if (result.NoErrors()) { LogTriggerHelpUIResult(name, feature_config.tracking_only ? TriggerHelpUIResult::SUCCESS_TRACKING_ONLY : TriggerHelpUIResult::SUCCESS); std::string action_name = "InProductHelp.ShouldTriggerHelpUIResult."; action_name.append(feature_config.tracking_only ? "WouldHaveTriggered" : "Triggered"); action_name.append("."); action_name.append(feature.name); base::RecordComputedAction(action_name); } else { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE); std::string action_name = "InProductHelp.ShouldTriggerHelpUIResult.NotTriggered."; action_name.append(feature.name); base::RecordComputedAction(action_name); } // Histogram about the failure reasons. if (!result.event_model_ready_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_EVENT_MODEL_NOT_READY); } if (!result.currently_showing_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_CURRENTLY_SHOWING); } if (!result.feature_enabled_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_FEATURE_DISABLED); } if (!result.config_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_CONFIG_INVALID); } if (!result.used_ok) { LogTriggerHelpUIResult( name, TriggerHelpUIResult::FAILURE_USED_PRECONDITION_UNMET); } if (!result.trigger_ok) { LogTriggerHelpUIResult( name, TriggerHelpUIResult::FAILURE_TRIGGER_PRECONDITION_UNMET); } if (!result.preconditions_ok) { LogTriggerHelpUIResult( name, TriggerHelpUIResult::FAILURE_OTHER_PRECONDITION_UNMET); } if (!result.session_rate_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_SESSION_RATE); } if (!result.availability_model_ready_ok) { LogTriggerHelpUIResult( name, TriggerHelpUIResult::FAILURE_AVAILABILITY_MODEL_NOT_READY); } if (!result.availability_ok) { LogTriggerHelpUIResult( name, TriggerHelpUIResult::FAILURE_AVAILABILITY_PRECONDITION_UNMET); } if (!result.display_lock_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_DISPLAY_LOCK); } } void RecordUserDismiss() { base::RecordAction(base::UserMetricsAction("InProductHelp.Dismissed")); } void RecordDbUpdate(bool success, StoreType type) { std::string histogram_name = "InProductHelp.Db.Update." + ToDbHistogramSuffix(type); base::UmaHistogramBoolean(histogram_name, success); } void RecordDbInitEvent(bool success, StoreType type) { std::string histogram_name = "InProductHelp.Db.Init." + ToDbHistogramSuffix(type); base::UmaHistogramBoolean(histogram_name, success); } void RecordEventDbLoadEvent(bool success, const std::vector& events) { std::string histogram_name = "InProductHelp.Db.Load." + ToDbHistogramSuffix(StoreType::EVENTS_STORE); base::UmaHistogramBoolean(histogram_name, success); UMA_HISTOGRAM_BOOLEAN("InProductHelp.Db.Load", success); if (!success) return; // Tracks total number of events records when the database is successfully // loaded. int event_count = 0; for (const auto& event : events) event_count += event.events_size(); UMA_HISTOGRAM_COUNTS_1000("InProductHelp.Db.TotalEvents", event_count); } void RecordAvailabilityDbLoadEvent(bool success) { std::string histogram_name = "InProductHelp.Db.Load." + ToDbHistogramSuffix(StoreType::AVAILABILITY_STORE); base::UmaHistogramBoolean(histogram_name, success); UMA_HISTOGRAM_BOOLEAN("InProductHelp.Db.Load", success); } void RecordConfigParsingEvent(ConfigParsingEvent event) { UMA_HISTOGRAM_ENUMERATION("InProductHelp.Config.ParsingEvent", event, ConfigParsingEvent::COUNT); } } // namespace stats } // namespace feature_engagement