chromium/chromecast/metrics/metrics_recorder.cc

// Copyright 2021 The Chromium Authors
// 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/check.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.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/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& SetAoghStandardAgentId(
      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_IN_MIGRATION();
    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