1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/debug/structured/structured_metrics_debug_provider.h"
6
7 #include <optional>
8 #include <string_view>
9 #include <utility>
10
11 #include "base/i18n/number_formatting.h"
12 #include "base/logging.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/strings/string_util.h"
15 #include "components/metrics/structured/event_validator.h"
16 #include "components/metrics/structured/project_validator.h"
17 #include "components/metrics/structured/proto/event_storage.pb.h"
18 #include "components/metrics/structured/recorder.h"
19 #include "components/metrics/structured/structured_metrics_service.h"
20 #include "components/metrics/structured/structured_metrics_validator.h"
21 #include "third_party/metrics_proto/structured_data.pb.h"
22
23 namespace metrics::structured {
24 namespace {
25
26 struct EventInfo {
27 std::string_view project_name;
28 std::string_view event_name;
29 raw_ptr<const EventValidator> event_validator;
30
31 // Normalizes the name into an easier to read format as defined in the
32 // structured.xml.
33 std::string NormalizeProjectName() const;
34 std::string NormalizeEventName() const;
35 };
36
Normalize(std::string_view value)37 std::string Normalize(std::string_view value) {
38 std::string result;
39 base::ReplaceChars(value, "_", ".", &result);
40 return result;
41 }
42
NormalizeProjectName() const43 std::string EventInfo::NormalizeProjectName() const {
44 return Normalize(project_name);
45 }
46
NormalizeEventName() const47 std::string EventInfo::NormalizeEventName() const {
48 return Normalize(event_name);
49 }
50
51 // Retrieves information about an event that is needed for rendering.
GetEventInfo(const StructuredEventProto & proto)52 std::optional<EventInfo> GetEventInfo(const StructuredEventProto& proto) {
53 validator::Validators* validators = validator::Validators::Get();
54 auto project_name = validators->GetProjectName(proto.project_name_hash());
55 if (!project_name.has_value()) {
56 return std::nullopt;
57 }
58
59 // This will not fail.
60 const auto* project_validator =
61 validators->GetProjectValidator(*project_name);
62 CHECK(project_validator);
63
64 const auto event_name =
65 project_validator->GetEventName(proto.event_name_hash());
66 if (!event_name.has_value()) {
67 return std::nullopt;
68 }
69
70 // This will not fail.
71 const auto* event_validator =
72 project_validator->GetEventValidator(*event_name);
73 CHECK(event_validator);
74
75 return EventInfo{.project_name = *project_name,
76 .event_name = *event_name,
77 .event_validator = event_validator};
78 }
79
80 // Creates a dictionary that represents a key-value pair.
CreateKeyValue(std::string_view key,base::Value value)81 base::Value::Dict CreateKeyValue(std::string_view key, base::Value value) {
82 base::Value::Dict result;
83 result.Set("key", key);
84 result.Set("value", std::move(value));
85 return result;
86 }
87
MetricToValue(const StructuredEventProto::Metric & metric)88 std::optional<base::Value> MetricToValue(
89 const StructuredEventProto::Metric& metric) {
90 using Metric = StructuredEventProto::Metric;
91 switch (metric.value_case()) {
92 case Metric::kValueHmac:
93 return base::Value(base::NumberToString(metric.value_hmac()));
94 case Metric::kValueInt64:
95 return base::Value(base::NumberToString(metric.value_int64()));
96 case Metric::kValueString:
97 return base::Value(metric.value_string());
98 case Metric::kValueDouble:
99 return base::Value(metric.value_double());
100 case Metric::kValueRepeatedInt64: {
101 base::Value::List list;
102 for (int value : metric.value_repeated_int64().values()) {
103 list.Append(value);
104 }
105 return base::Value(std::move(list));
106 }
107 case Metric::VALUE_NOT_SET:
108 return std::nullopt;
109 }
110 }
111
112 // Creates a list of metrics represented by a key-value pair from the metrics of
113 // an event.
CreateMetricsList(const google::protobuf::RepeatedPtrField<StructuredEventProto::Metric> & metrics,const EventValidator * event_validator)114 base::Value::List CreateMetricsList(const google::protobuf::RepeatedPtrField<
115 StructuredEventProto::Metric>& metrics,
116 const EventValidator* event_validator) {
117 base::Value::List result;
118 for (const auto& metric : metrics) {
119 std::string metric_name =
120 event_validator
121 ? std::string(event_validator->GetMetricName(metric.name_hash())
122 .value_or("unknown"))
123 : base::NumberToString(metric.name_hash());
124 auto value = MetricToValue(metric);
125 if (!value.has_value()) {
126 continue;
127 }
128 result.Append(CreateKeyValue(metric_name, std::move(*value)));
129 }
130 return result;
131 }
132
133 // Creates an event metadata dictionary from an event.
CreateEventMetadataDict(const StructuredEventProto::EventSequenceMetadata & sequence_metadata)134 base::Value::Dict CreateEventMetadataDict(
135 const StructuredEventProto::EventSequenceMetadata& sequence_metadata) {
136 base::Value::Dict metadata;
137 metadata.Set("systemUptimeMs",
138 base::FormatNumber(sequence_metadata.system_uptime()));
139 metadata.Set("id", base::NumberToString(sequence_metadata.event_unique_id()));
140 metadata.Set("resetCounter",
141 base::NumberToString(sequence_metadata.reset_counter()));
142 return metadata;
143 }
144
145 // Creates a dictionary from an event.
CreateEventDict(const StructuredEventProto & proto)146 base::Value::Dict CreateEventDict(const StructuredEventProto& proto) {
147 base::Value::Dict result;
148
149 auto event_info = GetEventInfo(proto);
150 const EventValidator* event_validator = nullptr;
151
152 if (event_info.has_value()) {
153 event_validator = event_info->event_validator;
154 result.Set("project", event_info->NormalizeProjectName());
155 result.Set("event", event_info->NormalizeEventName());
156 } else {
157 result.Set("project", base::NumberToString(proto.project_name_hash()));
158 result.Set("event", base::NumberToString(proto.event_name_hash()));
159 }
160
161 result.Set("metrics", CreateMetricsList(proto.metrics(), event_validator));
162
163 if (proto.event_type() == StructuredEventProto::SEQUENCE) {
164 result.Set("type", "sequence");
165 result.Set("sequenceMetadata",
166 CreateEventMetadataDict(proto.event_sequence_metadata()));
167 } else {
168 result.Set("type", "metric");
169 }
170
171 return result;
172 }
173
174 } // namespace
175
StructuredMetricsDebugProvider(StructuredMetricsService * service)176 StructuredMetricsDebugProvider::StructuredMetricsDebugProvider(
177 StructuredMetricsService* service)
178 : service_(service) {
179 CHECK(service);
180 LoadRecordedEvents();
181 service_->recorder()->AddEventsObserver(this);
182 }
183
~StructuredMetricsDebugProvider()184 StructuredMetricsDebugProvider::~StructuredMetricsDebugProvider() {
185 service_->recorder()->RemoveEventsObserver(this);
186 }
187
OnEventRecorded(const StructuredEventProto & event)188 void StructuredMetricsDebugProvider::OnEventRecorded(
189 const StructuredEventProto& event) {
190 events_.Append(CreateEventDict(event));
191 }
192
LoadRecordedEvents()193 void StructuredMetricsDebugProvider::LoadRecordedEvents() {
194 EventsProto proto;
195 service_->recorder()->event_storage()->CopyEvents(&proto);
196 for (const auto& event : proto.events()) {
197 events_.Append(CreateEventDict(event));
198 }
199 }
200
201 } // namespace metrics::structured
202