Hazelcast C++ Client
Hazelcast C++ Client Library
stats.cpp
1 /*
2  * Copyright (c) 2008-2022, 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 #include <boost/utility/string_view_fwd.hpp>
18 #include <iomanip>
19 
20 #include <boost/algorithm/string/replace.hpp>
21 #include <boost/utility/string_view.hpp>
22 #include <sstream>
23 
24 #include "hazelcast/client/client_config.h"
25 #include "hazelcast/client/client_properties.h"
26 #include "hazelcast/client/connection/ClientConnectionManagerImpl.h"
27 #include "hazelcast/client/connection/Connection.h"
28 #include "hazelcast/client/impl/metrics/metrics_compressor.h"
29 #include "hazelcast/client/impl/metrics/metric_descriptor.h"
30 #include "hazelcast/client/impl/statistics/Statistics.h"
31 #include "hazelcast/client/internal/nearcache/NearCache.h"
32 #include "hazelcast/client/internal/nearcache/NearCacheManager.h"
33 #include "hazelcast/client/monitor/impl/LocalMapStatsImpl.h"
34 #include "hazelcast/client/monitor/impl/NearCacheStatsImpl.h"
35 #include "hazelcast/client/protocol/codec/codecs.h"
36 #include "hazelcast/client/protocol/codec/codecs.h"
37 #include "hazelcast/client/spi/ClientContext.h"
38 #include "hazelcast/client/spi/impl/ClientExecutionServiceImpl.h"
39 #include "hazelcast/client/spi/impl/ClientInvocation.h"
40 #include "hazelcast/client/spi/lifecycle_service.h"
41 
42 namespace hazelcast {
43  namespace client {
44  namespace impl {
45  namespace statistics {
46  const std::string Statistics::NEAR_CACHE_CATEGORY_PREFIX("nc.");
47 
48  Statistics::Statistics(spi::ClientContext &client_context) : client_context_(client_context),
49  client_properties_(
50  client_context.get_client_properties()),
51  logger_(client_context.get_logger()),
52  periodic_stats_(*this) {
53  this->enabled_ = client_properties_.get_boolean(client_properties_.get_statistics_enabled());
54  }
55 
56  void Statistics::start() {
57  if (!enabled_) {
58  return;
59  }
60 
61  int64_t periodSeconds = client_properties_.get_long(client_properties_.get_statistics_period_seconds());
62  if (periodSeconds <= 0) {
63 
64  int64_t defaultValue = util::IOUtil::to_value<int64_t>(
65  client_properties_.get_statistics_period_seconds().get_default_value());
66  HZ_LOG(logger_, warning,
67  boost::str(boost::format(
68  "Provided client statistics %1% cannot be less than or equal to 0. "
69  "You provided %2% seconds as the configuration. "
70  "Client will use the default value of %3% instead.")
71  % client_properties_.get_statistics_period_seconds().get_name()
72  % periodSeconds % defaultValue)
73  );
74  periodSeconds = defaultValue;
75  }
76 
77  schedule_periodic_statistics_send_task(periodSeconds);
78 
79  HZ_LOG(logger_, info,
80  boost::str(boost::format("Client statistics is enabled with period %1% seconds.")
81  % periodSeconds)
82  );
83  }
84 
85  void Statistics::shutdown() {
86  if (send_task_timer_) {
87  boost::system::error_code ignored;
88  send_task_timer_->cancel(ignored);
89  }
90  }
91 
92  void Statistics::schedule_periodic_statistics_send_task(int64_t period_seconds) {
93  send_task_timer_ = client_context_.get_client_execution_service().schedule_with_repetition([=]() {
94  if (!client_context_.get_lifecycle_service().is_running()) {
95  return;
96  }
97 
98  auto collection_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
99  std::chrono::system_clock::now().time_since_epoch()).count();
100  std::shared_ptr<connection::Connection> connection = get_connection();
101  if (!connection) {
102  HZ_LOG(logger_, finest, "Cannot send client statistics to the server. No connection found.");
103  return;
104  }
105 
106  std::ostringstream stats;
107  metrics::metrics_compressor metrics_comp;
108 
109  periodic_stats_.fill_metrics(stats, metrics_comp, connection);
110 
111  periodic_stats_.add_near_cache_stats(stats, metrics_comp);
112 
113 
114  send_stats(collection_timestamp, stats.str(), metrics_comp.get_blob(), connection);
115  }, std::chrono::seconds(0), std::chrono::seconds(period_seconds));
116  }
117 
118  std::shared_ptr<connection::Connection> Statistics::get_connection() {
119  return client_context_.get_connection_manager().get_random_connection();
120  }
121 
122  void Statistics::send_stats(int64_t timestamp, const std::string &new_stats,
123  const std::vector<byte> metrics_blob,
124  const std::shared_ptr<connection::Connection> &connection) {
125  auto request = protocol::codec::client_statistics_encode(timestamp, new_stats, metrics_blob);
126  try {
127  spi::impl::ClientInvocation::create(client_context_, request, "", connection)->invoke().get();
128  } catch (exception::iexception &e) {
129  // suppress exception, do not print too many messages
130  HZ_LOG(logger_, finest,
131  boost::str(boost::format("Could not send stats %1%") % e)
132  );
133  }
134  }
135 
136  void Statistics::PeriodicStatistics::fill_metrics(std::ostringstream &stats,
137  metrics::metrics_compressor &compressor,
138  const std::shared_ptr<connection::Connection> &connection) {
139  stats << "lastStatisticsCollectionTime" << KEY_VALUE_SEPARATOR << util::current_time_millis();
140  add_stat(stats, "enterprise", false);
141  add_stat(stats, "clientType", protocol::ClientTypes::CPP);
142  add_stat(stats, "clientVersion", HAZELCAST_VERSION);
143  add_stat(stats, "clusterConnectionTimestamp", std::chrono::duration_cast<std::chrono::milliseconds>(
144  connection->get_start_time().time_since_epoch()).count());
145 
146  auto localSocketAddress = connection->get_local_socket_address();
147  stats << STAT_SEPARATOR << "clientAddress" << KEY_VALUE_SEPARATOR;
148  if (localSocketAddress) {
149  stats << localSocketAddress->get_host() << ":" << localSocketAddress->get_port();
150  }
151 
152  add_stat(stats, "clientName", statistics_.client_context_.get_name());
153 
154  auto credential = statistics_.client_context_.get_client_config().get_credentials();
155  if (credential) {
156  add_stat(stats, "credentials.principal", credential->name());
157  }
158 
159  auto hw_concurrency = std::thread::hardware_concurrency();
160  // necessary for compatibility with Management Center 4.0
161  add_stat(stats, "runtime.availableProcessors", hw_concurrency);
162  compressor.add_long({"runtime", "availableProcessors", metrics::probe_unit::COUNT}, hw_concurrency);
163 
164  // more gauges can be added here
165  }
166 
167  void Statistics::PeriodicStatistics::add_near_cache_stats(std::ostringstream &stats,
168  metrics::metrics_compressor &compressor) {
169  for (auto near_cache : statistics_.client_context_.get_near_cache_manager().list_all_near_caches()) {
170  std::string nc_name = near_cache->get_name();
171 
172  std::ostringstream nc_name_with_prefix_strm;
173  get_name_with_prefix(nc_name, nc_name_with_prefix_strm);
174  nc_name_with_prefix_strm << '.';
175  std::string nc_name_with_prefix = nc_name_with_prefix_strm.str();
176 
177  auto nc_stats = std::static_pointer_cast<monitor::impl::NearCacheStatsImpl>(
178  near_cache->get_near_cache_stats());
179 
180  add_near_cache_metric(stats,
181  compressor,
182  "creationTime",
183  nc_name,
184  nc_name_with_prefix,
185  nc_stats->get_creation_time(),
186  metrics::probe_unit::MS);
187 
188  add_near_cache_metric(stats,
189  compressor,
190  "evictions",
191  nc_name,
192  nc_name_with_prefix,
193  nc_stats->get_evictions(),
194  metrics::probe_unit::COUNT);
195 
196  add_near_cache_metric(stats,
197  compressor,
198  "hits",
199  nc_name,
200  nc_name_with_prefix,
201  nc_stats->get_hits(),
202  metrics::probe_unit::COUNT);
203 
204  add_near_cache_metric(stats,
205  compressor,
206  "lastPersistenceDuration",
207  nc_name,
208  nc_name_with_prefix,
209  nc_stats->get_last_persistence_duration(),
210  metrics::probe_unit::MS);
211 
212  add_near_cache_metric(stats,
213  compressor,
214  "lastPersistenceKeyCount",
215  nc_name,
216  nc_name_with_prefix,
217  nc_stats->get_last_persistence_key_count(),
218  metrics::probe_unit::COUNT);
219 
220  add_near_cache_metric(stats,
221  compressor,
222  "lastPersistenceTime",
223  nc_name,
224  nc_name_with_prefix,
225  nc_stats->get_last_persistence_time(),
226  metrics::probe_unit::MS);
227 
228  add_near_cache_metric(stats,
229  compressor,
230  "lastPersistenceWrittenBytes",
231  nc_name,
232  nc_name_with_prefix,
233  nc_stats->get_last_persistence_written_bytes(),
234  metrics::probe_unit::BYTES);
235 
236  add_near_cache_metric(stats,
237  compressor,
238  "misses",
239  nc_name,
240  nc_name_with_prefix,
241  nc_stats->get_misses(),
242  metrics::probe_unit::COUNT);
243 
244  add_near_cache_metric(stats,
245  compressor,
246  "ownedEntryCount",
247  nc_name,
248  nc_name_with_prefix,
249  nc_stats->get_owned_entry_count(),
250  metrics::probe_unit::COUNT);
251 
252  add_near_cache_metric(stats,
253  compressor,
254  "expirations",
255  nc_name,
256  nc_name_with_prefix,
257  nc_stats->get_expirations(),
258  metrics::probe_unit::COUNT);
259 
260  add_near_cache_metric(stats,
261  compressor,
262  "invalidations",
263  nc_name,
264  nc_name_with_prefix,
265  nc_stats->get_invalidations(),
266  metrics::probe_unit::COUNT);
267 
268  add_near_cache_metric(stats,
269  compressor,
270  "invalidationRequests",
271  nc_name,
272  nc_name_with_prefix,
273  nc_stats->get_invalidation_requests(),
274  metrics::probe_unit::COUNT);
275 
276  add_near_cache_metric(stats,
277  compressor,
278  "ownedEntryMemoryCost",
279  nc_name,
280  nc_name_with_prefix,
281  nc_stats->get_owned_entry_memory_cost(),
282  metrics::probe_unit::BYTES);
283  }
284 
285  }
286 
287  void Statistics::PeriodicStatistics::add_near_cache_metric(std::ostringstream &stats,
288  metrics::metrics_compressor &compressor,
289  const std::string &metric,
290  const std::string &near_cache_name,
291  const std::string &near_cache_name_with_prefix,
292  int64_t value,
293  metrics::probe_unit unit) {
294 
295  metrics::metric_descriptor desc{ "nearcache", metric, "name", near_cache_name, unit };
296  compressor.add_long(desc, value);
297 
298  // necessary for compatibility with Management Center 4.0
299  add_stat(stats, near_cache_name_with_prefix, metric, value);
300  }
301 
302  Statistics::PeriodicStatistics::PeriodicStatistics(Statistics &statistics) : statistics_(statistics) {}
303 
304  std::string Statistics::escape_special_characters(std::string &name) {
305  boost::replace_all(name, ",", "\\,");
306  boost::replace_all(name, "=", "\\=");
307  boost::replace_all(name, "\\", "\\\\");
308 
309  return name[0] == '/' ? name.substr(1) : name;
310  }
311 
312  void
313  Statistics::PeriodicStatistics::get_name_with_prefix(std::string &name, std::ostringstream &out) {
314  out << NEAR_CACHE_CATEGORY_PREFIX << Statistics::escape_special_characters(name);
315  }
316 
317  template<>
318  void Statistics::PeriodicStatistics::add_stat(std::ostringstream &stats, const std::string &name,
319  const bool &value) {
320  stats << STAT_SEPARATOR << name << KEY_VALUE_SEPARATOR << (value ? "true" : "false");
321  }
322 
323  }
324  }
325 
326  namespace monitor {
327  const int64_t local_instance_stats::STAT_NOT_AVAILABLE = -99L;
328 
329  namespace impl {
330  LocalMapStatsImpl::LocalMapStatsImpl() = default;
331 
332  LocalMapStatsImpl::LocalMapStatsImpl(const std::shared_ptr<monitor::near_cache_stats> &s) : near_cache_stats_(s) {}
333 
334  std::shared_ptr<monitor::near_cache_stats> LocalMapStatsImpl::get_near_cache_stats() const {
335  return near_cache_stats_;
336  }
337 
338  NearCacheStatsImpl::NearCacheStatsImpl() : creation_time_(util::current_time_millis()),
339  owned_entry_count_(0),
340  owned_entry_memory_cost_(0),
341  hits_(0),
342  misses_(0),
343  evictions_(0),
344  expirations_(0),
345  invalidations_(0),
346  invalidation_requests_(0),
347  persistence_count_(0),
348  last_persistence_time_(0),
349  last_persistence_duration_(0),
350  last_persistence_written_bytes_(0),
351  last_persistence_key_count_(0),
352  last_persistence_failure_("") {
353  }
354 
355  int64_t NearCacheStatsImpl::get_creation_time() {
356  return creation_time_;
357  }
358 
359  int64_t NearCacheStatsImpl::get_owned_entry_count() {
360  return owned_entry_count_;
361  }
362 
363  void NearCacheStatsImpl::set_owned_entry_count(int64_t owned_entry_count) {
364  this->owned_entry_count_ = owned_entry_count;
365  }
366 
367  void NearCacheStatsImpl::increment_owned_entry_count() {
368  ++owned_entry_count_;
369  }
370 
371  void NearCacheStatsImpl::decrement_owned_entry_count() {
372  --owned_entry_count_;
373  }
374 
375  int64_t NearCacheStatsImpl::get_owned_entry_memory_cost() {
376  return owned_entry_memory_cost_;
377  }
378 
379  void NearCacheStatsImpl::set_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
380  this->owned_entry_memory_cost_ = owned_entry_memory_cost;
381  }
382 
383  void NearCacheStatsImpl::increment_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
384  this->owned_entry_memory_cost_ += owned_entry_memory_cost;
385  }
386 
387  void NearCacheStatsImpl::decrement_owned_entry_memory_cost(int64_t owned_entry_memory_cost) {
388  this->owned_entry_memory_cost_ -= owned_entry_memory_cost;
389  }
390 
391  int64_t NearCacheStatsImpl::get_hits() {
392  return hits_;
393  }
394 
395  // just for testing
396  void NearCacheStatsImpl::set_hits(int64_t hits) {
397  this->hits_ = hits;
398  }
399 
400  void NearCacheStatsImpl::increment_hits() {
401  ++hits_;
402  }
403 
404  int64_t NearCacheStatsImpl::get_misses() {
405  return misses_;
406  }
407 
408  // just for testing
409  void NearCacheStatsImpl::set_misses(int64_t misses) {
410  this->misses_ = misses;
411  }
412 
413  void NearCacheStatsImpl::increment_misses() {
414  ++misses_;
415  }
416 
417  double NearCacheStatsImpl::get_ratio() {
418  if (misses_ == (int64_t) 0) {
419  if (hits_ == (int64_t) 0) {
420  return std::numeric_limits<double>::signaling_NaN();
421  } else {
422  return std::numeric_limits<double>::infinity();
423  }
424  } else {
425  return ((double) hits_ / misses_) * PERCENTAGE;
426  }
427  }
428 
429  int64_t NearCacheStatsImpl::get_evictions() {
430  return evictions_;
431  }
432 
433  void NearCacheStatsImpl::increment_evictions() {
434  ++evictions_;
435  }
436 
437  int64_t NearCacheStatsImpl::get_expirations() {
438  return expirations_;
439  }
440 
441  void NearCacheStatsImpl::increment_expirations() {
442  ++expirations_;
443  }
444 
445  int64_t NearCacheStatsImpl::get_invalidations() {
446  return invalidations_.load();
447  }
448 
449  void NearCacheStatsImpl::increment_invalidations() {
450  ++invalidations_;
451  }
452 
453  int64_t NearCacheStatsImpl::get_invalidation_requests() {
454  return invalidation_requests_.load();
455  }
456 
457  void NearCacheStatsImpl::increment_invalidation_requests() {
458  ++invalidation_requests_;
459  }
460 
461  void NearCacheStatsImpl::reset_invalidation_events() {
462  invalidation_requests_ = 0;
463  }
464 
465  int64_t NearCacheStatsImpl::get_persistence_count() {
466  return persistence_count_;
467  }
468 
469  void NearCacheStatsImpl::add_persistence(int64_t duration, int32_t written_bytes, int32_t key_count) {
470  ++persistence_count_;
471  last_persistence_time_ = util::current_time_millis();
472  last_persistence_duration_ = duration;
473  last_persistence_written_bytes_ = written_bytes;
474  last_persistence_key_count_ = key_count;
475  last_persistence_failure_ = "";
476  }
477 
478  int64_t NearCacheStatsImpl::get_last_persistence_time() {
479  return last_persistence_time_;
480  }
481 
482  int64_t NearCacheStatsImpl::get_last_persistence_duration() {
483  return last_persistence_duration_;
484  }
485 
486  int64_t NearCacheStatsImpl::get_last_persistence_written_bytes() {
487  return last_persistence_written_bytes_;
488  }
489 
490  int64_t NearCacheStatsImpl::get_last_persistence_key_count() {
491  return last_persistence_key_count_;
492  }
493 
494  std::string NearCacheStatsImpl::get_last_persistence_failure() {
495  return last_persistence_failure_;
496  }
497 
498  std::string NearCacheStatsImpl::to_string() {
499  std::ostringstream out;
500  std::string failureString = last_persistence_failure_;
501  out << "NearCacheStatsImpl{"
502  << "ownedEntryCount=" << owned_entry_count_
503  << ", ownedEntryMemoryCost=" << owned_entry_memory_cost_
504  << ", creationTime=" << creation_time_
505  << ", hits=" << hits_
506  << ", misses=" << misses_
507  << ", ratio=" << std::setprecision(1) << get_ratio()
508  << ", evictions=" << evictions_
509  << ", expirations=" << expirations_
510  << ", invalidations=" << invalidations_.load()
511  << ", invalidationRequests=" << invalidation_requests_.load()
512  << ", lastPersistenceTime=" << last_persistence_time_
513  << ", persistenceCount=" << persistence_count_
514  << ", lastPersistenceDuration=" << last_persistence_duration_
515  << ", lastPersistenceWrittenBytes=" << last_persistence_written_bytes_
516  << ", lastPersistenceKeyCount=" << last_persistence_key_count_
517  << ", lastPersistenceFailure='" << failureString << "'"
518  << '}';
519 
520  return out.str();
521  }
522 
523  const double NearCacheStatsImpl::PERCENTAGE = 100.0;
524 
525  }
526  }
527  }
528 }