blob: e57dcb96e8cde8faaad2dcdeb65bdc7cace9274c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metrics/debug/structured/structured_metrics_debug_provider.h"
#include <optional>
#include <string_view>
#include <utility>
#include "base/i18n/number_formatting.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "components/metrics/structured/event_validator.h"
#include "components/metrics/structured/project_validator.h"
#include "components/metrics/structured/proto/event_storage.pb.h"
#include "components/metrics/structured/recorder.h"
#include "components/metrics/structured/structured_metrics_service.h"
#include "components/metrics/structured/structured_metrics_validator.h"
#include "third_party/metrics_proto/structured_data.pb.h"
namespace metrics::structured {
namespace {
struct EventInfo {
std::string_view project_name;
std::string_view event_name;
raw_ptr<const EventValidator> event_validator;
// Normalizes the name into an easier to read format as defined in the
// structured.xml.
std::string NormalizeProjectName() const;
std::string NormalizeEventName() const;
};
std::string Normalize(std::string_view value) {
std::string result;
base::ReplaceChars(value, "_", ".", &result);
return result;
}
std::string EventInfo::NormalizeProjectName() const {
return Normalize(project_name);
}
std::string EventInfo::NormalizeEventName() const {
return Normalize(event_name);
}
// Retrieves information about an event that is needed for rendering.
std::optional<EventInfo> GetEventInfo(const StructuredEventProto& proto) {
validator::Validators* validators = validator::Validators::Get();
auto project_name = validators->GetProjectName(proto.project_name_hash());
if (!project_name.has_value()) {
return std::nullopt;
}
// This will not fail.
const auto* project_validator =
validators->GetProjectValidator(*project_name);
CHECK(project_validator);
const auto event_name =
project_validator->GetEventName(proto.event_name_hash());
if (!event_name.has_value()) {
return std::nullopt;
}
// This will not fail.
const auto* event_validator =
project_validator->GetEventValidator(*event_name);
CHECK(event_validator);
return EventInfo{.project_name = *project_name,
.event_name = *event_name,
.event_validator = event_validator};
}
// Creates a dictionary that represents a key-value pair.
base::Value::Dict CreateKeyValue(std::string_view key, base::Value value) {
base::Value::Dict result;
result.Set("key", key);
result.Set("value", std::move(value));
return result;
}
std::optional<base::Value> MetricToValue(
const StructuredEventProto::Metric& metric) {
using Metric = StructuredEventProto::Metric;
switch (metric.value_case()) {
case Metric::kValueHmac:
return base::Value(base::NumberToString(metric.value_hmac()));
case Metric::kValueInt64:
return base::Value(base::NumberToString(metric.value_int64()));
case Metric::kValueString:
return base::Value(metric.value_string());
case Metric::kValueDouble:
return base::Value(metric.value_double());
case Metric::kValueRepeatedInt64: {
base::Value::List list;
for (int value : metric.value_repeated_int64().values()) {
list.Append(value);
}
return base::Value(std::move(list));
}
case Metric::VALUE_NOT_SET:
return std::nullopt;
}
}
// Creates a list of metrics represented by a key-value pair from the metrics of
// an event.
base::Value::List CreateMetricsList(const google::protobuf::RepeatedPtrField<
StructuredEventProto::Metric>& metrics,
const EventValidator* event_validator) {
base::Value::List result;
for (const auto& metric : metrics) {
std::string metric_name =
event_validator
? std::string(event_validator->GetMetricName(metric.name_hash())
.value_or("unknown"))
: base::NumberToString(metric.name_hash());
auto value = MetricToValue(metric);
if (!value.has_value()) {
continue;
}
result.Append(CreateKeyValue(metric_name, std::move(*value)));
}
return result;
}
// Creates an event metadata dictionary from an event.
base::Value::Dict CreateEventMetadataDict(
const StructuredEventProto::EventSequenceMetadata& sequence_metadata) {
base::Value::Dict metadata;
metadata.Set("systemUptimeMs",
base::FormatNumber(sequence_metadata.system_uptime()));
metadata.Set("id", base::NumberToString(sequence_metadata.event_unique_id()));
metadata.Set("resetCounter",
base::NumberToString(sequence_metadata.reset_counter()));
return metadata;
}
// Creates a dictionary from an event.
base::Value::Dict CreateEventDict(const StructuredEventProto& proto) {
base::Value::Dict result;
auto event_info = GetEventInfo(proto);
const EventValidator* event_validator = nullptr;
if (event_info.has_value()) {
event_validator = event_info->event_validator;
result.Set("project", event_info->NormalizeProjectName());
result.Set("event", event_info->NormalizeEventName());
} else {
result.Set("project", base::NumberToString(proto.project_name_hash()));
result.Set("event", base::NumberToString(proto.event_name_hash()));
}
result.Set("metrics", CreateMetricsList(proto.metrics(), event_validator));
if (proto.event_type() == StructuredEventProto::SEQUENCE) {
result.Set("type", "sequence");
result.Set("sequenceMetadata",
CreateEventMetadataDict(proto.event_sequence_metadata()));
} else {
result.Set("type", "metric");
}
return result;
}
} // namespace
StructuredMetricsDebugProvider::StructuredMetricsDebugProvider(
StructuredMetricsService* service)
: service_(service) {
CHECK(service);
LoadRecordedEvents();
service_->recorder()->AddEventsObserver(this);
}
StructuredMetricsDebugProvider::~StructuredMetricsDebugProvider() {
service_->recorder()->RemoveEventsObserver(this);
}
void StructuredMetricsDebugProvider::OnEventRecorded(
const StructuredEventProto& event) {
events_.Append(CreateEventDict(event));
}
void StructuredMetricsDebugProvider::LoadRecordedEvents() {
EventsProto proto;
service_->recorder()->event_storage()->CopyEvents(&proto);
for (const auto& event : proto.events()) {
events_.Append(CreateEventDict(event));
}
}
} // namespace metrics::structured