| // 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 |