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

Unified Diff: components/ukm/ukm_service.cc

Issue 2567263003: Basic UkmService implementation (Closed)
Patch Set: Fix small bugs 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 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..30d5911b3c8f7c337eda8598c735dc66ea6d9aca
--- /dev/null
+++ b/components/ukm/ukm_service.cc
@@ -0,0 +1,229 @@
+// 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/strings/string_number_conversions.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 char kMimeType[] = "application/vnd.chrome.ukm";
battre 2017/01/12 08:12:55 I have seen a number of CLs, where this has been e
Steven Holte 2017/01/13 23:15:26 Done.
+
+// 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::GetVariationParamValueByFeature(
+ kUkmFeature, "ServerUrl");
+ if (!server_url.empty())
+ return server_url;
+ return kDefaultServerUrl;
+}
+
+uint64_t LoadOrGenerateClientId(PrefService* pref_service) {
+ uint64_t client_id = pref_service->GetInt64(prefs::kUkmClientId);
+ if (!client_id) {
+ // Generate and store a new client id.
+ while (!client_id)
+ client_id = base::RandUint64();
+ pref_service->SetInt64(prefs::kUkmClientId, client_id);
+ }
+ return client_id;
+}
+
+} // namespace
+
+const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT};
+
+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,
+ kMinPersistedLogs,
+ kMinPersistedBytes,
+ kMaxLogRetransmitSize),
+ initialize_started_(false),
+ log_upload_in_progress_(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 UkmService, and
+ // 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 (!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->RegisterInt64Pref(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_);
+ if (!persisted_logs_.has_staged_log())
+ persisted_logs_.StageLog();
+ // TODO(holte): Handle data usage on cellular, etc.
+ if (!log_uploader_) {
+ log_uploader_ = client_->CreateUploader(
+ GetServerUrl(), kMimeType, base::Bind(&UkmService::OnLogUploadComplete,
+ self_ptr_factory_.GetWeakPtr()));
+ }
+ log_upload_in_progress_ = true;
+
+ const std::string hash =
+ base::HexEncode(persisted_logs_.staged_log_hash().data(),
+ persisted_logs_.staged_log_hash().size());
+ log_uploader_->UploadLog(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 (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