summaryrefslogtreecommitdiffstats
path: root/chrome/common/metrics_log_manager.cc
diff options
context:
space:
mode:
authorstuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-29 15:18:10 +0000
committerstuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-29 15:18:10 +0000
commitcac267c7b19dbe68dd36a2f4d206e1fa9f530048 (patch)
tree2dc638aefc766e5d2d279f5b1e22928a875dcb55 /chrome/common/metrics_log_manager.cc
parent57c9ae399f80f00b533aa92c9eafeee748aa3b1c (diff)
downloadchromium_src-cac267c7b19dbe68dd36a2f4d206e1fa9f530048.zip
chromium_src-cac267c7b19dbe68dd36a2f4d206e1fa9f530048.tar.gz
chromium_src-cac267c7b19dbe68dd36a2f4d206e1fa9f530048.tar.bz2
Factor a log management helper out of MetricsService
This factors the details of the log transitions between current, staged (previously called pending; renamed for clarity), and unsent (i.e., to-be-stored) log, and all the encoding/compression involved in those transitions, into a helper class: MetricsLogManager. Also simplifies the state machine slightly by combining two states (SEND_OLD_INITIAL_LOGS and SENDING_OLD_LOGS) that behaved the same, moving the logic for deciding which log to send first into the new helper so that the behavior of prioritizing initial logs is unchanged). In order to allow this to continue to be shared with ChromeFrame, the serialization details are factored into a secondary helper; this isn't necessarily ideal, but it does have the benefit of making unit-testing the components easier. This is primarily to facilitate a second change that will add more cases where log serialization/deserialization happens, but even on its own it should be helpful since it moves a discrete chunk out of the overly-large MetricsService+MetricsServiceBase, increasing testability, readability, and maintainability. BUG=None TEST=Metrics should continue to work. Review URL: http://codereview.chromium.org/7237057 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@103280 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/metrics_log_manager.cc')
-rw-r--r--chrome/common/metrics_log_manager.cc162
1 files changed, 162 insertions, 0 deletions
diff --git a/chrome/common/metrics_log_manager.cc b/chrome/common/metrics_log_manager.cc
new file mode 100644
index 0000000..572e62a
--- /dev/null
+++ b/chrome/common/metrics_log_manager.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 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 "chrome/common/metrics_log_manager.h"
+
+#if defined(USE_SYSTEM_LIBBZ2)
+#include <bzlib.h>
+#else
+#include "third_party/bzip2/bzlib.h"
+#endif
+
+#include "base/metrics/histogram.h"
+#include "base/string_util.h"
+#include "chrome/common/metrics_helpers.h"
+
+MetricsLogManager::MetricsLogManager() : max_ongoing_log_store_size_(0) {}
+
+MetricsLogManager::~MetricsLogManager() {}
+
+void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log) {
+ DCHECK(!current_log_.get());
+ current_log_.reset(log);
+}
+
+void MetricsLogManager::StageCurrentLogForUpload() {
+ DCHECK(current_log_.get());
+ current_log_->CloseLog();
+ staged_log_.reset(current_log_.release());
+ CompressStagedLog();
+}
+
+bool MetricsLogManager::has_staged_log() const {
+ return staged_log_.get() || !compressed_staged_log_text_.empty();
+}
+
+void MetricsLogManager::DiscardStagedLog() {
+ staged_log_.reset();
+ compressed_staged_log_text_.clear();
+}
+
+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::StoreStagedLogAsUnsent(LogType log_type) {
+ DCHECK(staged_log_.get());
+ // If compressing the log failed, there's nothing to store.
+ if (compressed_staged_log_text_.empty())
+ return;
+
+ if (log_type == INITIAL_LOG) {
+ unsent_initial_logs_.push_back(compressed_staged_log_text_);
+ } else {
+ // If it's too large, just note that and discard it.
+ if (max_ongoing_log_store_size_ &&
+ compressed_staged_log_text_.length() > max_ongoing_log_store_size_) {
+ UMA_HISTOGRAM_COUNTS(
+ "UMA.Large Accumulated Log Not Persisted",
+ static_cast<int>(compressed_staged_log_text_.length()));
+ } else {
+ unsent_ongoing_logs_.push_back(compressed_staged_log_text_);
+ }
+ }
+ DiscardStagedLog();
+}
+
+void MetricsLogManager::StageNextStoredLogForUpload() {
+ // Prioritize initial logs for uploading.
+ std::vector<std::string>* source_list = unsent_initial_logs_.empty() ?
+ &unsent_ongoing_logs_ : &unsent_initial_logs_;
+ DCHECK(!source_list->empty());
+ DCHECK(compressed_staged_log_text_.empty());
+ compressed_staged_log_text_ = source_list->back();
+ source_list->pop_back();
+}
+
+void MetricsLogManager::PersistUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+ log_serializer_->SerializeLogs(unsent_initial_logs_, INITIAL_LOG);
+ log_serializer_->SerializeLogs(unsent_ongoing_logs_, ONGOING_LOG);
+}
+
+void MetricsLogManager::LoadPersistedUnsentLogs() {
+ DCHECK(log_serializer_.get());
+ if (!log_serializer_.get())
+ return;
+ log_serializer_->DeserializeLogs(INITIAL_LOG, &unsent_initial_logs_);
+ log_serializer_->DeserializeLogs(ONGOING_LOG, &unsent_ongoing_logs_);
+}
+
+void MetricsLogManager::CompressStagedLog() {
+ int text_size = staged_log_->GetEncodedLogSize();
+ std::string staged_log_text;
+ // Leave room for the NULL terminator.
+ staged_log_->GetEncodedLog(WriteInto(&staged_log_text, text_size + 1),
+ text_size);
+
+ bool success = Bzip2Compress(staged_log_text, &compressed_staged_log_text_);
+ if (success) {
+ // Allow security-conscious users to see all metrics logs that we send.
+ VLOG(1) << "METRICS LOG: " << staged_log_text;
+ } else {
+ LOG(DFATAL) << "Failed to compress log for transmission.";
+ }
+}
+
+// static
+// This implementation is based on the Firefox MetricsService implementation.
+bool MetricsLogManager::Bzip2Compress(const std::string& input,
+ std::string* output) {
+ bz_stream stream = {0};
+ // As long as our input is smaller than the bzip2 block size, we should get
+ // the best compression. For example, if your input was 250k, using a block
+ // size of 300k or 500k should result in the same compression ratio. Since
+ // our data should be under 100k, using the minimum block size of 100k should
+ // allocate less temporary memory, but result in the same compression ratio.
+ int result = BZ2_bzCompressInit(&stream,
+ 1, // 100k (min) block size
+ 0, // quiet
+ 0); // default "work factor"
+ if (result != BZ_OK) { // out of memory?
+ return false;
+ }
+
+ output->clear();
+
+ stream.next_in = const_cast<char*>(input.data());
+ stream.avail_in = static_cast<int>(input.size());
+ // NOTE: we don't need a BZ_RUN phase since our input buffer contains
+ // the entire input
+ do {
+ output->resize(output->size() + 1024);
+ stream.next_out = &((*output)[stream.total_out_lo32]);
+ stream.avail_out = static_cast<int>(output->size()) - stream.total_out_lo32;
+ result = BZ2_bzCompress(&stream, BZ_FINISH);
+ } while (result == BZ_FINISH_OK);
+ if (result != BZ_STREAM_END) { // unknown failure?
+ output->clear();
+ // TODO(jar): See if it would be better to do a CHECK() here.
+ return false;
+ }
+ result = BZ2_bzCompressEnd(&stream);
+ DCHECK(result == BZ_OK);
+
+ output->resize(stream.total_out_lo32);
+
+ return true;
+}