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