1 // Copyright 2023 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/structured/structured_metrics_service.h"
6
7 #include "components/metrics/metrics_log.h"
8 #include "components/metrics/metrics_service_client.h"
9 #include "components/metrics/structured/reporting/structured_metrics_reporting_service.h"
10 #include "components/metrics/structured/structured_metrics_features.h"
11 #include "third_party/metrics_proto/system_profile.pb.h"
12
13 namespace metrics::structured {
14
StructuredMetricsService(MetricsServiceClient * client,PrefService * local_state,std::unique_ptr<StructuredMetricsRecorder> recorder)15 StructuredMetricsService::StructuredMetricsService(
16 MetricsServiceClient* client,
17 PrefService* local_state,
18 std::unique_ptr<StructuredMetricsRecorder> recorder)
19 : recorder_(std::move(recorder)),
20 // This service is only enabled if both structured metrics and the service
21 // flags are enabled.
22 structured_metrics_enabled_(
23 base::FeatureList::IsEnabled(metrics::features::kStructuredMetrics) &&
24 base::FeatureList::IsEnabled(kEnabledStructuredMetricsService)),
25 client_(client) {
26 CHECK(client_);
27 CHECK(local_state);
28 CHECK(recorder_);
29
30 // If the StructuredMetricsService is not enabled then return early. The
31 // recorder needs to be initialized, but not the reporting service or
32 // scheduler.
33 if (!structured_metrics_enabled_) {
34 return;
35 }
36
37 // Setup the reporting service.
38 const UnsentLogStore::UnsentLogStoreLimits storage_limits =
39 GetLogStoreLimits();
40
41 reporting_service_ =
42 std::make_unique<reporting::StructuredMetricsReportingService>(
43 client_, local_state, storage_limits);
44
45 reporting_service_->Initialize();
46
47 // Setup the log rotation scheduler.
48 base::RepeatingClosure rotate_callback = base::BindRepeating(
49 &StructuredMetricsService::RotateLogsAndSend, weak_factory_.GetWeakPtr());
50 base::RepeatingCallback<base::TimeDelta(void)> get_upload_interval_callback =
51 base::BindRepeating(&StructuredMetricsService::GetUploadTimeInterval,
52 base::Unretained(this));
53
54 const bool fast_startup_for_test = client->ShouldStartUpFastForTesting();
55 scheduler_ = std::make_unique<StructuredMetricsScheduler>(
56 rotate_callback, get_upload_interval_callback, fast_startup_for_test);
57 }
58
~StructuredMetricsService()59 StructuredMetricsService::~StructuredMetricsService() {
60 // Will create a new log for all in-memory events.
61 // With this, we may be able to add a fast path initialization because flushed
62 // events do not need to be loaded.
63 if (recorder_ && recorder_->CanProvideMetrics() &&
64 recorder_->event_storage()->HasEvents()) {
65 Flush(metrics::MetricsLogsEventManager::CreateReason::kServiceShutdown);
66 }
67 }
68
EnableRecording()69 void StructuredMetricsService::EnableRecording() {
70 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
71 if (!structured_metrics_enabled_) {
72 return;
73 }
74 if (!initialize_complete_) {
75 Initialize();
76 }
77 recorder_->EnableRecording();
78
79 // Attempt an upload if reporting is also active.
80 if (initialize_complete_ && reporting_active()) {
81 MaybeStartUpload();
82 }
83 }
84
DisableRecording()85 void StructuredMetricsService::DisableRecording() {
86 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87 if (!structured_metrics_enabled_) {
88 return;
89 }
90 recorder_->DisableRecording();
91 }
92
EnableReporting()93 void StructuredMetricsService::EnableReporting() {
94 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
95 if (!structured_metrics_enabled_) {
96 return;
97 }
98 if (!reporting_active()) {
99 scheduler_->Start();
100 }
101 reporting_service_->EnableReporting();
102
103 // Attempt an upload if recording is also enabled.
104 if (initialize_complete_ && recording_enabled()) {
105 MaybeStartUpload();
106 }
107 }
108
DisableReporting()109 void StructuredMetricsService::DisableReporting() {
110 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
111 if (!structured_metrics_enabled_) {
112 return;
113 }
114 reporting_service_->DisableReporting();
115 scheduler_->Stop();
116 }
117
Flush(metrics::MetricsLogsEventManager::CreateReason reason)118 void StructuredMetricsService::Flush(
119 metrics::MetricsLogsEventManager::CreateReason reason) {
120 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
121 // The log should not be built if there aren't any events to log.
122 // This is mirroring a check in RotateLogsAndSend.
123 if (!recorder_->event_storage()->HasEvents()) {
124 return;
125 }
126 BuildAndStoreLog(reason);
127 reporting_service_->log_store()->TrimAndPersistUnsentLogs(true);
128 }
129
Purge()130 void StructuredMetricsService::Purge() {
131 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
132 if (!structured_metrics_enabled_) {
133 return;
134 }
135 recorder_->Purge();
136 reporting_service_->Purge();
137 }
138
GetUploadTimeInterval()139 base::TimeDelta StructuredMetricsService::GetUploadTimeInterval() {
140 return base::Seconds(GetUploadInterval());
141 }
142
RotateLogsAndSend()143 void StructuredMetricsService::RotateLogsAndSend() {
144 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
145
146 // Verify that the recorder has been initialized and can be providing metrics.
147 // And if it is, then see if there are any events ready to be uploaded.
148 if (!recorder_->CanProvideMetrics() ||
149 !recorder_->event_storage()->HasEvents()) {
150 return;
151 }
152
153 if (!reporting_service_->log_store()->has_unsent_logs()) {
154 BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason::kPeriodic);
155 }
156 reporting_service_->Start();
157 scheduler_->RotationFinished();
158 }
159
BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason reason)160 void StructuredMetricsService::BuildAndStoreLog(
161 metrics::MetricsLogsEventManager::CreateReason reason) {
162 ChromeUserMetricsExtension uma_proto;
163 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
164 InitializeUmaProto(uma_proto);
165 recorder_->ProvideEventMetrics(uma_proto);
166 const std::string serialized_log = SerializeLog(uma_proto);
167 reporting_service_->StoreLog(serialized_log, reason);
168 }
169
Initialize()170 void StructuredMetricsService::Initialize() {
171 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
172 DCHECK(!initialize_complete_);
173
174 initialize_complete_ = true;
175
176 // Notifies the scheduler that it is ready to start creating logs.
177 scheduler_->InitTaskComplete();
178 }
179
InitializeUmaProto(ChromeUserMetricsExtension & uma_proto)180 void StructuredMetricsService::InitializeUmaProto(
181 ChromeUserMetricsExtension& uma_proto) {
182 const int32_t product = client_->GetProduct();
183 if (product != uma_proto.product()) {
184 uma_proto.set_product(product);
185 }
186
187 SystemProfileProto* system_profile = uma_proto.mutable_system_profile();
188 metrics::MetricsLog::RecordCoreSystemProfile(client_, system_profile);
189 }
190
RegisterPrefs(PrefRegistrySimple * registry)191 void StructuredMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
192 reporting::StructuredMetricsReportingService::RegisterPrefs(registry);
193 }
194
SetRecorderForTest(std::unique_ptr<StructuredMetricsRecorder> recorder)195 void StructuredMetricsService::SetRecorderForTest(
196 std::unique_ptr<StructuredMetricsRecorder> recorder) {
197 recorder_ = std::move(recorder);
198 }
199
GetMetricsServiceClient() const200 MetricsServiceClient* StructuredMetricsService::GetMetricsServiceClient()
201 const {
202 return client_;
203 }
204
ManualUpload()205 void StructuredMetricsService::ManualUpload() {
206 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
207
208 if (!recorder_->CanProvideMetrics() ||
209 !recorder_->event_storage()->HasEvents()) {
210 return;
211 }
212
213 if (!reporting_service_->log_store()->has_unsent_logs()) {
214 BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
215 }
216 reporting_service_->Start();
217 }
218
MaybeStartUpload()219 void StructuredMetricsService::MaybeStartUpload() {
220 // We do not have any logs to upload. Nothing to do.
221 if (!reporting_service_->log_store()->has_unsent_logs()) {
222 return;
223 }
224
225 if (initial_upload_started_) {
226 return;
227 }
228
229 initial_upload_started_ = true;
230
231 // Starts an upload. If a log is not staged the next log will be staged for
232 // upload.
233 reporting_service_->Start();
234 }
235
236 // static:
SerializeLog(const ChromeUserMetricsExtension & uma_proto)237 std::string StructuredMetricsService::SerializeLog(
238 const ChromeUserMetricsExtension& uma_proto) {
239 std::string log_data;
240 const bool status = uma_proto.SerializeToString(&log_data);
241 DCHECK(status);
242 return log_data;
243 }
244
245 // static:
246 UnsentLogStore::UnsentLogStoreLimits
GetLogStoreLimits()247 StructuredMetricsService::GetLogStoreLimits() {
248 return UnsentLogStore::UnsentLogStoreLimits{
249 .min_log_count = static_cast<size_t>(kMinLogQueueCount.Get()),
250 .min_queue_size_bytes = static_cast<size_t>(kMinLogQueueSizeBytes.Get()),
251 .max_log_size_bytes = static_cast<size_t>(kMaxLogSizeBytes.Get()),
252 };
253 }
254
255 } // namespace metrics::structured
256