blob: b63ec3ed15fdc58c5be7617f1e36e563a075d1b4 [file] [log] [blame]
// 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 <stdint.h>
#include <map>
#include <string>
#include <utility>
#include <vector>
#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<float>& 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<CastEventBuilder> 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<CastEventBuilder> CreateCastEvent(const std::string& name) {
MetricsRecorder* recorder = MetricsRecorder::GetInstance();
if (recorder) {
return recorder->CreateEventBuilder(name);
}
return std::make_unique<DummyEventBuilder>();
}
void RecordCastEvent(const std::string& log_name,
std::unique_ptr<CastEventBuilder> 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<CastEventBuilder>(),
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<Observer>::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<ObserverList>()) {}
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<std::string, int64_t>& 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