// Copyright 2021 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 "chromecast/metrics/metrics_recorder.h" #include #include #include #include #include #include "base/bind.h" #include "base/callback_helpers.h" #include "base/check.h" #include "base/containers/flat_map.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/notreached.h" #include "base/observer_list.h" #include "base/synchronization/lock.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "chromecast/metrics/cast_event_builder.h" #include "net/base/ip_address.h" namespace chromecast { namespace { bool IsLogOn(int verbose_log_level) { // TODO(b/135640848): Determine a better way to log metrics for // Fuchsia, without producing logspam during development. return -verbose_log_level >= logging::GetMinLogLevel() || (DCHECK_IS_ON() && VLOG_IS_ON(verbose_log_level)); } // A no-op dummy event builder, used when MetricsRecorder is nullptr. class DummyEventBuilder : public CastEventBuilder { public: // receiver::CastEventBuilder implementation std::string GetName() override { return ""; } CastEventBuilder& SetName(const std::string& name) override { return *this; } CastEventBuilder& SetTime(const base::TimeTicks& time) override { return *this; } CastEventBuilder& SetTimezoneId(const std::string& timezone_id) override { return *this; } CastEventBuilder& SetAppId(const std::string& app_id) override { return *this; } CastEventBuilder& SetRemoteAppId(const std::string& remote_app_id) override { return *this; } CastEventBuilder& SetSessionId(const std::string& session_id) override { return *this; } CastEventBuilder& SetSdkVersion(const std::string& sdk_version) override { return *this; } CastEventBuilder& SetMplVersion(const std::string& mpl_version) override { return *this; } CastEventBuilder& SetConnectionInfo( const std::string& transport_connection_id, const std::string& virtual_connection_id) override { return *this; } CastEventBuilder& SetGroupUuid(const std::string& group_uuid) override { return *this; } CastEventBuilder& SetExtraValue(int64_t extra_value) override { return *this; } CastEventBuilder& SetConversationKey( const std::string& conversation_key) override { return *this; } CastEventBuilder& SetRequestId(int32_t request_id) override { return *this; } CastEventBuilder& SetEventId(const std::string& event_id) override { return *this; } CastEventBuilder& SetAoghRequestId(const std::string& request_id) override { return *this; } CastEventBuilder& SetAoghLocalDeviceId(int64_t local_id) override { return *this; } CastEventBuilder& SetAoghAgentId(const std::string& agent_id) override { return *this; } CastEventBuilder& SetUiVersion(const std::string& ui_version) override { return *this; } CastEventBuilder& SetAuditReport(const std::string& audit_report) override { return *this; } CastEventBuilder& SetDuoCoreVersion(int64_t version) override { return *this; } CastEventBuilder& SetHotwordModelId(const std::string& model_id) override { return *this; } CastEventBuilder& SetDiscoveryAppSubtype(const std::string& app_id) override { return *this; } CastEventBuilder& SetDiscoveryNamespaceSubtype( const std::string& namespace_hash) override { return *this; } CastEventBuilder& SetDiscoverySender( const net::IPAddressBytes& sender_ip) override { return *this; } CastEventBuilder& SetDiscoveryUnicastFlag(bool uses_unicast) override { return *this; } CastEventBuilder& SetFeatureVector( const std::vector& features) override { return *this; } CastEventBuilder& AddMetadata(const std::string& name, int64_t value) override { return *this; } CastEventBuilder& SetLaunchFrom(LaunchFrom launch_from) override { return *this; } CastEventBuilder& MergeFrom( const ::metrics::CastLogsProto_CastEventProto* event_proto) override { return *this; } ::metrics::CastLogsProto_CastEventProto* Build() override { NOTREACHED(); return nullptr; } }; MetricsRecorder* g_instance = nullptr; } // namespace void RecordEventWithLogPrefix(const std::string& action, std::unique_ptr event_builder, int verbose_log_level, const std::string& log_prefix) { MetricsRecorder* recorder = MetricsRecorder::GetInstance(); if (recorder && event_builder) { recorder->RecordCastEvent(std::move(event_builder)); } if (IsLogOn(verbose_log_level)) { VLOG_STREAM(verbose_log_level) << log_prefix << action; } } std::unique_ptr CreateCastEvent(const std::string& name) { MetricsRecorder* recorder = MetricsRecorder::GetInstance(); if (recorder) { return recorder->CreateEventBuilder(name); } return std::make_unique(); } void RecordCastEvent(const std::string& log_name, std::unique_ptr event_builder, int verbose_log_level) { RecordEventWithLogPrefix(log_name, std::move(event_builder), verbose_log_level, "cast event: "); } void RecordAction(const std::string& action, int verbose_log_level) { RecordEventWithLogPrefix(action, CreateCastEvent(action), verbose_log_level, "Record action: "); } void LogAction(const std::string& action, int verbose_log_level) { RecordEventWithLogPrefix(action, std::unique_ptr(), verbose_log_level, "Log action: "); } void RecordHistogramTime(const std::string& name, int sample, int min, int max, int num_buckets, int verbose_log_level) { MetricsRecorder* recorder = MetricsRecorder::GetInstance(); if (recorder) { recorder->RecordHistogramTime(name, sample, min, max, num_buckets); } if (IsLogOn(verbose_log_level)) { VLOG_STREAM(verbose_log_level) << "Time histogram: " << name << ", sample=" << sample << ", max=" << max << ", min=" << min << ", num_buckets=" << num_buckets; } } void RecordHistogramCount(const std::string& name, int sample, int min, int max, int num_buckets, int verbose_log_level) { MetricsRecorder* recorder = MetricsRecorder::GetInstance(); if (recorder) { recorder->RecordHistogramCount(name, sample, min, max, num_buckets); } if (IsLogOn(verbose_log_level)) { VLOG_STREAM(verbose_log_level) << "Count histogram: " << name << ", sample=" << sample << ", max=" << max << ", min=" << min << ", num_buckets=" << num_buckets; } } void RecordHistogramEnum(const std::string& name, int sample, int boundary, int verbose_log_level) { MetricsRecorder* recorder = MetricsRecorder::GetInstance(); if (recorder) { recorder->RecordHistogramEnum(name, sample, boundary); } if (IsLogOn(verbose_log_level)) { VLOG_STREAM(verbose_log_level) << "Count histogram: " << name << ", sample=" << sample << ", boundary=" << boundary; } } struct MetricsRecorder::ObserverList { base::ObserverList::Unchecked list; }; // static void MetricsRecorder::SetInstance(MetricsRecorder* recorder) { g_instance = recorder; } // static MetricsRecorder* MetricsRecorder::GetInstance() { return g_instance; } MetricsRecorder::MetricsRecorder() : observer_list_(std::make_unique()) {} MetricsRecorder::~MetricsRecorder() = default; void MetricsRecorder::NotifyOnPreUpload() { for (auto& o : observer_list_->list) o.OnPreUpload(); } void MetricsRecorder::AddObserver(Observer* o) { DCHECK(o); observer_list_->list.AddObserver(o); } void MetricsRecorder::RemoveObserver(Observer* o) { DCHECK(o); observer_list_->list.RemoveObserver(o); } void RecordCastEvent(const std::string& event, bool has_extra_value, int64_t value, MetricsRecorder* metrics_recorder) { DCHECK(metrics_recorder); auto event_builder = metrics_recorder->CreateEventBuilder(event); if (has_extra_value) { event_builder->SetExtraValue(value); } metrics_recorder->RecordCastEvent(std::move(event_builder)); } void RecordCastEventWithMetadata( const std::string& event, const base::flat_map& settings_map, MetricsRecorder* metrics_recorder) { DCHECK(metrics_recorder); auto event_builder = metrics_recorder->CreateEventBuilder(event); for (const auto& kv_pair : settings_map) { event_builder->AddMetadata(kv_pair.first, kv_pair.second); } metrics_recorder->RecordCastEvent(std::move(event_builder)); } } // namespace chromecast