diff --git a/userspace/libsinsp/metrics_collector.cpp b/userspace/libsinsp/metrics_collector.cpp index 72991d81bf..9cf3e39f52 100644 --- a/userspace/libsinsp/metrics_collector.cpp +++ b/userspace/libsinsp/metrics_collector.cpp @@ -55,6 +55,22 @@ static const char *const sinsp_stats_v2_resource_utilization_names[] = { [SINSP_STATS_V2_N_CONTAINERS] = "n_containers", }; +// For simplicity, needs to stay in sync w/ typedef enum metrics_v2_value_unit +static const char *const metrics_unit_name_mappings[] = { + [METRIC_VALUE_UNIT_COUNT] = "COUNT", + [METRIC_VALUE_UNIT_PERC] = "PERC", + [METRIC_VALUE_UNIT_MEMORY_BYTES] = "MEMORY_BYTES", + [METRIC_VALUE_UNIT_MEMORY_KILOBYTES] = "MEMORY_KILOBYTES", + [METRIC_VALUE_UNIT_MEMORY_MEGABYTES] = "MEMORY_MEGABYTES", + [METRIC_VALUE_UNIT_TIME_NS] = "TIME_NS", +}; + +// For simplicity, needs to stay in sync w/ typedef enum metrics_v2_metric_type +static const char *const metrics_metric_type_name_mappings[] = { + [METRIC_VALUE_MONOTONIC] = "MONOTONIC", + [METRIC_VALUE_NON_MONOTONIC_CURRENT] = "NON_MONOTONIC_CURRENT", +}; + namespace libsinsp::metrics { void metrics_collector::get_rss_vsz_pss_total_memory_and_open_fds(uint32_t &rss, uint32_t &vsz, uint32_t &pss, uint64_t &memory_used_host, uint64_t &open_fds_host) @@ -440,6 +456,43 @@ void metrics_collector::snapshot() } } +const std::string metrics_collector::convert_metric_to_prometheus_text(std::string metric_name, metrics_v2 metric) const +{ + std::string prometheus_text = metric_name; + prometheus_text += "{raw_name=\"" + std::string(metric.name) + "\",unit=\"" + std::string(metrics_unit_name_mappings[metric.unit]) \ + + "\",metric_type=\"" + std::string(metrics_metric_type_name_mappings[metric.metric_type]) + "\"} "; // white space at the end important! + switch (metric.type) + { + case METRIC_VALUE_TYPE_U32: + prometheus_text += std::to_string(metric.value.u32); + break; + case METRIC_VALUE_TYPE_S32: + prometheus_text += std::to_string(metric.value.s32); + break; + case METRIC_VALUE_TYPE_U64: + prometheus_text += std::to_string(metric.value.u64); + break; + case METRIC_VALUE_TYPE_S64: + prometheus_text += std::to_string(metric.value.s64); + break; + case METRIC_VALUE_TYPE_D: + prometheus_text += std::to_string(metric.value.d); + break; + case METRIC_VALUE_TYPE_F: + prometheus_text += std::to_string(metric.value.f); + break; + case METRIC_VALUE_TYPE_I: + prometheus_text += std::to_string(metric.value.i); + break; + default: + break; + } + + prometheus_text += " "; + prometheus_text += std::to_string(sinsp_utils::get_current_time_ns()); + return prometheus_text; +} + const std::vector& metrics_collector::get_metrics() const { return m_metrics; diff --git a/userspace/libsinsp/metrics_collector.h b/userspace/libsinsp/metrics_collector.h index bd7bbcf619..f7330101d2 100644 --- a/userspace/libsinsp/metrics_collector.h +++ b/userspace/libsinsp/metrics_collector.h @@ -87,13 +87,31 @@ class metrics_collector public: metrics_collector(sinsp* inspector, uint32_t flags, bool convert_memory_to_mb); - // Method to fill up m_metrics_buffer with metrics; refreshes m_metrics with up-to-date metrics on each call + /*! + \brief Method to fill up m_metrics_buffer with metrics; refreshes m_metrics with up-to-date metrics on each call + */ void snapshot(); - // Method to get a const reference to m_metrics vector + /*! + \brief Method to get a const reference to m_metrics vector + */ const std::vector& get_metrics() const; - // Method to convert memory units; tied to metrics_v2 definitions + /*! + \brief Method to convert a metric to the text-based Prometheus format. + * Reference: https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md + * Note: The design idea is to expose Prometheus metrics by piping text-based formats to new line-delimited fields + * exposed at /metrics in Falco or a similar approach, eliminating the need for implementing a full Prometheus client. + * Example: + * test.memory_used_host{raw_name="memory_used_host", unit="MEMORY_MEGABYTES", metric_type="NON_MONOTONIC_CURRENT"} 15096.000000 1706490801547502000 + * + * This method is a work in progress. + */ + const std::string convert_metric_to_prometheus_text(std::string metric_name, metrics_v2 metric) const; + + /*! + \brief Method to convert memory units; tied to metrics_v2 definitions + */ template double convert_memory(metrics_v2_value_unit source_unit, metrics_v2_value_unit dest_unit, T val) { diff --git a/userspace/libsinsp/test/sinsp_metrics.ut.cpp b/userspace/libsinsp/test/sinsp_metrics.ut.cpp index 091905b239..87f7b50e42 100644 --- a/userspace/libsinsp/test/sinsp_metrics.ut.cpp +++ b/userspace/libsinsp/test/sinsp_metrics.ut.cpp @@ -146,5 +146,19 @@ TEST_F(sinsp_with_test_input, sinsp_metrics_collector) converted_memory = metrics_collector->convert_memory(METRIC_VALUE_UNIT_MEMORY_MEGABYTES, METRIC_VALUE_UNIT_MEMORY_MEGABYTES, (uint64_t)50); ASSERT_EQ(converted_memory, 50); + /* Test public convert_metric_to_prometheus_text */ + for (const auto& metric: metrics_snapshot) + { + // This resembles the Falco client use case + char metric_name[METRIC_NAME_MAX] = "test."; + strlcat(metric_name, metric.name, sizeof(metric_name)); + std::string prometheus_text = metrics_collector->convert_metric_to_prometheus_text(metric_name, metric); + if (strncmp(metric.name, "n_missing_container_images", 17) == 0) + { + std::string prometheus_text_substring = "test.n_missing_container_images{raw_name=\"n_missing_container_images\",unit=\"COUNT\",metric_type=\"NON_MONOTONIC_CURRENT\"} 0"; + ASSERT_TRUE(prometheus_text.find(prometheus_text_substring) != std::string::npos) << "Substring not found in prometheus_text"; + } + ASSERT_GT(prometheus_text.length(), 8); + } } #endif