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