• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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