Modern C++ Kafka API
KafkaMetrics.h
1 #pragma once
2 
3 #include <kafka/Project.h>
4 
5 // https://github.com/Tencent/rapidjson/releases/tag/v1.1.0
6 #include <rapidjson/document.h>
7 #include <rapidjson/stringbuffer.h>
8 #include <rapidjson/writer.h>
9 
10 #include <algorithm>
11 #include <cstdint>
12 #include <iostream>
13 #include <sstream>
14 #include <stdexcept>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 
20 namespace KAFKA_API {
21 
26 {
27 public:
31  explicit KafkaMetrics(std::string jsonMetrics);
32 
33  static const constexpr char* WILDCARD = "*";
34 
35  using KeysType = std::vector<std::string>;
36 
40  template<typename ValueType>
41  using ResultsType = std::vector<std::pair<KeysType, ValueType>>;
42 
47  ResultsType<std::int64_t> getInt(const KeysType& keys) const { return get<std::int64_t>(keys); }
48 
53  ResultsType<std::string> getString(const KeysType& keys) const { return get<std::string>(keys); }
54 
55  static std::string toString(const KafkaMetrics::KeysType& keys);
56 
57  template<typename ValueType>
58  static std::string toString(const KafkaMetrics::ResultsType<ValueType>& results);
59 
60 private:
61  template<typename ValueType>
62  ResultsType<ValueType> get(const KeysType& keys) const;
63 
64  template<typename ValueType>
65  static void getResults(ResultsType<ValueType>& results,
66  KeysType& keysForWildcards,
67  rapidjson::Value::ConstMemberIterator iter,
68  KeysType::const_iterator keysToParse,
69  KeysType::const_iterator keysEnd);
70 
71  template<typename ValueType>
72  static ValueType getValue(rapidjson::Value::ConstMemberIterator iter);
73 
74 #if COMPILER_SUPPORTS_CPP_17
75  std::string _decodeBuf;
76 #else
77  std::vector<char> _decodeBuf;
78 #endif
79  rapidjson::Document _jsonDoc;
80 };
81 
82 inline
83 KafkaMetrics::KafkaMetrics(std::string jsonMetrics)
84 #if COMPILER_SUPPORTS_CPP_17
85  : _decodeBuf(std::move(jsonMetrics))
86 #else
87  : _decodeBuf(jsonMetrics.cbegin(), jsonMetrics.cend() + 1)
88 #endif
89 {
90  if (_jsonDoc.ParseInsitu(_decodeBuf.data()).HasParseError())
91  {
92  throw std::runtime_error("Failed to parse string with JSON format!");
93  }
94 }
95 
96 template<>
97 inline std::int64_t
98 KafkaMetrics::getValue<std::int64_t>(rapidjson::Value::ConstMemberIterator iter)
99 {
100  return iter->value.GetInt();
101 }
102 
103 template<>
104 inline std::string
105 KafkaMetrics::getValue<std::string>(rapidjson::Value::ConstMemberIterator iter)
106 {
107  return iter->value.GetString();
108 }
109 
110 template<typename ValueType>
111 inline KafkaMetrics::ResultsType<ValueType>
112 KafkaMetrics::get(const KeysType& keys) const
113 {
114  if (keys.empty()) throw std::invalid_argument("Input keys cannot be empty!");
115  if (keys.front() == WILDCARD) throw std::invalid_argument("The first key cannot be wildcard!");
116  if (keys.back() == WILDCARD) throw std::invalid_argument("The last key cannot be wildcard!");
117 
118  ResultsType<ValueType> results;
119 
120  const rapidjson::Value::ConstMemberIterator iter = _jsonDoc.FindMember(keys.front().c_str());
121  if (iter == _jsonDoc.MemberEnd()) return results;
122 
123  if (keys.size() == 1)
124  {
125  if (std::is_same<ValueType, std::string>::value ? iter->value.IsString() : iter->value.IsInt())
126  {
127  results.emplace_back(KeysType{}, getValue<ValueType>(iter));
128  }
129 
130  return results;
131  }
132 
133  KeysType keysForWildcards;
134 
135  getResults(results, keysForWildcards, iter, keys.cbegin() + 1, keys.cend());
136  return results;
137 }
138 
139 template<typename ValueType>
140 inline void
141 KafkaMetrics::getResults(KafkaMetrics::ResultsType<ValueType>& results,
142  KeysType& keysForWildcards,
143  rapidjson::Value::ConstMemberIterator iter,
144  KeysType::const_iterator keysToParse,
145  KeysType::const_iterator keysEnd)
146 {
147  if (!iter->value.IsObject()) return;
148 
149  const auto& key = *(keysToParse++);
150  const bool isTheEnd = (keysToParse == keysEnd);
151 
152  if (key == WILDCARD)
153  {
154  for (rapidjson::Value::ConstMemberIterator subIter = iter->value.MemberBegin(); subIter != iter->value.MemberEnd(); ++subIter)
155  {
156  KeysType newKeysForWildcards = keysForWildcards;
157  newKeysForWildcards.emplace_back(subIter->name.GetString());
158 
159  getResults(results, newKeysForWildcards, subIter, keysToParse, keysEnd);
160  }
161  }
162  else
163  {
164  const rapidjson::Value::ConstMemberIterator subIter = iter->value.FindMember(key.c_str());
165  if (subIter == iter->value.MemberEnd()) return;
166 
167  if (!isTheEnd)
168  {
169  getResults(results, keysForWildcards, subIter, keysToParse, keysEnd);
170  }
171  else if (std::is_same<ValueType, std::string>::value ? subIter->value.IsString() : subIter->value.IsInt())
172  {
173  results.emplace_back(keysForWildcards, getValue<ValueType>(subIter));
174  }
175  }
176 }
177 
178 inline std::string
179 KafkaMetrics::toString(const KafkaMetrics::KeysType& keys)
180 {
181  std::string ret;
182 
183  std::for_each(keys.cbegin(), keys.cend(),
184  [&ret](const auto& key){ ret.append((ret.empty() ? std::string() : std::string(", ")) + "\"" + key + "\""); });
185 
186  return ret;
187 }
188 
189 template<typename ValueType>
190 inline std::string
191 KafkaMetrics::toString(const KafkaMetrics::ResultsType<ValueType>& results)
192 {
193  std::ostringstream oss;
194  bool isTheFirstOne = true;
195 
196  std::for_each(results.cbegin(), results.cend(),
197  [&oss, &isTheFirstOne](const auto& result) {
198  const auto keysString = toString(result.first);
199 
200  oss << (isTheFirstOne ? (isTheFirstOne = false, "") : ", ")
201  << (keysString.empty() ? "" : (std::string("[") + keysString + "]:"));
202  oss << (std::is_same<ValueType, std::string>::value ? "\"" : "") << result.second << (std::is_same<ValueType, std::string>::value ? "\"" : "");
203  });
204 
205  return oss.str();
206 }
207 
208 } // end of KAFKA_API
209 
Helps to parse the metrics string with JSON format.
Definition: KafkaMetrics.h:26
ResultsType< std::string > getString(const KeysType &keys) const
Get string value(s) for the specified metrics.
Definition: KafkaMetrics.h:53
ResultsType< std::int64_t > getInt(const KeysType &keys) const
Get integer value(s) for the specified metrics.
Definition: KafkaMetrics.h:47
KafkaMetrics(std::string jsonMetrics)
Initilize with the metrics string.
Definition: KafkaMetrics.h:83
std::vector< std::pair< KeysType, ValueType > > ResultsType
The matched keys (for wildcards) and the value.
Definition: KafkaMetrics.h:41