Hazelcast C++ Client
Hazelcast C++ Client Library
stats.cpp
1 /*
2  * Copyright (c) 2008-2021, Hazelcast, Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Copyright (c) 2008-2021, Hazelcast, Inc. All Rights Reserved.
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  *
24  * http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS,
28  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  * See the License for the specific language governing permissions and
30  * limitations under the License.
31  */
32 #include <iomanip>
33 
34 #include <boost/algorithm/string/replace.hpp>
35 
36 #include "hazelcast/client/impl/statistics/Statistics.h"
37 #include "hazelcast/client/spi/ClientContext.h"
38 #include "hazelcast/client/client_properties.h"
39 #include "hazelcast/client/spi/impl/ClientExecutionServiceImpl.h"
40 #include "hazelcast/client/spi/impl/ClientInvocation.h"
41 #include "hazelcast/client/spi/lifecycle_service.h"
42 #include "hazelcast/client/connection/Connection.h"
43 #include "hazelcast/client/connection/ClientConnectionManagerImpl.h"
44 #include "hazelcast/client/protocol/codec/codecs.h"
45 #include "hazelcast/client/protocol/codec/codecs.h"
46 #include "hazelcast/client/internal/nearcache/NearCache.h"
47 #include "hazelcast/client/internal/nearcache/NearCacheManager.h"
48 #include "hazelcast/client/monitor/impl/NearCacheStatsImpl.h"
49 #include "hazelcast/client/monitor/impl/LocalMapStatsImpl.h"
50 #include "hazelcast/client/client_config.h"
51 
52 namespace hazelcast {
53  namespace client {
54  namespace impl {
55  namespace statistics {
56  const std::string Statistics::NEAR_CACHE_CATEGORY_PREFIX("nc.");
57 
58  Statistics::Statistics(spi::ClientContext &client_context) : client_context_(client_context),
59  client_properties_(
60  client_context.get_client_properties()),
61  logger_(client_context.get_logger()),
62  periodic_stats_(*this) {
63  this->enabled_ = client_properties_.get_boolean(client_properties_.get_statistics_enabled());
64  }
65 
66  void Statistics::start() {
67  if (!enabled_) {
68  return;
69  }
70 
71  int64_t periodSeconds = client_properties_.get_long(client_properties_.get_statistics_period_seconds());
72  if (periodSeconds <= 0) {
73 
74  int64_t defaultValue = util::IOUtil::to_value<int64_t>(
75  client_properties_.get_statistics_period_seconds().get_default_value());
76  HZ_LOG(logger_, warning,
77  boost::str(boost::format(
78  "Provided client statistics %1% cannot be less than or equal to 0. "
79  "You provided %2% seconds as the configuration. "
80  "Client will use the default value of %3% instead.")
81  % client_properties_.get_statistics_period_seconds().get_name()
82  % periodSeconds % defaultValue)
83  );
84  periodSeconds = defaultValue;
85  }
86 
87  schedule_periodic_statistics_send_task(periodSeconds);
88 
89  HZ_LOG(logger_, info,
90  boost::str(boost::format("Client statistics is enabled with period %1% seconds.")
91  % periodSeconds)
92  );
93  }
94 
95  void Statistics::shutdown() {
96  if (send_task_timer_) {
97  boost::system::error_code ignored;
98  send_task_timer_->cancel(ignored);
99  }
100  }
101 
102  void Statistics::schedule_periodic_statistics_send_task(int64_t period_seconds) {
103  send_task_timer_ = client_context_.get_client_execution_service().schedule_with_repetition([=]() {
104  if (!client_context_.get_lifecycle_service().is_running()) {
105  return;
106  }
107 
108  auto collection_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
109  std::chrono::system_clock::now().time_since_epoch()).count();
110  std::shared_ptr<connection::Connection> connection = get_connection();
111  if (!connection) {
112  HZ_LOG(logger_, finest, "Cannot send client statistics to the server. No connection found.");
113  return;
114  }
115 
116  std::ostringstream stats;
117 
118  periodic_stats_.fill_metrics(stats, connection);
119 
120  periodic_stats_.add_near_cache_stats(stats);
121 
122  send_stats(collection_timestamp, stats.str(), connection);
123  }, std::chrono::seconds(0), std::chrono::seconds(period_seconds));
124  }
125 
126  std::shared_ptr<connection::Connection> Statistics::get_connection() {
127  return client_context_.get_connection_manager().get_random_connection();
128  }
129 
130  void Statistics::send_stats(int64_t timestamp, const std::string &new_stats,
131  const std::shared_ptr<connection::Connection> &connection) {
132  // TODO: implement metrics blob
133  auto request = protocol::codec::client_statistics_encode(timestamp, new_stats, std::vector<byte>());
134  try {
135  spi::impl::ClientInvocation::create(client_context_, request, "", connection)->invoke().get();
136  } catch (exception::iexception &e) {
137  // suppress exception, do not print too many messages
138  HZ_LOG(logger_, finest,
139  boost::str(boost::format("Could not send stats %1%") % e)
140  );
141  }
142  }
143 
144  void Statistics::PeriodicStatistics::fill_metrics(std::ostringstream &stats,
145  const std::shared_ptr<connection::Connection> &connection) {
146  stats << "lastStatisticsCollectionTime" << KEY_VALUE_SEPARATOR << util::current_time_millis();
147  add_stat(stats, "enterprise", false);
148  add_stat(stats, "clientType", protocol::ClientTypes::CPP);
149  add_stat(stats, "clientVersion", HAZELCAST_VERSION);
150  add_stat(stats, "clusterConnectionTimestamp", std::chrono::duration_cast<std::chrono::milliseconds>(
151  connection->get_start_time().time_since_epoch()).count());
152 
153  auto localSocketAddress = connection->get_local_socket_address();
154  stats << STAT_SEPARATOR << "clientAddress" << KEY_VALUE_SEPARATOR;
155  if (localSocketAddress) {
156  stats << localSocketAddress->get_host() << ":" << localSocketAddress->get_port();
157  }
158 
159  add_stat(stats, "clientName", statistics_.client_context_.get_name());
160 
161  auto credential = statistics_.client_context_.get_client_config().get_credentials();
162  if (credential) {
163  add_stat(stats, "credentials.principal", credential->name());
164  }
165  }
166 
167  void Statistics::PeriodicStatistics::add_near_cache_stats(std::ostringstream &stats) {
168  for (auto nearCache : statistics_.client_context_.get_near_cache_manager().list_all_near_caches()) {
169  std::string nearCacheName = nearCache->get_name();
170  std::ostringstream nearCacheNameWithPrefix;
171  get_name_with_prefix(nearCacheName, nearCacheNameWithPrefix);
172 
173  nearCacheNameWithPrefix << '.';
174 
175  auto nearCacheStats = std::static_pointer_cast<monitor::impl::NearCacheStatsImpl>(nearCache->get_near_cache_stats());
176 
177  std::string prefix = nearCacheNameWithPrefix.str();
178 
179  add_stat(stats, prefix, "creationTime", nearCacheStats->get_creation_time());
180  add_stat(stats, prefix, "evictions", nearCacheStats->get_evictions());
181  add_stat(stats, prefix, "hits", nearCacheStats->get_hits());
182  add_stat(stats, prefix, "lastPersistenceDuration",
183  nearCacheStats->get_last_persistence_duration());
184  add_stat(stats, prefix, "lastPersistenceKeyCount",
185  nearCacheStats->get_last_persistence_key_count());
186  add_stat(stats, prefix, "lastPersistenceTime",
187  nearCacheStats->get_last_persistence_time());
188  add_stat(stats, prefix, "lastPersistenceWrittenBytes",
189  nearCacheStats->get_last_persistence_written_bytes());
190  add_stat(stats, prefix, "misses", nearCacheStats->get_misses());
191  add_stat(stats, prefix, "ownedEntryCount", nearCacheStats->get_owned_entry_count());
192  add_stat(stats, prefix, "expirations", nearCacheStats->get_expirations());
193  add_stat(stats, prefix, "invalidations", nearCacheStats->get_invalidations());
194  add_stat(stats, prefix, "invalidationRequests",
195  nearCacheStats->get_invalidation_requests());
196  add_stat(stats, prefix, "ownedEntryMemoryCost",
197  nearCacheStats->get_owned_entry_memory_cost());
198  std::string persistenceFailure = nearCacheStats->get_last_persistence_failure();
199  if (!persistenceFailure.empty()) {
200  add_stat(stats, prefix, "lastPersistenceFailure", persistenceFailure);
201  }
202  }
203 
204  }
205 
206  Statistics::PeriodicStatistics::PeriodicStatistics(Statistics &statistics) : statistics_(statistics) {}
207 
208  std::string Statistics::escape_special_characters(std::string &name) {
209  boost::replace_all(name, ",", "\\,");
210  boost::replace_all(name, "=", "\\=");
211  boost::replace_all(name, "\\", "\\\\");
212 
213  return name[0] == '/' ? name.substr(1) : name;
214  }
215 
216  void
217  Statistics::PeriodicStatistics::get_name_with_prefix(std::string &name, std::ostringstream &out) {
218  out << NEAR_CACHE_CATEGORY_PREFIX << Statistics::escape_special_characters(name);
219  }
220 
221  template<>
222  void Statistics::PeriodicStatistics::add_stat(std::ostringstream &stats, const std::string &name,
223  const bool &value) {
224  stats << STAT_SEPARATOR << name << KEY_VALUE_SEPARATOR << (value ? "true" : "false");
225  }
226 
227  }
228  }
229 
230  namespace monitor {
231  const int64_t local_instance_stats::STAT_NOT_AVAILABLE = -99L;
232 
233  namespace impl {
234  LocalMapStatsImpl::LocalMapStatsImpl() = default;
235 
236  LocalMapStatsImpl::LocalMapStatsImpl(const std::shared_ptr<monitor::near_cache_stats> &s) : near_cache_stats_(s) {}
237 
238  std::shared_ptr<monitor::near_cache_stats> LocalMapStatsImpl::get_near_cache_stats() const {
239  return near_cache_stats_;
240  }
241 
242  NearCacheStatsImpl::NearCacheStatsImpl() : creation_time_(util::current_time_millis()),
243  owned_entry_count_(0),
244  owned_entry_memory_cost_(0),
245  hits_(0),
246  misses_(0),
247  evictions_(0),
248  expirations_(0),
249  invalidations_(0),
250  invalidation_requests_(0),
251  persistence_count_(0),
252  last_persistence_time_(0),
253  last_persistence_duration_(0),
254  last_persistence_written_bytes_(0),
255  last_persistence_key_count_(0),
256  last_persistence_failure_("") {
257  }
258 
259  int64_t NearCacheStatsImpl::get_creation_time() {
260  return creation_time_;
261  }
262 
263  int64_t NearCacheStatsImpl::get_owned_entry_count() {
264  return owned_entry_count_;
265  }
266 
267  void NearCacheStatsImpl::set_owned_entry_count(int64_t owned_entry_count) {
268  this->owned_entry_count_ = owned_entry_count;
269  }
270 
271  void NearCacheStatsImpl::increment_owned_entry_count() {
272  ++owned_entry_count_;
273  }
274 
275  void NearCacheStatsImpl::decrement_owned_entry_count() {
276  --owned_entry_count_;
277  }
278 
279  int64_t NearCacheStatsImpl::get_owned_entry_memory_cost() {
280  return owned_entry_memory_cost_;
281  }
282 
283  void NearCacheStatsImpl::set_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
284  this->owned_entry_memory_cost_ = owned_entry_memory_cost;
285  }
286 
287  void NearCacheStatsImpl::increment_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
288  this->owned_entry_memory_cost_ += owned_entry_memory_cost;
289  }
290 
291  void NearCacheStatsImpl::decrement_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
292  this->owned_entry_memory_cost_ -= owned_entry_memory_cost;
293  }
294 
295  int64_t NearCacheStatsImpl::get_hits() {
296  return hits_;
297  }
298 
299  // just for testing
300  void NearCacheStatsImpl::set_hits(int64_t hits) {
301  this->hits_ = hits;
302  }
303 
304  void NearCacheStatsImpl::increment_hits() {
305  ++hits_;
306  }
307 
308  int64_t NearCacheStatsImpl::get_misses() {
309  return misses_;
310  }
311 
312  // just for testing
313  void NearCacheStatsImpl::set_misses(int64_t misses) {
314  this->misses_ = misses;
315  }
316 
317  void NearCacheStatsImpl::increment_misses() {
318  ++misses_;
319  }
320 
321  double NearCacheStatsImpl::get_ratio() {
322  if (misses_ == (int64_t) 0) {
323  if (hits_ == (int64_t) 0) {
324  return std::numeric_limits<double>::signaling_NaN();
325  } else {
326  return std::numeric_limits<double>::infinity();
327  }
328  } else {
329  return ((double) hits_ / misses_) * PERCENTAGE;
330  }
331  }
332 
333  int64_t NearCacheStatsImpl::get_evictions() {
334  return evictions_;
335  }
336 
337  void NearCacheStatsImpl::increment_evictions() {
338  ++evictions_;
339  }
340 
341  int64_t NearCacheStatsImpl::get_expirations() {
342  return expirations_;
343  }
344 
345  void NearCacheStatsImpl::increment_expirations() {
346  ++expirations_;
347  }
348 
349  int64_t NearCacheStatsImpl::get_invalidations() {
350  return invalidations_.load();
351  }
352 
353  void NearCacheStatsImpl::increment_invalidations() {
354  ++invalidations_;
355  }
356 
357  int64_t NearCacheStatsImpl::get_invalidation_requests() {
358  return invalidation_requests_.load();
359  }
360 
361  void NearCacheStatsImpl::increment_invalidation_requests() {
362  ++invalidation_requests_;
363  }
364 
365  void NearCacheStatsImpl::reset_invalidation_events() {
366  invalidation_requests_ = 0;
367  }
368 
369  int64_t NearCacheStatsImpl::get_persistence_count() {
370  return persistence_count_;
371  }
372 
373  void NearCacheStatsImpl::add_persistence(int64_t duration, int32_t written_bytes, int32_t key_count) {
374  ++persistence_count_;
375  last_persistence_time_ = util::current_time_millis();
376  last_persistence_duration_ = duration;
377  last_persistence_written_bytes_ = written_bytes;
378  last_persistence_key_count_ = key_count;
379  last_persistence_failure_ = "";
380  }
381 
382  int64_t NearCacheStatsImpl::get_last_persistence_time() {
383  return last_persistence_time_;
384  }
385 
386  int64_t NearCacheStatsImpl::get_last_persistence_duration() {
387  return last_persistence_duration_;
388  }
389 
390  int64_t NearCacheStatsImpl::get_last_persistence_written_bytes() {
391  return last_persistence_written_bytes_;
392  }
393 
394  int64_t NearCacheStatsImpl::get_last_persistence_key_count() {
395  return last_persistence_key_count_;
396  }
397 
398  std::string NearCacheStatsImpl::get_last_persistence_failure() {
399  return last_persistence_failure_;
400  }
401 
402  std::string NearCacheStatsImpl::to_string() {
403  std::ostringstream out;
404  std::string failureString = last_persistence_failure_;
405  out << "NearCacheStatsImpl{"
406  << "ownedEntryCount=" << owned_entry_count_
407  << ", ownedEntryMemoryCost=" << owned_entry_memory_cost_
408  << ", creationTime=" << creation_time_
409  << ", hits=" << hits_
410  << ", misses=" << misses_
411  << ", ratio=" << std::setprecision(1) << get_ratio()
412  << ", evictions=" << evictions_
413  << ", expirations=" << expirations_
414  << ", invalidations=" << invalidations_.load()
415  << ", invalidationRequests=" << invalidation_requests_.load()
416  << ", lastPersistenceTime=" << last_persistence_time_
417  << ", persistenceCount=" << persistence_count_
418  << ", lastPersistenceDuration=" << last_persistence_duration_
419  << ", lastPersistenceWrittenBytes=" << last_persistence_written_bytes_
420  << ", lastPersistenceKeyCount=" << last_persistence_key_count_
421  << ", lastPersistenceFailure='" << failureString << "'"
422  << '}';
423 
424  return out.str();
425  }
426 
427  const double NearCacheStatsImpl::PERCENTAGE = 100.0;
428  }
429  }
430  }
431 }