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