Chromium Code Reviews| Index: components/metrics/reporting_service.cc |
| diff --git a/components/metrics/reporting_service.cc b/components/metrics/reporting_service.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..db0eaaadf33a5c6ba99c6554ab00bb36090cbe7b |
| --- /dev/null |
| +++ b/components/metrics/reporting_service.cc |
| @@ -0,0 +1,193 @@ |
| +// 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. |
| + |
| +// ReportingService handles uploading serialized logs to a server. |
| + |
| +#include "components/metrics/reporting_service.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "components/metrics/data_use_tracker.h" |
| +#include "components/metrics/log_store.h" |
| +#include "components/metrics/metrics_log_uploader.h" |
| +#include "components/metrics/metrics_service_client.h" |
| +#include "components/metrics/metrics_upload_scheduler.h" |
| + |
| +namespace metrics { |
| + |
| +// static |
| +void ReportingService::RegisterPrefs(PrefRegistrySimple* registry) { |
| + DataUseTracker::RegisterPrefs(registry); |
| +} |
| + |
| +ReportingService::ReportingService(MetricsServiceClient* client, |
| + PrefService* local_state, |
| + size_t max_retransmit_size) |
| + : client_(client), |
| + max_retransmit_size_(max_retransmit_size), |
| + reporting_active_(false), |
| + log_upload_in_progress_(false), |
| + data_use_tracker_(DataUseTracker::Create(local_state)), |
| + self_ptr_factory_(this) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(client_); |
| + DCHECK(local_state); |
| +} |
| + |
| +ReportingService::~ReportingService() { |
| + DisableReporting(); |
| +} |
| + |
| +void ReportingService::Initialize() { |
| + log_store()->LoadPersistedUnsentLogs(); |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + base::Closure send_next_log_callback = base::Bind( |
| + &ReportingService::SendNextLog, self_ptr_factory_.GetWeakPtr()); |
| + upload_scheduler_.reset(new MetricsUploadScheduler(send_next_log_callback)); |
| +} |
| + |
| +void ReportingService::Start() { |
| + if (reporting_active_) |
| + upload_scheduler_->Start(); |
| +} |
| + |
| +void ReportingService::Stop() { |
| + if (upload_scheduler_) |
| + upload_scheduler_->Stop(); |
| +} |
| + |
| +void ReportingService::EnableReporting() { |
| + if (reporting_active_) |
| + return; |
| + reporting_active_ = true; |
| + Start(); |
| +} |
| + |
| +void ReportingService::DisableReporting() { |
| + reporting_active_ = false; |
| + Stop(); |
| +} |
| + |
| +bool ReportingService::reporting_active() const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
|
Alexei Svitkine (slow)
2017/03/01 16:06:29
Nit: Add these to all the other functions.
Steven Holte
2017/03/04 01:35:51
Done.
|
| + return reporting_active_; |
| +} |
| + |
| +void ReportingService::UpdateMetricsUsagePrefs(const std::string& service_name, |
| + int message_size, |
| + bool is_cellular) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + if (data_use_tracker_) { |
| + data_use_tracker_->UpdateMetricsUsagePrefs(service_name, message_size, |
| + is_cellular); |
| + } |
| +} |
| + |
| +//------------------------------------------------------------------------------ |
| +// private methods |
| +//------------------------------------------------------------------------------ |
| + |
| +void ReportingService::SendNextLog() { |
| + DVLOG(1) << "SendNextLog"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + if (!last_upload_finish_time_.is_null()) { |
| + LogActualUploadInterval(base::TimeTicks::Now() - last_upload_finish_time_); |
| + last_upload_finish_time_ = base::TimeTicks(); |
| + } |
| + if (!reporting_active()) { |
| + upload_scheduler_->StopAndUploadCancelled(); |
| + return; |
| + } |
| + if (!log_store()->has_unsent_logs()) { |
| + // Should only get here if serializing the log failed somehow. |
| + upload_scheduler_->Stop(); |
| + // Reset backoff interval |
| + upload_scheduler_->UploadFinished(true); |
| + return; |
| + } |
| + if (!log_store()->has_staged_log()) |
| + log_store()->StageNextLog(); |
| + |
| + // Proceed to stage the log for upload if log size satisfies cellular log |
| + // upload constrains. |
| + bool upload_canceled = false; |
| + bool is_cellular_logic = client_->IsUMACellularUploadLogicEnabled(); |
| + if (is_cellular_logic && data_use_tracker_ && |
| + !data_use_tracker_->ShouldUploadLogOnCellular( |
| + log_store()->staged_log_hash().size())) { |
| + upload_scheduler_->UploadOverDataUsageCap(); |
| + upload_canceled = true; |
| + } else { |
| + SendStagedLog(); |
| + } |
| + if (is_cellular_logic) { |
| + LogCellularConstraint(upload_canceled); |
| + } |
| +} |
| + |
| +void ReportingService::SendStagedLog() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(log_store()->has_staged_log()); |
| + if (!log_store()->has_staged_log()) |
| + return; |
| + |
| + DCHECK(!log_upload_in_progress_); |
| + log_upload_in_progress_ = true; |
| + |
| + if (!log_uploader_) { |
| + log_uploader_ = client_->CreateUploader( |
| + upload_url(), upload_mime_type(), service_type(), |
| + base::Bind(&ReportingService::OnLogUploadComplete, |
| + self_ptr_factory_.GetWeakPtr())); |
| + } |
| + |
| + const std::string hash = |
| + base::HexEncode(log_store()->staged_log_hash().data(), |
| + log_store()->staged_log_hash().size()); |
| + log_uploader_->UploadLog(log_store()->staged_log(), hash); |
| +} |
| + |
| +void ReportingService::OnLogUploadComplete(int response_code) { |
| + DVLOG(1) << "OnLogUploadComplete:" << response_code; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(log_upload_in_progress_); |
| + log_upload_in_progress_ = false; |
| + |
| + // Log a histogram to track response success vs. failure rates. |
| + LogResponseCode(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 = log_store()->staged_log().length(); |
| + if (upload_succeeded) { |
| + LogSuccess(log_size); |
| + } else if (log_size > max_retransmit_size_) { |
| + LogLargeRejection(log_size); |
| + discard_log = true; |
| + } else if (response_code == 400) { |
| + // Bad syntax. Retransmission won't work. |
| + discard_log = true; |
| + } |
| + |
| + if (upload_succeeded || discard_log) { |
| + log_store()->DiscardStagedLog(); |
| + // Store the updated list to disk now that the removed log is uploaded. |
| + log_store()->PersistUnsentLogs(); |
| + } |
| + |
| + // 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; |
| + if (!log_store()->has_unsent_logs()) { |
| + DVLOG(1) << "Stopping upload_scheduler_."; |
| + upload_scheduler_->Stop(); |
| + } |
| + upload_scheduler_->UploadFinished(server_is_healthy); |
| +} |
| + |
| +} // namespace metrics |