• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium Authors
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/metrics/metrics_service_observer.h"
6 
7 #include <string_view>
8 
9 #include "base/base64.h"
10 #include "base/callback_list.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "components/metrics/metrics_logs_event_manager.h"
18 
19 namespace metrics {
20 namespace {
21 
CreateEventStruct(MetricsLogsEventManager::LogEvent event,std::string_view message)22 MetricsServiceObserver::Log::Event CreateEventStruct(
23     MetricsLogsEventManager::LogEvent event,
24     std::string_view message) {
25   MetricsServiceObserver::Log::Event event_struct;
26   event_struct.event = event;
27   event_struct.timestampMs =
28       base::Time::Now().InMillisecondsFSinceUnixEpochIgnoringNull();
29   if (!message.empty()) {
30     event_struct.message = std::string(message);
31   }
32   return event_struct;
33 }
34 
LogTypeToString(MetricsLog::LogType log_type)35 std::string LogTypeToString(MetricsLog::LogType log_type) {
36   switch (log_type) {
37     case MetricsLog::LogType::INDEPENDENT_LOG:
38       return "Independent";
39     case MetricsLog::LogType::INITIAL_STABILITY_LOG:
40       return "Stability";
41     case MetricsLog::LogType::ONGOING_LOG:
42       return "Ongoing";
43   }
44   NOTREACHED();
45 }
46 
EventToString(MetricsLogsEventManager::LogEvent event)47 std::string EventToString(MetricsLogsEventManager::LogEvent event) {
48   switch (event) {
49     case MetricsLogsEventManager::LogEvent::kLogStaged:
50       return "Staged";
51     case MetricsLogsEventManager::LogEvent::kLogDiscarded:
52       return "Discarded";
53     case MetricsLogsEventManager::LogEvent::kLogTrimmed:
54       return "Trimmed";
55     case MetricsLogsEventManager::LogEvent::kLogUploading:
56       return "Uploading";
57     case MetricsLogsEventManager::LogEvent::kLogUploaded:
58       return "Uploaded";
59     case MetricsLogsEventManager::LogEvent::kLogCreated:
60       return "Created";
61   }
62   NOTREACHED();
63 }
64 
CreateReasonToString(metrics::MetricsLogsEventManager::CreateReason reason)65 std::string CreateReasonToString(
66     metrics::MetricsLogsEventManager::CreateReason reason) {
67   switch (reason) {
68     case MetricsLogsEventManager::CreateReason::kUnknown:
69       return std::string();
70     case MetricsLogsEventManager::CreateReason::kPeriodic:
71       return "Reason: Periodic log creation";
72     case MetricsLogsEventManager::CreateReason::kServiceShutdown:
73       return "Reason: Shutting down";
74     case MetricsLogsEventManager::CreateReason::kLoadFromPreviousSession:
75       return "Reason: Loaded from previous session";
76     case MetricsLogsEventManager::CreateReason::kBackgrounded:
77       return "Reason: Browser backgrounded";
78     case MetricsLogsEventManager::CreateReason::kForegrounded:
79       return "Reason: Browser foregrounded";
80     case MetricsLogsEventManager::CreateReason::kAlternateOngoingLogStoreSet:
81       return "Reason: Alternate ongoing log store set";
82     case MetricsLogsEventManager::CreateReason::kAlternateOngoingLogStoreUnset:
83       return "Reason: Alternate ongoing log store unset";
84     case MetricsLogsEventManager::CreateReason::kStability:
85       return "Reason: Stability metrics from previous session";
86     case MetricsLogsEventManager::CreateReason::kIndependent:
87       // TODO(crbug.com/40238818): Give more insight here (e.g. "independent log
88       // generated from pma file").
89       return "Reason: Independent log";
90   }
91 }
92 
93 }  // namespace
94 
MetricsServiceObserver(MetricsServiceType service_type)95 MetricsServiceObserver::MetricsServiceObserver(MetricsServiceType service_type)
96     : service_type_(service_type) {}
97 MetricsServiceObserver::~MetricsServiceObserver() = default;
98 MetricsServiceObserver::Log::Log() = default;
99 MetricsServiceObserver::Log::Log(const Log&) = default;
100 MetricsServiceObserver::Log& MetricsServiceObserver::Log::operator=(
101     const Log&) = default;
102 MetricsServiceObserver::Log::~Log() = default;
103 MetricsServiceObserver::Log::Event::Event() = default;
104 MetricsServiceObserver::Log::Event::Event(const Event&) = default;
105 MetricsServiceObserver::Log::Event&
106 MetricsServiceObserver::Log::Event::operator=(const Event&) = default;
107 MetricsServiceObserver::Log::Event::~Event() = default;
108 
OnLogCreated(std::string_view log_hash,std::string_view log_data,std::string_view log_timestamp,metrics::MetricsLogsEventManager::CreateReason reason)109 void MetricsServiceObserver::OnLogCreated(
110     std::string_view log_hash,
111     std::string_view log_data,
112     std::string_view log_timestamp,
113     metrics::MetricsLogsEventManager::CreateReason reason) {
114   DCHECK(!GetLogFromHash(log_hash));
115 
116   // Insert a new log into |logs_| with the given |log_hash| to indicate that
117   // this observer is now aware and keeping track of this log.
118   std::unique_ptr<Log> log = std::make_unique<Log>();
119   log->hash = std::string(log_hash);
120   log->timestamp = std::string(log_timestamp);
121   log->data = std::string(log_data);
122   if (uma_log_type_.has_value()) {
123     DCHECK_EQ(service_type_, MetricsServiceType::UMA);
124     log->type = uma_log_type_;
125   }
126 
127   // Immediately create a |kLogCreated| log event, along with the reason why the
128   // log was created.
129   log->events.push_back(
130       CreateEventStruct(MetricsLogsEventManager::LogEvent::kLogCreated,
131                         CreateReasonToString(reason)));
132 
133   indexed_logs_.emplace(log->hash, log.get());
134   logs_.push_back(std::move(log));
135 
136   // Call all registered callbacks.
137   notified_callbacks_.Notify();
138 }
139 
OnLogEvent(MetricsLogsEventManager::LogEvent event,std::string_view log_hash,std::string_view message)140 void MetricsServiceObserver::OnLogEvent(MetricsLogsEventManager::LogEvent event,
141                                         std::string_view log_hash,
142                                         std::string_view message) {
143   Log* log = GetLogFromHash(log_hash);
144 
145   // If this observer is not aware of any logs with the given |log_hash|, do
146   // nothing. This may happen if this observer started observing after a log
147   // was already created.
148   if (!log)
149     return;
150 
151   log->events.push_back(CreateEventStruct(event, message));
152 
153   // Call all registered callbacks.
154   notified_callbacks_.Notify();
155 }
156 
OnLogType(std::optional<MetricsLog::LogType> log_type)157 void MetricsServiceObserver::OnLogType(
158     std::optional<MetricsLog::LogType> log_type) {
159   uma_log_type_ = log_type;
160 }
161 
ExportLogsAsJson(bool include_log_proto_data,std::string * json_output)162 bool MetricsServiceObserver::ExportLogsAsJson(bool include_log_proto_data,
163                                               std::string* json_output) {
164   base::Value::List logs_list;
165   // Create and append to |logs_list| a base::Value for each log in |logs_|.
166   for (const std::unique_ptr<Log>& log : logs_) {
167     base::Value::Dict log_dict;
168 
169     if (log->type.has_value()) {
170       DCHECK_EQ(service_type_, MetricsServiceType::UMA);
171       log_dict.Set("type", LogTypeToString(log->type.value()));
172     }
173     log_dict.Set("hash", base::HexEncode(log->hash));
174     log_dict.Set("timestamp", log->timestamp);
175 
176     if (include_log_proto_data) {
177       log_dict.Set("data", base::Base64Encode(log->data));
178     }
179 
180     log_dict.Set("size", static_cast<int>(log->data.length()));
181 
182     base::Value::List log_events_list;
183     for (const Log::Event& event : log->events) {
184       base::Value::Dict log_event_dict;
185       log_event_dict.Set("event", EventToString(event.event));
186       log_event_dict.Set("timestampMs", event.timestampMs);
187       if (event.message.has_value())
188         log_event_dict.Set("message", event.message.value());
189       log_events_list.Append(std::move(log_event_dict));
190     }
191     log_dict.Set("events", std::move(log_events_list));
192 
193     logs_list.Append(std::move(log_dict));
194   }
195 
196   // Create a last |dict| that contains all the logs and |service_type_|,
197   // convert it to a JSON string, and write it to |json_output|.
198   base::Value::Dict dict;
199   dict.Set("logType", service_type_ == MetricsServiceType::UMA ? "UMA" : "UKM");
200   dict.Set("logs", std::move(logs_list));
201 
202   JSONStringValueSerializer serializer(json_output);
203   return serializer.Serialize(dict);
204 }
205 
ExportLogsToFile(const base::FilePath & path)206 void MetricsServiceObserver::ExportLogsToFile(const base::FilePath& path) {
207   std::string logs_data;
208   bool success = ExportLogsAsJson(/*include_log_proto_data=*/true, &logs_data);
209   DCHECK(success);
210   if (!base::WriteFile(path, logs_data)) {
211     LOG(ERROR) << "Failed to export logs to " << path << ": " << logs_data;
212   }
213 }
214 
AddNotifiedCallback(base::RepeatingClosure callback)215 base::CallbackListSubscription MetricsServiceObserver::AddNotifiedCallback(
216     base::RepeatingClosure callback) {
217   return notified_callbacks_.Add(callback);
218 }
219 
GetLogFromHash(std::string_view log_hash)220 MetricsServiceObserver::Log* MetricsServiceObserver::GetLogFromHash(
221     std::string_view log_hash) {
222   auto it = indexed_logs_.find(log_hash);
223   return it != indexed_logs_.end() ? it->second : nullptr;
224 }
225 
226 }  // namespace metrics
227