1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/feature_engagement/internal/stats.h"
6 
7 #include <string>
8 
9 #include "base/metrics/histogram_functions.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/metrics/user_metrics.h"
12 #include "components/feature_engagement/public/feature_list.h"
13 
14 namespace feature_engagement {
15 namespace stats {
16 namespace {
17 
18 // Histogram suffixes for database metrics, must match the ones in
19 // histograms.xml.
20 const char kEventStoreSuffix[] = "EventStore";
21 const char kAvailabilityStoreSuffix[] = "AvailabilityStore";
22 
23 // A shadow histogram across all features. Also the base name for the suffix
24 // based feature specific histograms; for example for IPH_MyFun, it would be:
25 // InProductHelp.ShouldTriggerHelpUI.IPH_MyFun.
26 const char kShouldTriggerHelpUIHistogram[] =
27     "InProductHelp.ShouldTriggerHelpUI";
28 
29 // Helper function to log a TriggerHelpUIResult.
LogTriggerHelpUIResult(const std::string & name,TriggerHelpUIResult result)30 void LogTriggerHelpUIResult(const std::string& name,
31                             TriggerHelpUIResult result) {
32   // Must not use histograms macros here because we pass in the histogram name.
33   base::UmaHistogramEnumeration(name, result, TriggerHelpUIResult::COUNT);
34   base::UmaHistogramEnumeration(kShouldTriggerHelpUIHistogram, result,
35                                 TriggerHelpUIResult::COUNT);
36 }
37 
38 }  // namespace
39 
ToDbHistogramSuffix(StoreType type)40 std::string ToDbHistogramSuffix(StoreType type) {
41   switch (type) {
42     case StoreType::EVENTS_STORE:
43       return std::string(kEventStoreSuffix);
44     case StoreType::AVAILABILITY_STORE:
45       return std::string(kAvailabilityStoreSuffix);
46     default:
47       NOTREACHED();
48       return std::string();
49   }
50 }
51 
RecordNotifyEvent(const std::string & event_name,const Configuration * config,bool is_model_ready)52 void RecordNotifyEvent(const std::string& event_name,
53                        const Configuration* config,
54                        bool is_model_ready) {
55   DCHECK(!event_name.empty());
56   DCHECK(config);
57 
58   // Find which feature this event belongs to.
59   const Configuration::ConfigMap& features =
60       config->GetRegisteredFeatureConfigs();
61   std::string feature_name;
62   for (const auto& element : features) {
63     const std::string fname = element.first;
64     const FeatureConfig& feature_config = element.second;
65 
66     // Track used event separately.
67     if (feature_config.used.name == event_name) {
68       feature_name = fname;
69       DCHECK(!feature_name.empty());
70       std::string used_event_action = "InProductHelp.NotifyUsedEvent.";
71       used_event_action.append(feature_name);
72       base::RecordComputedAction(used_event_action);
73       break;
74     }
75 
76     // Find if the |event_name| matches any configuration.
77     for (const auto& event : feature_config.event_configs) {
78       if (event.name == event_name) {
79         feature_name = fname;
80         break;
81       }
82     }
83     if (feature_config.trigger.name == event_name) {
84       feature_name = fname;
85       break;
86     }
87   }
88 
89   // Do nothing if no events in the configuration matches the |event_name|.
90   if (feature_name.empty())
91     return;
92 
93   std::string event_action = "InProductHelp.NotifyEvent.";
94   event_action.append(feature_name);
95   base::RecordComputedAction(event_action);
96 
97   std::string event_histogram = "InProductHelp.NotifyEventReadyState.";
98   event_histogram.append(feature_name);
99   base::UmaHistogramBoolean(event_histogram, is_model_ready);
100 }
101 
RecordShouldTriggerHelpUI(const base::Feature & feature,const FeatureConfig & feature_config,const ConditionValidator::Result & result)102 void RecordShouldTriggerHelpUI(const base::Feature& feature,
103                                const FeatureConfig& feature_config,
104                                const ConditionValidator::Result& result) {
105   // Records the user action.
106   std::string name = std::string(kShouldTriggerHelpUIHistogram)
107                          .append(".")
108                          .append(feature.name);
109   base::RecordComputedAction(name);
110 
111   // Total count histogram, used to compute the percentage of each failure type,
112   // in addition to a user action for whether the result was to trigger or not.
113   if (result.NoErrors()) {
114     LogTriggerHelpUIResult(name,
115                            feature_config.tracking_only
116                                ? TriggerHelpUIResult::SUCCESS_TRACKING_ONLY
117                                : TriggerHelpUIResult::SUCCESS);
118     std::string action_name = "InProductHelp.ShouldTriggerHelpUIResult.";
119     action_name.append(feature_config.tracking_only ? "WouldHaveTriggered"
120                                                     : "Triggered");
121     action_name.append(".");
122     action_name.append(feature.name);
123     base::RecordComputedAction(action_name);
124   } else {
125     LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE);
126     std::string action_name =
127         "InProductHelp.ShouldTriggerHelpUIResult.NotTriggered.";
128     action_name.append(feature.name);
129     base::RecordComputedAction(action_name);
130   }
131 
132   // Histogram about the failure reasons.
133   if (!result.event_model_ready_ok) {
134     LogTriggerHelpUIResult(name,
135                            TriggerHelpUIResult::FAILURE_EVENT_MODEL_NOT_READY);
136   }
137   if (!result.currently_showing_ok) {
138     LogTriggerHelpUIResult(name,
139                            TriggerHelpUIResult::FAILURE_CURRENTLY_SHOWING);
140   }
141   if (!result.feature_enabled_ok) {
142     LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_FEATURE_DISABLED);
143   }
144   if (!result.config_ok) {
145     LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_CONFIG_INVALID);
146   }
147   if (!result.used_ok) {
148     LogTriggerHelpUIResult(
149         name, TriggerHelpUIResult::FAILURE_USED_PRECONDITION_UNMET);
150   }
151   if (!result.trigger_ok) {
152     LogTriggerHelpUIResult(
153         name, TriggerHelpUIResult::FAILURE_TRIGGER_PRECONDITION_UNMET);
154   }
155   if (!result.preconditions_ok) {
156     LogTriggerHelpUIResult(
157         name, TriggerHelpUIResult::FAILURE_OTHER_PRECONDITION_UNMET);
158   }
159   if (!result.session_rate_ok) {
160     LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_SESSION_RATE);
161   }
162   if (!result.availability_model_ready_ok) {
163     LogTriggerHelpUIResult(
164         name, TriggerHelpUIResult::FAILURE_AVAILABILITY_MODEL_NOT_READY);
165   }
166   if (!result.availability_ok) {
167     LogTriggerHelpUIResult(
168         name, TriggerHelpUIResult::FAILURE_AVAILABILITY_PRECONDITION_UNMET);
169   }
170   if (!result.display_lock_ok) {
171     LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_DISPLAY_LOCK);
172   }
173 }
174 
RecordUserDismiss()175 void RecordUserDismiss() {
176   base::RecordAction(base::UserMetricsAction("InProductHelp.Dismissed"));
177 }
178 
RecordDbUpdate(bool success,StoreType type)179 void RecordDbUpdate(bool success, StoreType type) {
180   std::string histogram_name =
181       "InProductHelp.Db.Update." + ToDbHistogramSuffix(type);
182   base::UmaHistogramBoolean(histogram_name, success);
183 }
184 
RecordDbInitEvent(bool success,StoreType type)185 void RecordDbInitEvent(bool success, StoreType type) {
186   std::string histogram_name =
187       "InProductHelp.Db.Init." + ToDbHistogramSuffix(type);
188   base::UmaHistogramBoolean(histogram_name, success);
189 }
190 
RecordEventDbLoadEvent(bool success,const std::vector<Event> & events)191 void RecordEventDbLoadEvent(bool success, const std::vector<Event>& events) {
192   std::string histogram_name =
193       "InProductHelp.Db.Load." + ToDbHistogramSuffix(StoreType::EVENTS_STORE);
194   base::UmaHistogramBoolean(histogram_name, success);
195   UMA_HISTOGRAM_BOOLEAN("InProductHelp.Db.Load", success);
196 
197   if (!success)
198     return;
199 
200   // Tracks total number of events records when the database is successfully
201   // loaded.
202   int event_count = 0;
203   for (const auto& event : events)
204     event_count += event.events_size();
205   UMA_HISTOGRAM_COUNTS_1000("InProductHelp.Db.TotalEvents", event_count);
206 }
207 
RecordAvailabilityDbLoadEvent(bool success)208 void RecordAvailabilityDbLoadEvent(bool success) {
209   std::string histogram_name =
210       "InProductHelp.Db.Load." +
211       ToDbHistogramSuffix(StoreType::AVAILABILITY_STORE);
212   base::UmaHistogramBoolean(histogram_name, success);
213   UMA_HISTOGRAM_BOOLEAN("InProductHelp.Db.Load", success);
214 }
215 
RecordConfigParsingEvent(ConfigParsingEvent event)216 void RecordConfigParsingEvent(ConfigParsingEvent event) {
217   UMA_HISTOGRAM_ENUMERATION("InProductHelp.Config.ParsingEvent", event,
218                             ConfigParsingEvent::COUNT);
219 }
220 
221 }  // namespace stats
222 }  // namespace feature_engagement
223