Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: components/ukm/ukm_service.cc

Issue 2567263003: Basic UkmService implementation (Closed)
Patch Set: Observe helper Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
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/ukm/ukm_service.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/feature_list.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/rand_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/time/time.h"
18 #include "components/metrics/metrics_log_uploader.h"
19 #include "components/metrics/metrics_service_client.h"
20 #include "components/metrics/proto/ukm/report.pb.h"
21 #include "components/prefs/pref_registry_simple.h"
22 #include "components/prefs/pref_service.h"
23 #include "components/ukm/metrics_reporting_scheduler.h"
24 #include "components/ukm/persisted_logs_metrics_impl.h"
25 #include "components/ukm/ukm_pref_names.h"
26 #include "components/variations/variations_associated_data.h"
27
28 namespace ukm {
29
30 namespace {
31
32 constexpr char kMimeType[] = "application/vnd.chrome.ukm";
33
34 // The UKM server's URL.
35 constexpr char kDefaultServerUrl[] = "https://clients4.google.com/ukm";
36
37 // The delay, in seconds, after starting recording before doing expensive
38 // initialization work.
39 constexpr int kInitializationDelaySeconds = 5;
40
41 // The number of UKM logs that will be stored in PersistedLogs before logs
42 // start being dropped.
43 constexpr int kMinPersistedLogs = 8;
44
45 // The number of bytes UKM logs that will be stored in PersistedLogs before
46 // logs start being dropped.
47 // This ensures that a reasonable amount of history will be stored even if there
48 // is a long series of very small logs.
49 constexpr int kMinPersistedBytes = 300000;
50
51 // If an upload fails, and the transmission was over this byte count, then we
52 // will discard the log, and not try to retransmit it. We also don't persist
53 // the log to the prefs for transmission during the next chrome session if this
54 // limit is exceeded.
55 constexpr size_t kMaxLogRetransmitSize = 100 * 1024;
56
57 std::string GetServerUrl() {
58 std::string server_url =
59 variations::GetVariationParamValueByFeature(kUkmFeature, "ServerUrl");
60 if (!server_url.empty())
61 return server_url;
62 return kDefaultServerUrl;
63 }
64
65 uint64_t LoadOrGenerateClientId(PrefService* pref_service) {
66 uint64_t client_id = pref_service->GetInt64(prefs::kUkmClientId);
67 if (!client_id) {
68 // Generate and store a new client id.
69 while (!client_id)
70 client_id = base::RandUint64();
71 pref_service->SetInt64(prefs::kUkmClientId, client_id);
72 }
73 return client_id;
74 }
75
76 } // namespace
77
78 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT};
79
80 UkmService::UkmService(PrefService* pref_service,
81 metrics::MetricsServiceClient* client)
82 : pref_service_(pref_service),
83 client_(client),
84 persisted_logs_(std::unique_ptr<ukm::PersistedLogsMetricsImpl>(
85 new ukm::PersistedLogsMetricsImpl()),
86 pref_service,
87 prefs::kUkmPersistedLogs,
88 kMinPersistedLogs,
89 kMinPersistedBytes,
90 kMaxLogRetransmitSize),
91 initialize_started_(false),
92 initialize_complete_(false),
93 log_upload_in_progress_(false),
94 self_ptr_factory_(this) {
95 DCHECK(pref_service_);
96 DCHECK(client_);
97
98 persisted_logs_.DeserializeLogs();
99
100 base::Closure rotate_callback =
101 base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr());
102 // MetricsServiceClient outlives UkmService, and
103 // MetricsReportingScheduler is tied to the lifetime of |this|.
104 const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback =
105 base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval,
106 base::Unretained(client_));
107 scheduler_.reset(new ukm::MetricsReportingScheduler(
108 rotate_callback, get_upload_interval_callback));
109 }
110
111 UkmService::~UkmService() {
112 DisableReporting();
113 }
114
115 void UkmService::Initialize() {
116 DCHECK(thread_checker_.CalledOnValidThread());
117 DVLOG(1) << "UkmService::Initialize";
118 initialize_started_ = true;
119
120 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
121 FROM_HERE,
122 base::Bind(&UkmService::StartInitTask, self_ptr_factory_.GetWeakPtr()),
123 base::TimeDelta::FromSeconds(kInitializationDelaySeconds));
124 }
125
126 void UkmService::EnableReporting() {
127 DCHECK(thread_checker_.CalledOnValidThread());
128 DVLOG(1) << "UkmService::EnableReporting";
129 if (!initialize_started_)
130 Initialize();
131 scheduler_->Start();
132 }
133
134 void UkmService::DisableReporting() {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 DVLOG(1) << "UkmService::DisableReporting";
137 scheduler_->Stop();
138 Flush();
139 }
140
141 void UkmService::Flush() {
142 if (initialize_complete_)
143 BuildAndStoreLog();
144 persisted_logs_.SerializeLogs();
145 }
146
147 void UkmService::Purge() {
148 DVLOG(1) << "UkmService::Purge";
149 persisted_logs_.Purge();
150 // TODO(oystein): Delete any stored sources.
151 }
152
153 // static
154 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) {
155 registry->RegisterInt64Pref(prefs::kUkmClientId, 0);
156 registry->RegisterListPref(prefs::kUkmPersistedLogs);
157 }
158
159 void UkmService::StartInitTask() {
160 DCHECK(thread_checker_.CalledOnValidThread());
161 DVLOG(1) << "UkmService::StartInitTask";
162 client_id_ = LoadOrGenerateClientId(pref_service_);
163 client_->InitializeSystemProfileMetrics(base::Bind(
164 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr()));
165 }
166
167 void UkmService::FinishedInitTask() {
168 DCHECK(thread_checker_.CalledOnValidThread());
169 DVLOG(1) << "UkmService::FinishedInitTask";
170 initialize_complete_ = true;
171 scheduler_->InitTaskComplete();
172 }
173
174 void UkmService::RotateLog() {
175 DCHECK(thread_checker_.CalledOnValidThread());
176 DCHECK(!log_upload_in_progress_);
177 DVLOG(1) << "UkmService::RotateLog";
178 if (persisted_logs_.empty()) {
179 BuildAndStoreLog();
180 }
181 StartScheduledUpload();
182 }
183
184 void UkmService::BuildAndStoreLog() {
185 DCHECK(thread_checker_.CalledOnValidThread());
186 DVLOG(1) << "UkmService::BuildAndStoreLog";
187 Report report;
188 report.set_client_id(client_id_);
189 // TODO(holte): Populate system_profile.
190 // TODO(oystein): Populate sources.
191 std::string serialized_log;
192 report.SerializeToString(&serialized_log);
193 persisted_logs_.StoreLog(serialized_log);
194 }
195
196 void UkmService::StartScheduledUpload() {
197 DCHECK(thread_checker_.CalledOnValidThread());
198 DCHECK(!log_upload_in_progress_);
199 if (!persisted_logs_.has_staged_log())
200 persisted_logs_.StageLog();
201 // TODO(holte): Handle data usage on cellular, etc.
202 if (!log_uploader_) {
203 log_uploader_ = client_->CreateUploader(
204 GetServerUrl(), kMimeType, base::Bind(&UkmService::OnLogUploadComplete,
205 self_ptr_factory_.GetWeakPtr()));
206 }
207 log_upload_in_progress_ = true;
208
209 const std::string hash =
210 base::HexEncode(persisted_logs_.staged_log_hash().data(),
211 persisted_logs_.staged_log_hash().size());
212 log_uploader_->UploadLog(persisted_logs_.staged_log(), hash);
213 }
214
215 void UkmService::OnLogUploadComplete(int response_code) {
216 DCHECK(thread_checker_.CalledOnValidThread());
217 DCHECK(log_upload_in_progress_);
218 DVLOG(1) << "UkmService::OnLogUploadComplete";
219 log_upload_in_progress_ = false;
220
221 UMA_HISTOGRAM_SPARSE_SLOWLY("UKM.Upload.ResponseCode", response_code);
222
223 bool upload_succeeded = response_code == 200;
224
225 // Provide boolean for error recovery (allow us to ignore response_code).
226 bool discard_log = false;
227 const size_t log_size_bytes = persisted_logs_.staged_log().length();
228 if (upload_succeeded) {
229 UMA_HISTOGRAM_COUNTS_10000("UKM.LogSize.OnSuccess", log_size_bytes / 1024);
230 } else if (response_code == 400) {
231 // Bad syntax. Retransmission won't work.
232 discard_log = true;
233 }
234
235 if (upload_succeeded || discard_log) {
236 persisted_logs_.DiscardStagedLog();
237 // Store the updated list to disk now that the removed log is uploaded.
238 persisted_logs_.SerializeLogs();
239 }
240
241 // Error 400 indicates a problem with the log, not with the server, so
242 // don't consider that a sign that the server is in trouble.
243 bool server_is_healthy = upload_succeeded || response_code == 400;
244 scheduler_->UploadFinished(server_is_healthy, !persisted_logs_.empty());
245 }
246
247 } // namespace ukm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698