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