Index: ios/chrome/today_extension/today_metrics_logger.mm |
diff --git a/ios/chrome/today_extension/today_metrics_logger.mm b/ios/chrome/today_extension/today_metrics_logger.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..859b9b2e4121b26f54ae794d147b082e7bdd3dd4 |
--- /dev/null |
+++ b/ios/chrome/today_extension/today_metrics_logger.mm |
@@ -0,0 +1,302 @@ |
+// Copyright 2015 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. |
+ |
+#import <UIKit/UIKit.h> |
+ |
+#import "ios/chrome/today_extension/today_metrics_logger.h" |
+ |
+#include "base/base64.h" |
+#include "base/cpu.h" |
+#include "base/mac/bind_objc_block.h" |
+#import "base/mac/scoped_nsobject.h" |
+#include "base/metrics/histogram_base.h" |
+#include "base/metrics/statistics_recorder.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/sys_info.h" |
+#include "components/metrics/metrics_log.h" |
+#include "components/metrics/metrics_log_uploader.h" |
+#include "components/metrics/metrics_pref_names.h" |
+#include "components/metrics/metrics_service_client.h" |
+#include "components/metrics/net/version_utils.h" |
+#include "components/prefs/json_pref_store.h" |
+#include "components/prefs/pref_registry_simple.h" |
+#include "components/prefs/pref_service.h" |
+#include "components/prefs/pref_service_factory.h" |
+#include "components/prefs/value_map_pref_store.h" |
+#include "components/variations/active_field_trials.h" |
+#include "components/version_info/version_info.h" |
+#import "ios/chrome/common/app_group/app_group_constants.h" |
+#import "ios/chrome/common/app_group/app_group_metrics.h" |
+#import "ios/chrome/common/app_group/app_group_metrics_client.h" |
+#include "ios/chrome/common/channel_info.h" |
+ |
+namespace { |
+ |
+// User default key to keep track of the current log session ID. Increased every |
+// time a log is created. This ID must be offset using |
+// app_group::AppGroupSessionID. |
+NSString* const kTodayExtensionMetricsSessionID = @"MetricsSessionID"; |
+ |
+// User default key to the current log serialized. In case of an extension |
+// restart, this log can be written to disk for upload. |
+NSString* const kTodayExtensionMetricsCurrentLog = @"MetricsCurrentLog"; |
+ |
+// Maximum number of event in a log. |
+const int kMaxEventsPerLog = 1000; |
+ |
+// Maximum age of a log. |
+const int kMaxLogLifeTimeInSeconds = 86400; |
+ |
+// A simple implementation of metrics::MetricsServiceClient. |
+// As logs are uploaded by Chrome application, not all methods are needed. |
+// Only the method needed to initialize the metrics logs are implementsd. |
+class TodayMetricsServiceClient : public metrics::MetricsServiceClient { |
+ public: |
+ TodayMetricsServiceClient() {} |
+ metrics::MetricsService* GetMetricsService() override; |
+ void SetMetricsClientId(const std::string& client_id) override; |
+ int32_t GetProduct() override; |
+ std::string GetApplicationLocale() override; |
+ bool GetBrand(std::string* brand_code) override; |
+ metrics::SystemProfileProto::Channel GetChannel() override; |
+ std::string GetVersionString() override; |
+ void OnLogUploadComplete() override; |
+ void InitializeSystemProfileMetrics( |
+ const base::Closure& done_callback) override; |
+ void CollectFinalMetricsForLog(const base::Closure& done_callback) override; |
+ std::unique_ptr<metrics::MetricsLogUploader> CreateUploader( |
+ const base::Callback<void(int)>& on_upload_complete) override; |
+ base::TimeDelta GetStandardUploadInterval() override; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(TodayMetricsServiceClient); |
+}; |
+ |
+class TodayMetricsLog : public metrics::MetricsLog { |
+ public: |
+ // Creates a new today metrics log of the specified type. |
+ // TodayMetricsLog is similar to metrics::MetricsLog but allow serialization |
+ // of open logs. |
+ TodayMetricsLog(const std::string& client_id, |
+ int session_id, |
+ LogType log_type, |
+ TodayMetricsServiceClient* client, |
+ PrefService* local_state); |
+ |
+ // Fills |encoded_log| with the serialized protobuf representation of the |
+ // record. Can be called even on open log. |
+ void GetOpenEncodedLog(std::string* encoded_log) const; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(TodayMetricsLog); |
+}; |
+ |
+metrics::MetricsService* TodayMetricsServiceClient::GetMetricsService() { |
+ NOTREACHED(); |
+ return nullptr; |
+} |
+ |
+void TodayMetricsServiceClient::SetMetricsClientId( |
+ const std::string& client_id) { |
+ NOTREACHED(); |
+} |
+ |
+int32_t TodayMetricsServiceClient::GetProduct() { |
+ return metrics::ChromeUserMetricsExtension::CHROME; |
+} |
+ |
+bool TodayMetricsServiceClient::GetBrand(std::string* brand_code) { |
+ base::scoped_nsobject<NSUserDefaults> shared_defaults( |
+ [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]); |
+ |
+ NSString* ns_brand_code = [shared_defaults |
+ stringForKey:base::SysUTF8ToNSString(app_group::kBrandCode)]; |
+ if (!ns_brand_code) |
+ return false; |
+ |
+ *brand_code = base::SysNSStringToUTF8(ns_brand_code); |
+ return true; |
+} |
+ |
+metrics::SystemProfileProto::Channel TodayMetricsServiceClient::GetChannel() { |
+ return metrics::AsProtobufChannel(::GetChannel()); |
+} |
+ |
+std::string TodayMetricsServiceClient::GetApplicationLocale() { |
+ return base::SysNSStringToUTF8( |
+ [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]); |
+} |
+ |
+std::string TodayMetricsServiceClient::GetVersionString() { |
+ return metrics::GetVersionString(); |
+} |
+ |
+void TodayMetricsServiceClient::OnLogUploadComplete() { |
+ NOTREACHED(); |
+} |
+ |
+void TodayMetricsServiceClient::InitializeSystemProfileMetrics( |
+ const base::Closure& done_callback) { |
+ NOTREACHED(); |
+} |
+ |
+void TodayMetricsServiceClient::CollectFinalMetricsForLog( |
+ const base::Closure& done_callback) { |
+ NOTREACHED(); |
+} |
+ |
+std::unique_ptr<metrics::MetricsLogUploader> |
+TodayMetricsServiceClient::CreateUploader( |
+ const base::Callback<void(int)>& on_upload_complete) { |
+ NOTREACHED(); |
+ return nullptr; |
+} |
+ |
+base::TimeDelta TodayMetricsServiceClient::GetStandardUploadInterval() { |
+ NOTREACHED(); |
+ return base::TimeDelta::FromSeconds(0); |
+} |
+ |
+TodayMetricsLog::TodayMetricsLog(const std::string& client_id, |
+ int session_id, |
+ LogType log_type, |
+ TodayMetricsServiceClient* client, |
+ PrefService* local_state) |
+ : metrics::MetricsLog(client_id, |
+ session_id, |
+ log_type, |
+ client, |
+ local_state) {} |
+ |
+void TodayMetricsLog::GetOpenEncodedLog(std::string* encoded_log) const { |
+ uma_proto()->SerializeToString(encoded_log); |
+} |
+ |
+} // namespace |
+ |
+TodayMetricsLogger* TodayMetricsLogger::GetInstance() { |
+ // |logger| is a singleton that should live as long as the application. |
+ // We do not delete it and it will be deleted when the application dies. |
+ static TodayMetricsLogger* logger = new TodayMetricsLogger(); |
+ return logger; |
+} |
+ |
+void TodayMetricsLogger::RecordUserAction(base::UserMetricsAction action) { |
+ if (!log_ && !CreateNewLog()) { |
+ return; |
+ } |
+ log_->RecordUserAction(action.str_); |
+ PersistLogs(); |
+} |
+ |
+void TodayMetricsLogger::PersistLogs() { |
+ histogram_snapshot_manager_.PrepareDeltas( |
+ base::StatisticsRecorder::begin(false), base::StatisticsRecorder::end(), |
+ base::Histogram::kNoFlags, base::Histogram::kNoFlags); |
+ std::string encoded_log; |
+ log_->GetOpenEncodedLog(&encoded_log); |
+ NSData* ns_encoded_log = |
+ [NSData dataWithBytes:encoded_log.c_str() length:encoded_log.length()]; |
+ [[NSUserDefaults standardUserDefaults] |
+ setObject:ns_encoded_log |
+ forKey:kTodayExtensionMetricsCurrentLog]; |
+ if (log_->num_events() >= kMaxEventsPerLog || |
+ (base::TimeTicks::Now() - log_->creation_time()).InSeconds() >= |
+ kMaxLogLifeTimeInSeconds) { |
+ CreateNewLog(); |
+ } |
+} |
+ |
+bool TodayMetricsLogger::CreateNewLog() { |
+ id previous_log = [[NSUserDefaults standardUserDefaults] |
+ dataForKey:kTodayExtensionMetricsCurrentLog]; |
+ if (previous_log) { |
+ app_group::client_app::AddPendingLog(previous_log, |
+ app_group::APP_GROUP_TODAY_EXTENSION); |
+ thread_pool_->PostTask( |
+ FROM_HERE, base::Bind(&app_group::client_app::CleanOldPendingLogs)); |
+ } |
+ |
+ base::scoped_nsobject<NSUserDefaults> shared_defaults( |
+ [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]); |
+ |
+ NSString* client_id = [shared_defaults |
+ stringForKey:base::SysUTF8ToNSString(app_group::kChromeAppClientID)]; |
+ NSString* enabled_date = [shared_defaults |
+ stringForKey:base::SysUTF8ToNSString(app_group::kUserMetricsEnabledDate)]; |
+ |
+ NSString* install_date = [shared_defaults |
+ stringForKey:base::SysUTF8ToNSString(app_group::kInstallDate)]; |
+ |
+ if (!client_id || !enabled_date || !install_date) { |
+ return false; |
+ } |
+ |
+ int session_id = [[NSUserDefaults standardUserDefaults] |
+ integerForKey:kTodayExtensionMetricsSessionID]; |
+ [[NSUserDefaults standardUserDefaults] |
+ setInteger:session_id + 1 |
+ forKey:kTodayExtensionMetricsSessionID]; |
+ session_id = app_group::AppGroupSessionID( |
+ session_id, app_group::APP_GROUP_TODAY_EXTENSION); |
+ log_.reset(new TodayMetricsLog(base::SysNSStringToUTF8(client_id), session_id, |
+ metrics::MetricsLog::ONGOING_LOG, |
+ metrics_service_client_.get(), |
+ pref_service_.get())); |
+ |
+ log_->RecordEnvironment(std::vector<metrics::MetricsProvider*>(), |
+ std::vector<variations::ActiveGroupId>(), |
+ [install_date longLongValue], |
+ [enabled_date longLongValue]); |
+ |
+ return true; |
+} |
+ |
+TodayMetricsLogger::TodayMetricsLogger() |
+ : pref_registry_(new PrefRegistrySimple()), |
+ thread_pool_( |
+ new base::SequencedWorkerPool(2, |
+ "LoggerPool", |
+ base::TaskPriority::BACKGROUND)), |
+ metrics_service_client_(new TodayMetricsServiceClient()), |
+ histogram_snapshot_manager_(this) { |
+ metrics::MetricsLog::RegisterPrefs(pref_registry_.get()); |
+ |
+ NSString* url = [[NSSearchPathForDirectoriesInDomains( |
+ NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] |
+ stringByAppendingPathComponent:@"Application Support/localstate"]; |
+ base::FilePath path(base::SysNSStringToUTF8(url)); |
+ sequenced_task_runner_ = |
+ JsonPrefStore::GetTaskRunnerForFile(path, thread_pool_.get()); |
+ PrefServiceFactory factory; |
+ factory.set_extension_prefs(value_map_prefs_.get()); |
+ factory.SetUserPrefsFile(path, sequenced_task_runner_.get()); |
+ pref_service_ = factory.Create(pref_registry_.get()); |
+ base::StatisticsRecorder::Initialize(); |
+} |
+ |
+TodayMetricsLogger::~TodayMetricsLogger() {} |
+ |
+void TodayMetricsLogger::RecordDelta(const base::HistogramBase& histogram, |
+ const base::HistogramSamples& snapshot) { |
+ log_->RecordHistogramDelta(histogram.histogram_name(), snapshot); |
+} |
+ |
+void TodayMetricsLogger::InconsistencyDetected( |
+ base::HistogramBase::Inconsistency problem) { |
+ UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowser", problem, |
+ base::HistogramBase::NEVER_EXCEEDED_VALUE); |
+} |
+ |
+void TodayMetricsLogger::UniqueInconsistencyDetected( |
+ base::HistogramBase::Inconsistency problem) { |
+ UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowserUnique", problem, |
+ base::HistogramBase::NEVER_EXCEEDED_VALUE); |
+} |
+ |
+void TodayMetricsLogger::InconsistencyDetectedInLoggedCount(int amount) { |
+ UMA_HISTOGRAM_COUNTS("Histogram.InconsistentSnapshotBrowser", |
+ std::abs(amount)); |
+} |