// Copyright 2014 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/metrics/metrics_log_manager.h" #include <algorithm> #include "base/metrics/histogram.h" #include "base/strings/string_util.h" #include "base/timer/elapsed_timer.h" #include "components/metrics/metrics_log.h" #include "components/metrics/metrics_pref_names.h" namespace metrics { namespace { // The number of "initial" logs to save, and hope to send during a future Chrome // session. Initial logs contain crash stats, and are pretty small. const size_t kInitialLogsPersistLimit = 20; // The number of ongoing logs to save persistently, and hope to // send during a this or future sessions. Note that each log may be pretty // large, as presumably the related "initial" log wasn't sent (probably nothing // was, as the user was probably off-line). As a result, the log probably kept // accumulating while the "initial" log was stalled, and couldn't be sent. As a // result, we don't want to save too many of these mega-logs. // A "standard shutdown" will create a small log, including just the data that // was not yet been transmitted, and that is normal (to have exactly one // ongoing_log_ at startup). const size_t kOngoingLogsPersistLimit = 8; // The number of bytes each of initial and ongoing logs that must be stored. // This ensures that a reasonable amount of history will be stored even if there // is a long series of very small logs. const size_t kStorageByteLimitPerLogType = 300000; } // namespace MetricsLogManager::MetricsLogManager(PrefService* local_state, size_t max_ongoing_log_size) : unsent_logs_loaded_(false), initial_log_queue_(local_state, prefs::kMetricsInitialLogs, prefs::kMetricsInitialLogsOld, kInitialLogsPersistLimit, kStorageByteLimitPerLogType, 0), ongoing_log_queue_(local_state, prefs::kMetricsOngoingLogs, prefs::kMetricsOngoingLogsOld, kOngoingLogsPersistLimit, kStorageByteLimitPerLogType, max_ongoing_log_size) {} MetricsLogManager::~MetricsLogManager() {} void MetricsLogManager::BeginLoggingWithLog(scoped_ptr<MetricsLog> log) { DCHECK(!current_log_); current_log_ = log.Pass(); } void MetricsLogManager::FinishCurrentLog() { DCHECK(current_log_.get()); current_log_->CloseLog(); std::string log_data; current_log_->GetEncodedLog(&log_data); if (!log_data.empty()) StoreLog(log_data, current_log_->log_type()); current_log_.reset(); } void MetricsLogManager::StageNextLogForUpload() { DCHECK(!has_staged_log()); if (!initial_log_queue_.empty()) initial_log_queue_.StageLog(); else ongoing_log_queue_.StageLog(); } void MetricsLogManager::DiscardStagedLog() { DCHECK(has_staged_log()); if (initial_log_queue_.has_staged_log()) initial_log_queue_.DiscardStagedLog(); else ongoing_log_queue_.DiscardStagedLog(); DCHECK(!has_staged_log()); } void MetricsLogManager::DiscardCurrentLog() { current_log_->CloseLog(); current_log_.reset(); } void MetricsLogManager::PauseCurrentLog() { DCHECK(!paused_log_.get()); paused_log_.reset(current_log_.release()); } void MetricsLogManager::ResumePausedLog() { DCHECK(!current_log_.get()); current_log_.reset(paused_log_.release()); } void MetricsLogManager::StoreLog(const std::string& log_data, MetricsLog::LogType log_type) { switch (log_type) { case MetricsLog::INITIAL_STABILITY_LOG: initial_log_queue_.StoreLog(log_data); break; case MetricsLog::ONGOING_LOG: ongoing_log_queue_.StoreLog(log_data); break; } } void MetricsLogManager::StoreStagedLogAsUnsent( PersistedLogs::StoreType store_type) { DCHECK(has_staged_log()); if (initial_log_queue_.has_staged_log()) initial_log_queue_.StoreStagedLogAsUnsent(store_type); else ongoing_log_queue_.StoreStagedLogAsUnsent(store_type); } void MetricsLogManager::DiscardLastProvisionalStore() { // We have at most one provisional store, (since at most one log is being // uploaded at a time), so at least one of these will be a no-op. initial_log_queue_.DiscardLastProvisionalStore(); ongoing_log_queue_.DiscardLastProvisionalStore(); } void MetricsLogManager::PersistUnsentLogs() { DCHECK(unsent_logs_loaded_); if (!unsent_logs_loaded_) return; base::ElapsedTimer timer; initial_log_queue_.SerializeLogs(); ongoing_log_queue_.SerializeLogs(); UMA_HISTOGRAM_TIMES("UMA.StoreLogsTime", timer.Elapsed()); } void MetricsLogManager::LoadPersistedUnsentLogs() { base::ElapsedTimer timer; initial_log_queue_.DeserializeLogs(); ongoing_log_queue_.DeserializeLogs(); UMA_HISTOGRAM_TIMES("UMA.LoadLogsTime", timer.Elapsed()); unsent_logs_loaded_ = true; } } // namespace metrics