1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_metric/metric.h"
16
17 #include <array>
18 #include <span>
19
20 #include "pw_log/log.h"
21 #include "pw_tokenizer/base64.h"
22
23 namespace pw::metric {
24 namespace {
25
26 template <typename T>
AsSpan(const T & t)27 std::span<const std::byte> AsSpan(const T& t) {
28 return std::span<const std::byte>(reinterpret_cast<const std::byte*>(&t),
29 sizeof(t));
30 }
31
32 // A convenience class to encode a token as base64 while managing the storage.
33 // TODO(keir): Consider putting this into upstream pw_tokenizer.
34 struct Base64EncodedToken {
Base64EncodedTokenpw::metric::__anon9c2b5a7d0111::Base64EncodedToken35 Base64EncodedToken(Token token) {
36 int encoded_size = tokenizer::PrefixedBase64Encode(AsSpan(token), data);
37 data[encoded_size] = 0;
38 }
39
valuepw::metric::__anon9c2b5a7d0111::Base64EncodedToken40 const char* value() { return data.data(); }
41 std::array<char, 16> data;
42 };
43
Indent(int level)44 const char* Indent(int level) {
45 static const char* kWhitespace8 = " ";
46 level = std::min(level, 4);
47 return kWhitespace8 + 8 - 2 * level;
48 }
49
50 } // namespace
51
52 // Enable easier registration when used as a member.
Metric(Token name,float value,IntrusiveList<Metric> & metrics)53 Metric::Metric(Token name, float value, IntrusiveList<Metric>& metrics)
54 : Metric(name, value) {
55 metrics.push_front(*this);
56 }
Metric(Token name,uint32_t value,IntrusiveList<Metric> & metrics)57 Metric::Metric(Token name, uint32_t value, IntrusiveList<Metric>& metrics)
58 : Metric(name, value) {
59 metrics.push_front(*this);
60 }
61
as_float() const62 float Metric::as_float() const {
63 PW_DCHECK(is_float());
64 return float_;
65 }
66
as_int() const67 uint32_t Metric::as_int() const {
68 PW_DCHECK(is_int());
69 return uint_;
70 }
71
Increment(uint32_t amount)72 void Metric::Increment(uint32_t amount) {
73 PW_DCHECK(is_int());
74 uint_ += amount;
75 }
76
SetInt(uint32_t value)77 void Metric::SetInt(uint32_t value) {
78 PW_DCHECK(is_int());
79 uint_ = value;
80 }
81
SetFloat(float value)82 void Metric::SetFloat(float value) {
83 PW_DCHECK(is_float());
84 float_ = value;
85 }
86
Dump(int level)87 void Metric::Dump(int level) {
88 Base64EncodedToken encoded_name(name());
89 const char* indent = Indent(level);
90 if (is_float()) {
91 PW_LOG_INFO("%s \"%s\": %f,", indent, encoded_name.value(), as_float());
92 } else {
93 PW_LOG_INFO("%s \"%s\": %u,",
94 indent,
95 encoded_name.value(),
96 static_cast<unsigned int>(as_int()));
97 }
98 }
99
Dump(IntrusiveList<Metric> & metrics,int level)100 void Metric::Dump(IntrusiveList<Metric>& metrics, int level) {
101 for (auto& m : metrics) {
102 m.Dump(level);
103 }
104 }
105
Group(Token name)106 Group::Group(Token name) : name_(name) {}
107
Group(Token name,IntrusiveList<Group> & groups)108 Group::Group(Token name, IntrusiveList<Group>& groups) : name_(name) {
109 groups.push_front(*this);
110 }
111
Dump(int level)112 void Group::Dump(int level) {
113 Base64EncodedToken encoded_name(name());
114 const char* indent = Indent(level);
115 PW_LOG_INFO("%s \"%s\": {", indent, encoded_name.value());
116 Group::Dump(children(), level + 1);
117 Metric::Dump(metrics(), level + 1);
118 PW_LOG_INFO("%s }", indent);
119 }
120
Dump(IntrusiveList<Group> & groups,int level)121 void Group::Dump(IntrusiveList<Group>& groups, int level) {
122 for (auto& group : groups) {
123 group.Dump(level);
124 }
125 }
126
127 } // namespace pw::metric
128