249 std::vector<dicom_metric>& metrics,
250 const std::string& op_name,
264 if (
auto it = config.find(
"collect_associations"); it != config.end()) {
267 if (
auto it = config.find(
"collect_transfers"); it != config.end()) {
270 if (
auto it = config.find(
"collect_storage"); it != config.end()) {
273 if (
auto it = config.find(
"collect_queries"); it != config.end()) {
276 if (
auto it = config.find(
"collect_pools"); it != config.end()) {
284 std::vector<dicom_metric> metrics;
311 std::vector<std::string> types;
314 types.push_back(
"dicom_associations_active");
315 types.push_back(
"dicom_associations_peak");
316 types.push_back(
"dicom_associations_total");
317 types.push_back(
"dicom_associations_rejected_total");
318 types.push_back(
"dicom_associations_aborted_total");
321 types.push_back(
"dicom_images_sent_total");
322 types.push_back(
"dicom_images_received_total");
323 types.push_back(
"dicom_bytes_sent_total");
324 types.push_back(
"dicom_bytes_received_total");
325 types.push_back(
"dicom_transfer_rate_mbps");
328 static constexpr std::array<const char*, 11> ops = {
329 "c_echo",
"c_store",
"c_find",
"c_move",
"c_get",
330 "n_create",
"n_set",
"n_get",
"n_action",
"n_event",
"n_delete"
332 for (
const auto* op : ops) {
333 types.push_back(std::string(
"dicom_") + op +
"_total");
334 types.push_back(std::string(
"dicom_") + op +
"_success_total");
335 types.push_back(std::string(
"dicom_") + op +
"_failure_total");
336 types.push_back(std::string(
"dicom_") + op +
"_duration_seconds");
340 types.push_back(
"dicom_element_pool_hits");
341 types.push_back(
"dicom_element_pool_misses");
342 types.push_back(
"dicom_dataset_pool_hits");
343 types.push_back(
"dicom_dataset_pool_misses");
351 stats[
"active_associations"] =
353 stats[
"total_images_processed"] =
355 stats[
"total_bytes_transferred"] =
361 snapshot.
timestamp = std::chrono::system_clock::now();
364 const auto& assoc =
pacs.associations();
365 const auto& transfer =
pacs.transfer();
369 assoc.total_established.load(std::memory_order_relaxed);
371 assoc.current_active.load(std::memory_order_relaxed);
373 assoc.total_rejected.load(std::memory_order_relaxed) +
374 assoc.total_aborted.load(std::memory_order_relaxed);
376 assoc.peak_active.load(std::memory_order_relaxed);
381 transfer.images_stored.load(std::memory_order_relaxed);
383 transfer.bytes_sent.load(std::memory_order_relaxed);
385 transfer.bytes_received.load(std::memory_order_relaxed);
391 store.success_count.load(std::memory_order_relaxed);
393 store.failure_count.load(std::memory_order_relaxed);
395 static_cast<double>(store.average_duration_us()) / 1000.0;
401 query.success_count.load(std::memory_order_relaxed);
403 query.failure_count.load(std::memory_order_relaxed);
405 static_cast<double>(query.average_duration_us()) / 1000.0;
436 std::vector<dicom_metric>& metrics) {
442 "dicom_associations_active",
443 static_cast<double>(assoc.current_active.load(std::memory_order_relaxed)),
448 "dicom_associations_peak",
449 static_cast<double>(assoc.peak_active.load(std::memory_order_relaxed)),
454 "dicom_associations_total",
455 static_cast<double>(assoc.total_established.load(std::memory_order_relaxed)),
460 "dicom_associations_rejected_total",
461 static_cast<double>(assoc.total_rejected.load(std::memory_order_relaxed)),
466 "dicom_associations_aborted_total",
467 static_cast<double>(assoc.total_aborted.load(std::memory_order_relaxed)),
471 const auto total = assoc.total_established.load(std::memory_order_relaxed);
472 const auto rejected = assoc.total_rejected.load(std::memory_order_relaxed);
473 const auto attempted = total + rejected;
474 const double success_rate = (attempted > 0)
475 ?
static_cast<double>(total) /
static_cast<double>(attempted)
479 "dicom_associations_success_rate",
485 std::vector<dicom_metric>& metrics) {
491 "dicom_images_stored_total",
492 static_cast<double>(transfer.images_stored.load(std::memory_order_relaxed)),
497 "dicom_images_retrieved_total",
498 static_cast<double>(transfer.images_retrieved.load(std::memory_order_relaxed)),
503 "dicom_bytes_sent_total",
504 static_cast<double>(transfer.bytes_sent.load(std::memory_order_relaxed)),
509 "dicom_bytes_received_total",
510 static_cast<double>(transfer.bytes_received.load(std::memory_order_relaxed)),
515 std::vector<dicom_metric>& metrics) {
525 std::vector<dicom_metric>& metrics) {
555 std::vector<dicom_metric>& metrics) {
560 const auto& elem_pool =
pacs.element_pool();
562 "dicom_element_pool_acquisitions_total",
563 static_cast<double>(elem_pool.total_acquisitions.load(std::memory_order_relaxed)),
565 {{
"pool",
"element"}}));
566 metrics.push_back(create_base_metric(
567 "dicom_element_pool_hits_total",
568 static_cast<double>(elem_pool.pool_hits.load(std::memory_order_relaxed)),
570 {{
"pool",
"element"}}));
571 metrics.push_back(create_base_metric(
572 "dicom_element_pool_misses_total",
573 static_cast<double>(elem_pool.pool_misses.load(std::memory_order_relaxed)),
575 {{
"pool",
"element"}}));
576 metrics.push_back(create_base_metric(
577 "dicom_element_pool_hit_ratio",
578 elem_pool.hit_ratio(),
580 {{
"pool",
"element"}}));
583 const auto& ds_pool =
pacs.dataset_pool();
584 metrics.push_back(create_base_metric(
585 "dicom_dataset_pool_acquisitions_total",
586 static_cast<double>(ds_pool.total_acquisitions.load(std::memory_order_relaxed)),
588 {{
"pool",
"dataset"}}));
589 metrics.push_back(create_base_metric(
590 "dicom_dataset_pool_hits_total",
591 static_cast<double>(ds_pool.pool_hits.load(std::memory_order_relaxed)),
593 {{
"pool",
"dataset"}}));
594 metrics.push_back(create_base_metric(
595 "dicom_dataset_pool_misses_total",
596 static_cast<double>(ds_pool.pool_misses.load(std::memory_order_relaxed)),
598 {{
"pool",
"dataset"}}));
599 metrics.push_back(create_base_metric(
600 "dicom_dataset_pool_hit_ratio",
603 {{
"pool",
"dataset"}}));
606 const auto& pdu_pool =
pacs.pdu_buffer_pool();
607 metrics.push_back(create_base_metric(
608 "dicom_pdu_buffer_pool_acquisitions_total",
609 static_cast<double>(pdu_pool.total_acquisitions.load(std::memory_order_relaxed)),
611 {{
"pool",
"pdu_buffer"}}));
612 metrics.push_back(create_base_metric(
613 "dicom_pdu_buffer_pool_hits_total",
614 static_cast<double>(pdu_pool.pool_hits.load(std::memory_order_relaxed)),
616 {{
"pool",
"pdu_buffer"}}));
617 metrics.push_back(create_base_metric(
618 "dicom_pdu_buffer_pool_misses_total",
619 static_cast<double>(pdu_pool.pool_misses.load(std::memory_order_relaxed)),
621 {{
"pool",
"pdu_buffer"}}));
622 metrics.push_back(create_base_metric(
623 "dicom_pdu_buffer_pool_hit_ratio",
624 pdu_pool.hit_ratio(),
626 {{
"pool",
"pdu_buffer"}}));
629inline void dicom_metrics_collector::collect_dimse_operation_metrics(
630 std::vector<dicom_metric>& metrics,
631 const std::string& op_name,
634 std::unordered_map<std::string, std::string> op_tags{{
"operation", op_name}};
637 metrics.push_back(create_base_metric(
638 "dicom_" + op_name +
"_total",
639 static_cast<double>(
counter.total_count()),
644 metrics.push_back(create_base_metric(
645 "dicom_" + op_name +
"_success_total",
646 static_cast<double>(
counter.success_count.load(std::memory_order_relaxed)),
651 metrics.push_back(create_base_metric(
652 "dicom_" + op_name +
"_failure_total",
653 static_cast<double>(
counter.failure_count.load(std::memory_order_relaxed)),
658 if (
counter.total_count() > 0) {
660 metrics.push_back(create_base_metric(
661 "dicom_" + op_name +
"_duration_seconds_avg",
662 static_cast<double>(
counter.average_duration_us()) / 1'000'000.0,
667 metrics.push_back(create_base_metric(
668 "dicom_" + op_name +
"_duration_seconds_sum",
669 static_cast<double>(
counter.total_duration_us.load(std::memory_order_relaxed)) / 1'000'000.0,
674 const auto min_us =
counter.min_duration_us.load(std::memory_order_relaxed);
675 if (min_us != UINT64_MAX) {
676 metrics.push_back(create_base_metric(
677 "dicom_" + op_name +
"_duration_seconds_min",
678 static_cast<double>(min_us) / 1'000'000.0,
684 const auto max_us =
counter.max_duration_us.load(std::memory_order_relaxed);
686 metrics.push_back(create_base_metric(
687 "dicom_" + op_name +
"_duration_seconds_max",
688 static_cast<double>(max_us) / 1'000'000.0,
CRTP base class for DICOM metric collectors.
dicom_metric create_base_metric(const std::string &name, double value, const std::string &type, const std::unordered_map< std::string, std::string > &extra_tags={}) const
CRTP-based unified DICOM metrics collector.
void set_collect_storage(bool enabled)
Enable or disable storage metrics collection.
dicom_metrics_collector(dicom_metrics_collector &&)=delete
bool collect_associations_
dicom_metrics_collector()=default
Default constructor.
void set_collect_pools(bool enabled)
Enable or disable pool metrics collection.
void do_add_statistics(stats_map &stats) const
Add collector-specific statistics.
void collect_query_metrics(std::vector< dicom_metric > &metrics)
void collect_transfer_metrics(std::vector< dicom_metric > &metrics)
dicom_metrics_snapshot get_last_snapshot() const
Get last collected snapshot.
dicom_metrics_collector & operator=(const dicom_metrics_collector &)=delete
void set_collect_queries(bool enabled)
Enable or disable query metrics collection.
void collect_storage_metrics(std::vector< dicom_metric > &metrics)
dicom_metrics_snapshot get_snapshot() const
Get a snapshot of current metrics.
static constexpr const char * collector_name
Collector name for CRTP base class.
std::mutex snapshot_mutex_
dicom_metrics_collector(const dicom_metrics_collector &)=delete
void collect_dimse_operation_metrics(std::vector< dicom_metric > &metrics, const std::string &op_name, const operation_counter &counter)
void set_collect_associations(bool enabled)
Enable or disable association metrics collection.
bool do_initialize(const config_map &config)
Collector-specific initialization.
void collect_association_metrics(std::vector< dicom_metric > &metrics)
dicom_metrics_snapshot last_snapshot_
bool is_available() const
Check if DICOM metrics collection is available.
std::vector< std::string > do_get_metric_types() const
Get supported metric types.
void set_collect_transfers(bool enabled)
Enable or disable transfer metrics collection.
void collect_pool_metrics(std::vector< dicom_metric > &metrics)
std::vector< dicom_metric > do_collect()
Collect all DICOM metrics.
~dicom_metrics_collector() override=default
Destructor.
dicom_metrics_collector & operator=(dicom_metrics_collector &&)=delete
const data_transfer_metrics & transfer() const noexcept
Get data transfer metrics.
static pacs_metrics & global_metrics() noexcept
Get the global singleton instance.
const association_counters & associations() const noexcept
Get association counters.
CRTP base class for DICOM metrics collectors.
@ c_store
C-STORE (Storage Service)
@ c_find
C-FIND (Query Service)
@ c_get
C-GET (Retrieve Service)
@ n_create
N-CREATE (MPPS)
@ c_move
C-MOVE (Retrieve Service)
@ c_echo
C-ECHO (Verification Service)
std::unordered_map< std::string, double > stats_map
Type alias for statistics map.
@ counter
Monotonic increasing value.
std::unordered_map< std::string, std::string > config_map
Type alias for configuration map.
Operation metrics collection for PACS DICOM services.
Snapshot of all DICOM metrics at a point in time.
std::uint64_t total_associations
std::uint64_t peak_active_associations
std::uint64_t store_operations
double avg_store_latency_ms
std::chrono::system_clock::time_point timestamp
std::uint64_t failed_stores
double avg_query_latency_ms
std::uint64_t failed_queries
std::uint64_t successful_stores
std::uint64_t bytes_received
std::uint64_t failed_associations
std::uint64_t query_operations
std::uint64_t successful_queries
std::uint64_t active_associations
std::uint64_t images_received
std::uint64_t images_sent
Atomic counter for tracking operation success/failure counts.