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