-
Notifications
You must be signed in to change notification settings - Fork 169
/
Copy pathmetrics_collector.h
349 lines (319 loc) · 13.1 KB
/
metrics_collector.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <libscap/metrics_v2.h>
#include <libscap/scap_machine_info.h>
#include <libsinsp/threadinfo.h>
#include <libscap/strl.h>
#include <cmath>
#include <memory>
#include <optional>
#include <string_view>
#include <map>
struct sinsp_stats_v2 {
///@(
/** fdtable state related counters, unit: count. */
uint64_t m_n_noncached_fd_lookups;
uint64_t m_n_cached_fd_lookups;
uint64_t m_n_failed_fd_lookups;
uint64_t m_n_added_fds;
uint64_t m_n_removed_fds;
///@)
///@(
/** evt parsing related counters, unit: count. */
uint64_t m_n_stored_evts;
uint64_t m_n_store_evts_drops;
uint64_t m_n_retrieved_evts;
uint64_t m_n_retrieve_evts_drops;
///@)
///@(
/** threadtable state related counters, unit: count. */
uint64_t m_n_noncached_thread_lookups;
uint64_t m_n_cached_thread_lookups;
uint64_t m_n_failed_thread_lookups;
uint64_t m_n_added_threads;
uint64_t m_n_removed_threads;
///@)
uint32_t m_n_drops_full_threadtable; ///< Number of drops due to full threadtable, unit: count.
uint32_t
m_n_missing_container_images; ///< Number of cached containers (cgroups) without
///< container info such as image, hijacked
///< sinsp_container_manager::remove_inactive_containers()
///< -> every flush snapshot update, unit: count.
uint32_t m_n_containers; ///< Number of containers (cgroups) currently cached by
///< sinsp_container_manager, hijacked
///< sinsp_container_manager::remove_inactive_containers() -> every
///< flush snapshot update, unit: count.
};
namespace libs::metrics {
template<typename T>
double convert_memory(metrics_v2_value_unit source_unit, metrics_v2_value_unit dest_unit, T val) {
double factor = 1;
switch(source_unit) {
case METRIC_VALUE_UNIT_MEMORY_BYTES:
factor = 1;
break;
case METRIC_VALUE_UNIT_MEMORY_KIBIBYTES:
factor = 1024.;
break;
case METRIC_VALUE_UNIT_MEMORY_MEGABYTES:
factor = 1024. * 1024.;
break;
default:
return 0;
}
double bytes_val = val * factor;
switch(dest_unit) {
case METRIC_VALUE_UNIT_MEMORY_BYTES:
return bytes_val;
case METRIC_VALUE_UNIT_MEMORY_KIBIBYTES:
return std::round((bytes_val / 1024.) * 10.) / 10.; // round to 1 decimal
case METRIC_VALUE_UNIT_MEMORY_MEGABYTES:
return std::round((bytes_val / 1024. / 1024.) * 10.) / 10.; // round to 1 decimal
default:
return 0;
}
}
class metrics_converter {
public:
virtual ~metrics_converter() = default;
virtual std::string convert_metric_to_text(const metrics_v2& metric) const;
virtual void convert_metric_to_unit_convention(metrics_v2& metric) const = 0;
};
// Subclass for Prometheus-specific metric conversion
class prometheus_metrics_converter : public metrics_converter {
public:
/*!
\brief Method to convert a metrics_v2 metric to the text-based Prometheus exposition 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's existing HTTP webserver (w/ optional mTLS support),
eliminating the need for implementing
* a complete Prometheus client.
*
* We exclusively support counter and gauge Prometheus metric types, covering metrics from
kernel driver tracepoints
* to linsinsp and client metrics. Introducing a registry seems excessive, especially given the
dynamic nature of the final
* metric string names, such as variations in tracepoints across architectures.
* Considering the simplistic use case, adding another dependency to the project does not seem
justified. Furthermore, for C++
* (compared to Go for example), there appear to be fewer formal client library projects
available. Plus, we need to think
* about stability and long-term support before adding any new dependency.
*
* The final fully qualified Prometheus metric name partially follows
https://prometheus.io/docs/practices/naming/
* Prepend namespace and subsystem with "_" delimiter to create a fully qualified metric name
according to
* https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#Opts + append unit with "_"
delimiter
* We do not strictly follow and enforce the concept of base_units, but guarantee no units are
mixed per unique
* `prometheus_metric_name_fully_qualified`
*
* We are monitoring updates wrt
/~https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md
*
* Example:
*
* # HELP testns_falco_n_threads_total https://falco.org/docs/metrics/
* # TYPE testns_falco_n_threads_total gauge
* testns_falco_n_threads_total{raw_name="n_threads",example_key1="example1",example_key2="example2"} 12
* # HELP testns_falco_memory_rss_megabytes https://falco.org/docs/metrics/
* # TYPE testns_falco_memory_rss_megabytes gauge
* testns_falco_memory_rss_megabytes{raw_name="memory_rss",example_key1="example1",example_key2="example2"} 350.000000
*
* This method is a work in progress.
*
* @param metric metrics_v2 metric
* @param prometheus_namespace first component of `prometheus_metric_name_fully_qualified`
(optional)
* @param prometheus_subsystem second component of `prometheus_metric_name_fully_qualified`
(optional)
* @param const_labels map of additional labels (rarely used for a metrics_v2 metric)
* @return Complete new line delimited text-based Prometheus exposition format metric string
* w/ a `prometheus_metric_name_fully_qualified` - optional components prepended to and unit
appended to.
* 3-lines including # HELP and # TYPE lines followed by the metric line, raw metric name always
present as label.
*/
std::string convert_metric_to_text_prometheus(
const metrics_v2& metric,
std::string_view prometheus_namespace = "",
std::string_view prometheus_subsystem = "",
const std::map<std::string, std::string>& const_labels = {}) const;
/*!
\brief Overloaded method to convert a pseudo-metric / software version like metric_name to the
text-based Prometheus exposition format.
*
* Note: Instead of using const_labels, which is a rare use case according to
* https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels,
* exposing an overload to support metrics similar to
https://www.robustperception.io/exposing-the-software-version-to-prometheus/.
* This approach is applicable to https://falco.org/docs/metrics/, such as Falco's "Base Fields"
like
* falco.kernel_release and falco.version.
*
* Example:
*
* # HELP testns_falco_kernel_release_info https://falco.org/docs/metrics/
* # TYPE testns_falco_kernel_release_info gauge
* testns_falco_kernel_release_info{raw_name="kernel_release",kernel_release="6.6.7-200.fc39.x86_64"} 1
*
* @param metric_name raw metric name
* @param prometheus_namespace first component of `prometheus_metric_name_fully_qualified`
(optional)
* @param prometheus_subsystem second component of `prometheus_metric_name_fully_qualified`
(optional)
* @param const_labels map of additional labels (typically used in software version like
metrics)
* @return Complete new line delimited text-based Prometheus exposition format metric string
* w/ a `prometheus_metric_name_fully_qualified` - optional components prepended to and unit
appended to.
* 3-lines including # HELP and # TYPE lines followed by the metric line, raw metric name always
present as label.
*/
std::string convert_metric_to_text_prometheus(
std::string_view metric_name,
std::string_view prometheus_namespace = "",
std::string_view prometheus_subsystem = "",
const std::map<std::string, std::string>& const_labels = {}) const;
/*!
* \brief Method to convert metric units to Prometheus base units.
*
* \note Metric names shall be updated within this method, and the respective
* Prometheus-compliant unit suffix shall be added. Prometheus compliance means every metric
* name has a unit suffix, see https://prometheus.io/docs/practices/naming/ or
* https://prometheus.io/docs/practices/naming/#base-units. We conform to the best practices
* except for keeping libbpf stats metrics and timestamps in nanoseconds to avoid precision loss
* when converting them to seconds. Please note that, for example, even cAdvisor sometimes
* deviates from the standards, e.g., `container_memory_rss` instead of
* `container_memory_rss_bytes`. `metric.unit` is also modified and always matches the metric
* name unit suffix.
*
* In summary, effectively for Falco/libs, it just means converting all memory to bytes and CPU
* usage to a ratio.
*/
void convert_metric_to_unit_convention(metrics_v2& metric) const override;
};
// Subclass for output_rule-specific metric conversion
class output_rule_metrics_converter : public metrics_converter {
public:
/*!
\brief Method to convert metric units of memory-related metrics to mb
*
* \note metrics names w/ unit suffix shall be updated within this method.
* `metric.unit` is also modified and always matches the metric name unit suffix if applicable.
*
*/
void convert_metric_to_unit_convention(metrics_v2& metric) const override;
};
class libsinsp_metrics {
public:
template<typename T>
static void set_metric_value(metrics_v2& metric, metrics_v2_value_type type, T val) {
switch(type) {
case METRIC_VALUE_TYPE_U32:
metric.value.u32 = static_cast<uint32_t>(val);
break;
case METRIC_VALUE_TYPE_S32:
metric.value.s32 = static_cast<int32_t>(val);
break;
case METRIC_VALUE_TYPE_U64:
metric.value.u64 = static_cast<uint64_t>(val);
break;
case METRIC_VALUE_TYPE_S64:
metric.value.s64 = static_cast<int64_t>(val);
break;
case METRIC_VALUE_TYPE_D:
metric.value.d = static_cast<double>(val);
break;
case METRIC_VALUE_TYPE_F:
metric.value.f = static_cast<float>(val);
break;
case METRIC_VALUE_TYPE_I:
metric.value.i = static_cast<int>(val);
break;
default:
break;
}
}
/*!
\brief Method to create a new metrics_v2
*/
template<typename T>
static inline metrics_v2 new_metric(const char* name,
uint32_t flags,
metrics_v2_value_type type,
metrics_v2_value_unit unit,
metrics_v2_metric_type metric_type,
T val) {
metrics_v2 metric;
strlcpy(metric.name, name, METRIC_NAME_MAX);
metric.flags = flags;
metric.type = type;
metric.unit = unit;
metric.metric_type = metric_type;
set_metric_value(metric, type, val);
return metric;
}
libsinsp_metrics() = default;
libsinsp_metrics(const libsinsp_metrics&) = default;
libsinsp_metrics(libsinsp_metrics&&) = delete;
libsinsp_metrics& operator=(const libsinsp_metrics&) = default;
libsinsp_metrics& operator=(libsinsp_metrics&&) = delete;
virtual ~libsinsp_metrics() = default;
virtual std::vector<metrics_v2> to_metrics() { return std::vector<metrics_v2>(); };
};
class libs_state_counters : libsinsp_metrics {
public:
libs_state_counters(const std::shared_ptr<sinsp_stats_v2>& sinsp_stats_v2,
sinsp_thread_manager* thread_manager);
std::vector<metrics_v2> to_metrics() override;
private:
std::shared_ptr<sinsp_stats_v2> m_sinsp_stats_v2;
uint64_t m_n_fds; ///< Total number of fds currently stored across all threadtables associated
///< with each active thread in the sinsp state thread table, unit: count.
uint64_t m_n_threads; ///< Total number of threads currently stored in the sinsp state thread
///< table, unit: count.
};
class libs_metrics_collector {
public:
libs_metrics_collector(sinsp* inspector, uint32_t flags);
/*!
\brief Method to fill up m_metrics_buffer with metrics; refreshes m_metrics with up-to-date
metrics on each call
*/
void snapshot();
/*!
\brief Method to get a const reference to m_metrics vector
*/
const std::vector<metrics_v2>& get_metrics() const;
/*!
\brief Method to get a non-const reference to m_metrics vector
*/
std::vector<metrics_v2>& get_metrics();
private:
sinsp* m_inspector;
std::shared_ptr<sinsp_stats_v2> m_sinsp_stats_v2;
uint32_t m_metrics_flags = METRICS_V2_KERNEL_COUNTERS | METRICS_V2_LIBBPF_STATS |
METRICS_V2_RESOURCE_UTILIZATION | METRICS_V2_STATE_COUNTERS |
METRICS_V2_PLUGINS | METRICS_V2_KERNEL_COUNTERS_PER_CPU;
std::vector<metrics_v2> m_metrics;
};
} // namespace libs::metrics