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

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

Issue 2567263003: Basic UkmService implementation (Closed)
Patch Set: RegisterPrefs 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 <utility>
8
9 #include "base/feature_list.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/rand_util.h"
14 #include "base/time/time.h"
15 #include "components/metrics/metrics_log_uploader.h"
16 #include "components/metrics/metrics_service_client.h"
17 #include "components/metrics/proto/ukm/report.pb.h"
18 #include "components/prefs/pref_registry_simple.h"
19 #include "components/prefs/pref_service.h"
20 #include "components/ukm/metrics_reporting_scheduler.h"
21 #include "components/ukm/persisted_logs_metrics_impl.h"
22 #include "components/ukm/ukm_pref_names.h"
23 #include "components/variations/variations_associated_data.h"
24
25 namespace ukm {
26
27 namespace {
28
29 const char kMimeType[] = "application/vnd.chrome.ukm";
30
31 // Constants for the UKM rollout field trial.
32 const char kUkmRolloutFieldTrialName[] = "UkmRollout";
33
34 // Constant for the variations parameter name for the server URL
35 const char kUkmRolloutServerUrlParam[] = "ServerUrl";
36
37 // The UKM server's URL.
38 const char kDefaultServerUrl[] = "https://clients4.google.com/ukm";
39
40 // The delay, in seconds, after starting recording before doing expensive
41 // initialization work.
42 const int kInitializationDelaySeconds = 5;
43
44 // The number of UKM logs that will be stored in PersistedLogs before logs
45 // start being dropped.
46 const int kMinPersistedLogs = 8;
47
48 // The number of bytes UKM logs that will be stored in PersistedLogs before
49 // logs start being dropped.
50 // This ensures that a reasonable amount of history will be stored even if there
51 // is a long series of very small logs.
52 const int kMinPersistedBytes = 300000;
53
54 // If an upload fails, and the transmission was over this byte count, then we
55 // will discard the log, and not try to retransmit it. We also don't persist
56 // the log to the prefs for transmission during the next chrome session if this
57 // limit is exceeded.
58 const size_t kMaxLogRetransmitSize = 100 * 1024;
59
60 std::string GetServerUrl() {
61 std::string server_url = variations::GetVariationParamValue(
62 kUkmRolloutFieldTrialName, kUkmRolloutServerUrlParam);
Alexei Svitkine (slow) 2017/01/10 17:37:19 Nit: Use GetVariationParamValueByFeature() and the
Steven Holte 2017/01/10 22:52:45 Done.
63 if (!server_url.empty())
64 return server_url;
65 return kDefaultServerUrl;
66 }
67
68 uint64_t LoadOrGenerateClientId(PrefService* pref_service) {
69 uint64_t client_id = pref_service->GetInteger(prefs::kUkmClientId);
70 if (!client_id) {
Alexei Svitkine (slow) 2017/01/10 17:37:19 What if 0 is legitimately generated? You can use
Steven Holte 2017/01/10 22:52:44 Added the loop.
71 // Generate and store a new client id.
72 client_id = base::RandUint64();
73 pref_service->SetInteger(prefs::kUkmClientId, client_id);
Alexei Svitkine (slow) 2017/01/10 17:37:19 SetInt64() and GetInt64() Integer may be 32 bit o
Steven Holte 2017/01/10 22:52:45 Done.
74 }
75 return client_id;
76 }
77
78 } // namespace
79
80 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT};
81
82 UkmService::UkmService(PrefService* pref_service,
83 metrics::MetricsServiceClient* client)
84 : pref_service_(pref_service),
85 client_(client),
86 persisted_logs_(std::unique_ptr<ukm::PersistedLogsMetricsImpl>(
87 new ukm::PersistedLogsMetricsImpl()),
88 pref_service,
89 prefs::kUkmPersistedLogs,
90 kMinPersistedLogs,
91 kMinPersistedBytes,
92 kMaxLogRetransmitSize),
93 initialize_started_(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
113 void UkmService::Initialize() {
114 DCHECK(thread_checker_.CalledOnValidThread());
115 DVLOG(1) << "UkmService::Initialize";
116 initialize_started_ = true;
117
118 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
119 FROM_HERE,
120 base::Bind(&UkmService::StartInitTask, self_ptr_factory_.GetWeakPtr()),
121 base::TimeDelta::FromSeconds(kInitializationDelaySeconds));
122 }
123
124 void UkmService::EnableReporting() {
125 DCHECK(thread_checker_.CalledOnValidThread());
126 DVLOG(1) << "UkmService::EnableReporting";
127 if (!initialize_started_)
128 Initialize();
129 scheduler_->Start();
130 }
131
132 void UkmService::DisableReporting() {
133 DCHECK(thread_checker_.CalledOnValidThread());
134 DVLOG(1) << "UkmService::DisableReporting";
135 scheduler_->UploadCancelled();
136 scheduler_->Stop();
137 }
138
139 // static
140 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) {
141 registry->RegisterIntegerPref(prefs::kUkmClientId, 0);
142 registry->RegisterListPref(prefs::kUkmPersistedLogs);
143 }
144
145 void UkmService::StartInitTask() {
146 DCHECK(thread_checker_.CalledOnValidThread());
147 DVLOG(1) << "UkmService::StartInitTask";
148 client_id_ = LoadOrGenerateClientId(pref_service_);
149 client_->InitializeSystemProfileMetrics(base::Bind(
150 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr()));
151 }
152
153 void UkmService::FinishedInitTask() {
154 DCHECK(thread_checker_.CalledOnValidThread());
155 DVLOG(1) << "UkmService::FinishedInitTask";
156 scheduler_->InitTaskComplete();
157 }
158
159 void UkmService::RotateLog() {
160 DCHECK(thread_checker_.CalledOnValidThread());
161 DCHECK(!log_upload_in_progress_);
162 DVLOG(1) << "UkmService::RotateLog";
163 if (persisted_logs_.empty()) {
164 BuildAndStoreLog();
165 }
166 StartScheduledUpload();
167 }
168
169 void UkmService::BuildAndStoreLog() {
170 DCHECK(thread_checker_.CalledOnValidThread());
171 DVLOG(1) << "UkmService::BuildAndStoreLog";
172 Report report;
173 report.set_client_id(client_id_);
174 // TODO(holte): Populate system_profile.
175 // TODO(zhenw): Populate sources.
176 std::string serialized_log;
177 report.SerializeToString(&serialized_log);
178 persisted_logs_.StoreLog(serialized_log);
179 }
180
181 void UkmService::StartScheduledUpload() {
182 DCHECK(thread_checker_.CalledOnValidThread());
183 DCHECK(!log_upload_in_progress_);
184 persisted_logs_.StageLog();
185 // TODO(holte): Handle data usage on cellular, etc.
186 if (!log_uploader_) {
187 log_uploader_ = client_->CreateUploader(
188 GetServerUrl(), kMimeType, base::Bind(&UkmService::OnLogUploadComplete,
189 self_ptr_factory_.GetWeakPtr()));
190 }
191 log_upload_in_progress_ = true;
192 log_uploader_->UploadLog(persisted_logs_.staged_log(),
193 persisted_logs_.staged_log_hash());
194 }
195
196 void UkmService::OnLogUploadComplete(int response_code) {
197 DCHECK(thread_checker_.CalledOnValidThread());
198 DCHECK(log_upload_in_progress_);
199 DVLOG(1) << "UkmService::OnLogUploadComplete";
200 log_upload_in_progress_ = false;
201
202 UMA_HISTOGRAM_SPARSE_SLOWLY("UKM.Upload.ResponseCode", response_code);
203
204 bool upload_succeeded = response_code == 200;
205
206 // Provide boolean for error recovery (allow us to ignore response_code).
207 bool discard_log = false;
208 const size_t log_size_bytes = persisted_logs_.staged_log().length();
209 if (upload_succeeded) {
210 UMA_HISTOGRAM_COUNTS_10000("UKM.LogSize.OnSuccess", log_size_bytes / 1024);
211 } else if (log_size_bytes > kMaxLogRetransmitSize) {
Alexei Svitkine (slow) 2017/01/10 17:37:19 Nit: Wondering if we really need it? I would sugge
Steven Holte 2017/01/10 22:52:44 Done.
212 UMA_HISTOGRAM_COUNTS_1M("UKM.OversizeRejected.LogSize",
213 static_cast<int>(log_size_bytes));
214 discard_log = true;
215 } else if (response_code == 400) {
216 // Bad syntax. Retransmission won't work.
217 discard_log = true;
218 }
219
220 if (upload_succeeded || discard_log) {
221 persisted_logs_.DiscardStagedLog();
222 // Store the updated list to disk now that the removed log is uploaded.
223 persisted_logs_.SerializeLogs();
224 }
225
226 // Error 400 indicates a problem with the log, not with the server, so
227 // don't consider that a sign that the server is in trouble.
228 bool server_is_healthy = upload_succeeded || response_code == 400;
229 scheduler_->UploadFinished(server_is_healthy, !persisted_logs_.empty());
230 }
231
232 } // namespace ukm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698