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

Unified Diff: components/ukm/ukm_service.cc

Issue 2567263003: Basic UkmService implementation (Closed)
Patch Set: Updated Created 3 years, 12 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 side-by-side diff with in-line comments
Download patch
Index: components/ukm/ukm_service.cc
diff --git a/components/ukm/ukm_service.cc b/components/ukm/ukm_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bada02f1c9455a72616441bc9c7ce4224bd5be41
--- /dev/null
+++ b/components/ukm/ukm_service.cc
@@ -0,0 +1,241 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ukm/ukm_service.h"
+
+#include <utility>
+
+#include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/time/time.h"
+#include "components/metrics/metrics_log_uploader.h"
+#include "components/metrics/metrics_service_client.h"
+#include "components/metrics/proto/ukm/report.pb.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/ukm/metrics_reporting_scheduler.h"
+#include "components/ukm/persisted_logs_metrics_impl.h"
+#include "components/ukm/ukm_pref_names.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace ukm {
+
+namespace {
+
+const base::Feature kUkmFeature = {
+ "Ukm", base::FEATURE_DISABLED_BY_DEFAULT
+};
+
+const char kMimeType[] = "application/vnd.chrome.ukm";
+
+// Constants for the UKM rollout field trial.
+const char kUkmRolloutFieldTrialName[] = "UkmRollout";
+
+// Constant for the variations parameter name for the server URL
+const char kUkmRolloutServerUrlParam[] = "ServerUrl";
+
+// The UKM server's URL.
+const char kDefaultServerUrl[] = "https://clients4.google.com/ukm";
+
+// The delay, in seconds, after starting recording before doing expensive
+// initialization work.
+const int kInitializationDelaySeconds = 5;
+
+// The number of UKM logs that will be stored in PersistedLogs before logs
+// start being dropped.
+const int kMinPersistedLogs = 8;
+
+// The number of bytes UKM logs that will be stored in PersistedLogs before
+// logs start being dropped.
+// This ensures that a reasonable amount of history will be stored even if there
+// is a long series of very small logs.
+const int kMinPersistedBytes = 300000;
+
+// If an upload fails, and the transmission was over this byte count, then we
+// will discard the log, and not try to retransmit it. We also don't persist
+// the log to the prefs for transmission during the next chrome session if this
+// limit is exceeded.
+const size_t kMaxLogRetransmitSize = 100 * 1024;
+
+std::string GetServerUrl() {
+ std::string server_url = variations::GetVariationParamValue(
+ kUkmRolloutFieldTrialName, kUkmRolloutServerUrlParam);
+ if (!server_url.empty())
+ return server_url;
+ return kDefaultServerUrl;
+}
+
+uint64_t LoadOrGenerateClientId(PrefService* pref_service) {
+ uint64_t client_id = pref_service->GetInteger(prefs::kUkmClientId);
+ if (!client_id) {
+ // Generate and store a new client id.
+ client_id = base::RandUint64();
+ pref_service->SetInteger(prefs::kUkmClientId, client_id);
+ }
+ return client_id;
+}
+
+} // namespace
+
+UkmService::UkmService(
+ PrefService* pref_service,
+ metrics::MetricsServiceClient* client)
+ : pref_service_(pref_service),
+ client_(client),
+ persisted_logs_(std::unique_ptr<ukm::PersistedLogsMetricsImpl>(
+ new ukm::PersistedLogsMetricsImpl()),
+ pref_service,
+ prefs::kUkmPersistedLogs,
+ NULL,
+ kMinPersistedLogs,
+ kMinPersistedBytes,
+ kMaxLogRetransmitSize),
+ initialize_started_(false),
+ self_ptr_factory_(this) {
+ DCHECK(pref_service_);
+ DCHECK(client_);
+
+ persisted_logs_.DeserializeLogs();
+
+ base::Closure rotate_callback =
+ base::Bind(&UkmService::RotateLog,
+ self_ptr_factory_.GetWeakPtr());
+ // MetricsServiceClient outlives MetricsService, and
rkaplow 2017/01/04 17:57:57 UKMService
Steven Holte 2017/01/04 22:47:55 Done.
+ // MetricsReportingScheduler is tied to the lifetime of |this|.
+ const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback =
+ base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval,
+ base::Unretained(client_));
+ scheduler_.reset(new ukm::MetricsReportingScheduler(
+ rotate_callback, get_upload_interval_callback));
+}
+
+UkmService::~UkmService() {}
+
+void UkmService::Initialize() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::Initialize";
+ initialize_started_ = true;
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, base::Bind(&UkmService::StartInitTask,
+ self_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(kInitializationDelaySeconds));
+}
+
+void UkmService::EnableReporting() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::EnableReporting";
+ if (!base::FeatureList::IsEnabled(kUkmFeature))
+ return;
+ if (!initialize_started_)
+ Initialize();
+ scheduler_->Start();
+}
+
+void UkmService::DisableReporting() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::DisableReporting";
+ scheduler_->UploadCancelled();
+ scheduler_->Stop();
+}
+
+// static
+void UkmService::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterIntegerPref(prefs::kUkmClientId, 0);
+ registry->RegisterListPref(prefs::kUkmPersistedLogs);
+}
+
+void UkmService::StartInitTask() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::StartInitTask";
+ client_id_ = LoadOrGenerateClientId(pref_service_);
+ client_->InitializeSystemProfileMetrics(
+ base::Bind(&UkmService::FinishedInitTask,
+ self_ptr_factory_.GetWeakPtr()));
+}
+
+void UkmService::FinishedInitTask() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::FinishedInitTask";
+ scheduler_->InitTaskComplete();
+}
+
+void UkmService::RotateLog() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!log_upload_in_progress_);
+ DVLOG(1) << "UkmService::RotateLog";
+ if (persisted_logs_.empty()) {
+ BuildAndStoreLog();
+ }
+ StartScheduledUpload();
+}
+
+void UkmService::BuildAndStoreLog() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "UkmService::BuildAndStoreLog";
+ Report report;
+ report.set_client_id(client_id_);
+ // TODO(holte): Populate system_profile.
+ // TODO(zhenw): Populate sources.
+ std::string serialized_log;
+ report.SerializeToString(&serialized_log);
+ persisted_logs_.StoreLog(serialized_log);
+}
+
+void UkmService::StartScheduledUpload() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!log_upload_in_progress_);
+ persisted_logs_.StageLog();
+ // TODO (holte): Handle data usage on cellular, etc.
rkaplow 2017/01/04 17:57:57 extra ws
Steven Holte 2017/01/04 22:47:55 Done.
+ if (!log_uploader_) {
+ log_uploader_ = client_->CreateUploader(
+ GetServerUrl(), kMimeType,
+ base::Bind(&UkmService::OnLogUploadComplete,
+ self_ptr_factory_.GetWeakPtr()));
+ }
+ log_upload_in_progress_ = true;
+ log_uploader_->UploadLog(persisted_logs_.staged_log(),
+ persisted_logs_.staged_log_hash());
+}
+
+void UkmService::OnLogUploadComplete(int response_code) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(log_upload_in_progress_);
+ DVLOG(1) << "UkmService::OnLogUploadComplete";
+ log_upload_in_progress_ = false;
+
+ UMA_HISTOGRAM_SPARSE_SLOWLY("UKM.Upload.ResponseCode", response_code);
+
+ bool upload_succeeded = response_code == 200;
+
+ // Provide boolean for error recovery (allow us to ignore response_code).
+ bool discard_log = false;
+ const size_t log_size_bytes = persisted_logs_.staged_log().length();
+ if (upload_succeeded) {
+ UMA_HISTOGRAM_COUNTS_10000("UKM.LogSize.OnSuccess", log_size_bytes / 1024);
+ } else if (log_size_bytes > kMaxLogRetransmitSize) {
+ UMA_HISTOGRAM_COUNTS_1M("UKM.OversizeRejected.LogSize",
+ static_cast<int>(log_size_bytes));
+ discard_log = true;
+ } else if (response_code == 400) {
+ // Bad syntax. Retransmission won't work.
+ discard_log = true;
+ }
+
+ if (upload_succeeded || discard_log) {
+ persisted_logs_.DiscardStagedLog();
+ // Store the updated list to disk now that the removed log is uploaded.
+ persisted_logs_.SerializeLogs();
+ }
+
+ // Error 400 indicates a problem with the log, not with the server, so
+ // don't consider that a sign that the server is in trouble.
+ bool server_is_healthy = upload_succeeded || response_code == 400;
+ scheduler_->UploadFinished(server_is_healthy, !persisted_logs_.empty());
+}
+
+} // namespace ukm

Powered by Google App Engine
This is Rietveld 408576698