Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
4dc849fa
Commit
4dc849fa
authored
Jun 17, 2020
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Streamline metric registry API and metric naming
parent
0db5680e
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
401 additions
and
84 deletions
+401
-84
libcaf_core/caf/telemetry/collector/prometheus.hpp
libcaf_core/caf/telemetry/collector/prometheus.hpp
+13
-0
libcaf_core/caf/telemetry/dbl_gauge.hpp
libcaf_core/caf/telemetry/dbl_gauge.hpp
+2
-0
libcaf_core/caf/telemetry/histogram.hpp
libcaf_core/caf/telemetry/histogram.hpp
+10
-3
libcaf_core/caf/telemetry/int_gauge.hpp
libcaf_core/caf/telemetry/int_gauge.hpp
+2
-0
libcaf_core/caf/telemetry/label.hpp
libcaf_core/caf/telemetry/label.hpp
+2
-0
libcaf_core/caf/telemetry/metric_family_impl.hpp
libcaf_core/caf/telemetry/metric_family_impl.hpp
+26
-2
libcaf_core/caf/telemetry/metric_impl.hpp
libcaf_core/caf/telemetry/metric_impl.hpp
+2
-0
libcaf_core/caf/telemetry/metric_registry.hpp
libcaf_core/caf/telemetry/metric_registry.hpp
+144
-28
libcaf_core/src/actor_registry.cpp
libcaf_core/src/actor_registry.cpp
+1
-1
libcaf_core/src/telemetry/collector/prometheus.cpp
libcaf_core/src/telemetry/collector/prometheus.cpp
+103
-3
libcaf_core/src/telemetry/label.cpp
libcaf_core/src/telemetry/label.cpp
+5
-0
libcaf_core/src/telemetry/metric_registry.cpp
libcaf_core/src/telemetry/metric_registry.cpp
+20
-5
libcaf_core/test/telemetry/collector/prometheus.cpp
libcaf_core/test/telemetry/collector/prometheus.cpp
+21
-7
libcaf_core/test/telemetry/metric_registry.cpp
libcaf_core/test/telemetry/metric_registry.cpp
+45
-30
libcaf_io/src/detail/prometheus_broker.cpp
libcaf_io/src/detail/prometheus_broker.cpp
+5
-5
No files found.
libcaf_core/caf/telemetry/collector/prometheus.hpp
View file @
4dc849fa
...
@@ -75,12 +75,22 @@ public:
...
@@ -75,12 +75,22 @@ public:
void
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
void
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
int_gauge
*
gauge
);
const
int_gauge
*
gauge
);
void
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
dbl_histogram
*
val
);
void
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
int_histogram
*
val
);
private:
private:
/// Sets `current_family_` if not pointing to `family` already. When setting
/// Sets `current_family_` if not pointing to `family` already. When setting
/// the member variable, also writes meta information to `buf_`.
/// the member variable, also writes meta information to `buf_`.
void
set_current_family
(
const
metric_family
*
family
,
void
set_current_family
(
const
metric_family
*
family
,
string_view
prometheus_type
);
string_view
prometheus_type
);
template
<
class
ValueType
>
void
append_histogram
(
const
metric_family
*
family
,
const
metric
*
instance
,
const
histogram
<
ValueType
>*
val
);
/// Stores the generated text output.
/// Stores the generated text output.
char_buffer
buf_
;
char_buffer
buf_
;
...
@@ -90,6 +100,9 @@ private:
...
@@ -90,6 +100,9 @@ private:
/// Caches type information and help text for a metric.
/// Caches type information and help text for a metric.
std
::
unordered_map
<
const
metric_family
*
,
char_buffer
>
meta_info_
;
std
::
unordered_map
<
const
metric_family
*
,
char_buffer
>
meta_info_
;
/// Caches type information and help text for a metric.
std
::
unordered_map
<
const
metric
*
,
std
::
vector
<
char_buffer
>>
virtual_metrics_
;
/// Caches which metric family is currently collected.
/// Caches which metric family is currently collected.
const
metric_family
*
current_family_
=
nullptr
;
const
metric_family
*
current_family_
=
nullptr
;
...
...
libcaf_core/caf/telemetry/dbl_gauge.hpp
View file @
4dc849fa
...
@@ -35,6 +35,8 @@ public:
...
@@ -35,6 +35,8 @@ public:
using
value_type
=
double
;
using
value_type
=
double
;
using
family_setting
=
unit_t
;
// -- constants --------------------------------------------------------------
// -- constants --------------------------------------------------------------
static
constexpr
metric_type
runtime_type
=
metric_type
::
dbl_gauge
;
static
constexpr
metric_type
runtime_type
=
metric_type
::
dbl_gauge
;
...
...
libcaf_core/caf/telemetry/histogram.hpp
View file @
4dc849fa
...
@@ -39,6 +39,8 @@ public:
...
@@ -39,6 +39,8 @@ public:
using
gauge_type
=
gauge
<
value_type
>
;
using
gauge_type
=
gauge
<
value_type
>
;
using
family_setting
=
std
::
vector
<
value_type
>
;
struct
bucket_type
{
struct
bucket_type
{
value_type
upper_bound
;
value_type
upper_bound
;
gauge_type
gauge
;
gauge_type
gauge
;
...
@@ -52,7 +54,7 @@ public:
...
@@ -52,7 +54,7 @@ public:
// -- constructors, destructors, and assignment operators --------------------
// -- constructors, destructors, and assignment operators --------------------
histogram
(
span
<
const
value_type
>
upper_bounds
)
{
explicit
histogram
(
span
<
const
value_type
>
upper_bounds
)
{
using
limits
=
std
::
numeric_limits
<
value_type
>
;
using
limits
=
std
::
numeric_limits
<
value_type
>
;
CAF_ASSERT
(
std
::
is_sorted
(
upper_bounds
.
begin
(),
upper_bounds
.
end
()));
CAF_ASSERT
(
std
::
is_sorted
(
upper_bounds
.
begin
(),
upper_bounds
.
end
()));
num_buckets_
=
upper_bounds
.
size
()
+
1
;
num_buckets_
=
upper_bounds
.
size
()
+
1
;
...
@@ -66,11 +68,15 @@ public:
...
@@ -66,11 +68,15 @@ public:
buckets_
[
index
].
upper_bound
=
limits
::
max
();
buckets_
[
index
].
upper_bound
=
limits
::
max
();
}
}
histogram
(
std
::
initializer_list
<
value_type
>
upper_bounds
)
explicit
histogram
(
std
::
initializer_list
<
value_type
>
upper_bounds
)
:
histogram
(
make_span
(
upper_bounds
.
begin
(),
upper_bounds
.
size
()))
{
:
histogram
(
make_span
(
upper_bounds
.
begin
(),
upper_bounds
.
size
()))
{
// nop
// nop
}
}
histogram
(
const
histogram
&
)
=
delete
;
histogram
&
operator
=
(
const
histogram
&
)
=
delete
;
~
histogram
()
{
~
histogram
()
{
delete
[]
buckets_
;
delete
[]
buckets_
;
}
}
...
@@ -106,9 +112,10 @@ private:
...
@@ -106,9 +112,10 @@ private:
gauge_type
sum_
;
gauge_type
sum_
;
};
};
///
///
Convenience alias for a histogram with value type `double`.
using
dbl_histogram
=
histogram
<
double
>
;
using
dbl_histogram
=
histogram
<
double
>
;
/// Convenience alias for a histogram with value type `int64_t`.
using
int_histogram
=
histogram
<
int64_t
>
;
using
int_histogram
=
histogram
<
int64_t
>
;
}
// namespace caf::telemetry
}
// namespace caf::telemetry
libcaf_core/caf/telemetry/int_gauge.hpp
View file @
4dc849fa
...
@@ -35,6 +35,8 @@ public:
...
@@ -35,6 +35,8 @@ public:
using
value_type
=
int64_t
;
using
value_type
=
int64_t
;
using
family_setting
=
unit_t
;
// -- constants --------------------------------------------------------------
// -- constants --------------------------------------------------------------
static
constexpr
metric_type
runtime_type
=
metric_type
::
int_gauge
;
static
constexpr
metric_type
runtime_type
=
metric_type
::
int_gauge
;
...
...
libcaf_core/caf/telemetry/label.hpp
View file @
4dc849fa
...
@@ -59,6 +59,8 @@ public:
...
@@ -59,6 +59,8 @@ public:
str_
.
size
()
-
name_length_
-
1
};
str_
.
size
()
-
name_length_
-
1
};
}
}
void
value
(
string_view
new_value
);
/// Returns the label in `name=value` notation.
/// Returns the label in `name=value` notation.
const
std
::
string
&
str
()
const
noexcept
{
const
std
::
string
&
str
()
const
noexcept
{
return
str_
;
return
str_
;
...
...
libcaf_core/caf/telemetry/metric_family_impl.hpp
View file @
4dc849fa
...
@@ -36,9 +36,21 @@ namespace caf::telemetry {
...
@@ -36,9 +36,21 @@ namespace caf::telemetry {
template
<
class
Type
>
template
<
class
Type
>
class
metric_family_impl
:
public
metric_family
{
class
metric_family_impl
:
public
metric_family
{
public:
public:
using
super
=
metric_family
;
using
impl_type
=
metric_impl
<
Type
>
;
using
impl_type
=
metric_impl
<
Type
>
;
using
metric_family
::
metric_family
;
using
extra_setting_type
=
typename
impl_type
::
family_setting
;
template
<
class
...
Ts
>
metric_family_impl
(
std
::
string
prefix
,
std
::
string
name
,
std
::
vector
<
std
::
string
>
label_names
,
std
::
string
helptext
,
std
::
string
unit
,
bool
is_sum
,
Ts
&&
...
xs
)
:
super
(
std
::
move
(
prefix
),
std
::
move
(
name
),
std
::
move
(
label_names
),
std
::
move
(
helptext
),
std
::
move
(
unit
),
is_sum
),
extra_setting_
(
std
::
forward
<
Ts
>
(
xs
)...)
{
// nop
}
metric_type
type
()
const
noexcept
override
{
metric_type
type
()
const
noexcept
override
{
return
Type
::
runtime_type
;
return
Type
::
runtime_type
;
...
@@ -55,7 +67,12 @@ public:
...
@@ -55,7 +67,12 @@ public:
if
(
m
==
metrics_
.
end
())
{
if
(
m
==
metrics_
.
end
())
{
std
::
vector
<
label
>
cpy
{
labels
.
begin
(),
labels
.
end
()};
std
::
vector
<
label
>
cpy
{
labels
.
begin
(),
labels
.
end
()};
std
::
sort
(
cpy
.
begin
(),
cpy
.
end
());
std
::
sort
(
cpy
.
begin
(),
cpy
.
end
());
m
=
metrics_
.
emplace
(
m
,
std
::
make_unique
<
impl_type
>
(
std
::
move
(
cpy
)));
std
::
unique_ptr
<
impl_type
>
ptr
;
if
constexpr
(
std
::
is_same
<
extra_setting_type
,
unit_t
>::
value
)
ptr
.
reset
(
new
impl_type
(
std
::
move
(
cpy
)));
else
ptr
.
reset
(
new
impl_type
(
std
::
move
(
cpy
),
extra_setting_
));
m
=
metrics_
.
emplace
(
m
,
std
::
move
(
ptr
));
}
}
return
std
::
addressof
(
m
->
get
()
->
impl
());
return
std
::
addressof
(
m
->
get
()
->
impl
());
}
}
...
@@ -64,6 +81,12 @@ public:
...
@@ -64,6 +81,12 @@ public:
return
get_or_add
(
span
<
const
label_view
>
{
labels
.
begin
(),
labels
.
size
()});
return
get_or_add
(
span
<
const
label_view
>
{
labels
.
begin
(),
labels
.
size
()});
}
}
// -- properties --
const
auto
&
extra_setting
()
const
noexcept
{
return
extra_setting_
;
}
template
<
class
Collector
>
template
<
class
Collector
>
void
collect
(
Collector
&
collector
)
const
{
void
collect
(
Collector
&
collector
)
const
{
std
::
unique_lock
<
std
::
mutex
>
guard
{
mx_
};
std
::
unique_lock
<
std
::
mutex
>
guard
{
mx_
};
...
@@ -72,6 +95,7 @@ public:
...
@@ -72,6 +95,7 @@ public:
}
}
private:
private:
extra_setting_type
extra_setting_
;
mutable
std
::
mutex
mx_
;
mutable
std
::
mutex
mx_
;
std
::
vector
<
std
::
unique_ptr
<
impl_type
>>
metrics_
;
std
::
vector
<
std
::
unique_ptr
<
impl_type
>>
metrics_
;
};
};
...
...
libcaf_core/caf/telemetry/metric_impl.hpp
View file @
4dc849fa
...
@@ -31,6 +31,8 @@ namespace caf::telemetry {
...
@@ -31,6 +31,8 @@ namespace caf::telemetry {
template
<
class
Type
>
template
<
class
Type
>
class
metric_impl
:
public
metric
{
class
metric_impl
:
public
metric
{
public:
public:
using
family_setting
=
typename
Type
::
family_setting
;
template
<
class
...
Ts
>
template
<
class
...
Ts
>
metric_impl
(
std
::
vector
<
label
>
labels
,
Ts
&&
...
xs
)
metric_impl
(
std
::
vector
<
label
>
labels
,
Ts
&&
...
xs
)
:
metric
(
std
::
move
(
labels
)),
impl_
(
std
::
forward
<
Ts
>
(
xs
)...)
{
:
metric
(
std
::
move
(
labels
)),
impl_
(
std
::
forward
<
Ts
>
(
xs
)...)
{
...
...
libcaf_core/caf/telemetry/metric_registry.hpp
View file @
4dc849fa
This diff is collapsed.
Click to expand it.
libcaf_core/src/actor_registry.cpp
View file @
4dc849fa
...
@@ -49,7 +49,7 @@ actor_registry::~actor_registry() {
...
@@ -49,7 +49,7 @@ actor_registry::~actor_registry() {
}
}
actor_registry
::
actor_registry
(
actor_system
&
sys
)
:
system_
(
sys
)
{
actor_registry
::
actor_registry
(
actor_system
&
sys
)
:
system_
(
sys
)
{
running_
=
sys
.
telemetry
().
singleton
<
telemetry
::
int_gauge
>
(
running_
=
sys
.
telemetry
().
gauge_singleton
(
"caf"
,
"running_actors"
,
"Number of currently running actors."
);
"caf"
,
"running_actors"
,
"Number of currently running actors."
);
}
}
...
...
libcaf_core/src/telemetry/collector/prometheus.cpp
View file @
4dc849fa
...
@@ -48,6 +48,10 @@ struct ms_timestamp {
...
@@ -48,6 +48,10 @@ struct ms_timestamp {
ms_timestamp
&
operator
=
(
const
ms_timestamp
&
)
=
default
;
ms_timestamp
&
operator
=
(
const
ms_timestamp
&
)
=
default
;
};
};
struct
underline_to_hyphen
{
string_view
str
;
};
void
append
(
prometheus
::
char_buffer
&
)
{
void
append
(
prometheus
::
char_buffer
&
)
{
// End of recursion.
// End of recursion.
}
}
...
@@ -55,6 +59,9 @@ void append(prometheus::char_buffer&) {
...
@@ -55,6 +59,9 @@ void append(prometheus::char_buffer&) {
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
string_view
,
Ts
&&
...);
void
append
(
prometheus
::
char_buffer
&
,
string_view
,
Ts
&&
...);
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
underline_to_hyphen
,
Ts
&&
...);
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
char
,
Ts
&&
...);
void
append
(
prometheus
::
char_buffer
&
,
char
,
Ts
&&
...);
...
@@ -68,18 +75,31 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs);
...
@@ -68,18 +75,31 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs);
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
const
metric_family
*
,
Ts
&&
...);
void
append
(
prometheus
::
char_buffer
&
,
const
metric_family
*
,
Ts
&&
...);
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
const
std
::
vector
<
label
>&
,
Ts
&&
...);
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
const
metric
*
,
Ts
&&
...);
void
append
(
prometheus
::
char_buffer
&
,
const
metric
*
,
Ts
&&
...);
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
ms_timestamp
,
Ts
&&
...);
void
append
(
prometheus
::
char_buffer
&
,
ms_timestamp
,
Ts
&&
...);
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
,
const
prometheus
::
char_buffer
&
,
Ts
&&
...);
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
string_view
str
,
Ts
&&
...
xs
)
{
void
append
(
prometheus
::
char_buffer
&
buf
,
string_view
str
,
Ts
&&
...
xs
)
{
buf
.
insert
(
buf
.
end
(),
str
.
begin
(),
str
.
end
());
buf
.
insert
(
buf
.
end
(),
str
.
begin
(),
str
.
end
());
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
underline_to_hyphen
x
,
Ts
&&
...
xs
)
{
for
(
auto
c
:
x
.
str
)
buf
.
emplace_back
(
c
!=
'-'
?
c
:
'_'
);
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
}
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
char
ch
,
Ts
&&
...
xs
)
{
void
append
(
prometheus
::
char_buffer
&
buf
,
char
ch
,
Ts
&&
...
xs
)
{
buf
.
emplace_back
(
ch
);
buf
.
emplace_back
(
ch
);
...
@@ -111,7 +131,8 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs) {
...
@@ -111,7 +131,8 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs) {
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
const
metric_family
*
family
,
void
append
(
prometheus
::
char_buffer
&
buf
,
const
metric_family
*
family
,
Ts
&&
...
xs
)
{
Ts
&&
...
xs
)
{
append
(
buf
,
family
->
prefix
(),
'_'
,
family
->
name
());
append
(
buf
,
underline_to_hyphen
{
family
->
prefix
()},
'_'
,
underline_to_hyphen
{
family
->
name
()});
if
(
family
->
unit
()
!=
"1"
_sv
)
if
(
family
->
unit
()
!=
"1"
_sv
)
append
(
buf
,
'_'
,
family
->
unit
());
append
(
buf
,
'_'
,
family
->
unit
());
if
(
family
->
is_sum
())
if
(
family
->
is_sum
())
...
@@ -120,8 +141,8 @@ void append(prometheus::char_buffer& buf, const metric_family* family,
...
@@ -120,8 +141,8 @@ void append(prometheus::char_buffer& buf, const metric_family* family,
}
}
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
const
metric
*
instance
,
Ts
&&
...
xs
)
{
void
append
(
prometheus
::
char_buffer
&
buf
,
const
std
::
vector
<
label
>&
labels
,
const
auto
&
labels
=
instance
->
labels
();
Ts
&&
...
xs
)
{
if
(
!
labels
.
empty
())
{
if
(
!
labels
.
empty
())
{
append
(
buf
,
'{'
);
append
(
buf
,
'{'
);
auto
i
=
labels
.
begin
();
auto
i
=
labels
.
begin
();
...
@@ -133,12 +154,24 @@ void append(prometheus::char_buffer& buf, const metric* instance, Ts&&... xs) {
...
@@ -133,12 +154,24 @@ void append(prometheus::char_buffer& buf, const metric* instance, Ts&&... xs) {
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
const
metric
*
instance
,
Ts
&&
...
xs
)
{
append
(
buf
,
instance
->
labels
(),
std
::
forward
<
Ts
>
(
xs
)...);
}
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
ms_timestamp
ts
,
Ts
&&
...
xs
)
{
void
append
(
prometheus
::
char_buffer
&
buf
,
ms_timestamp
ts
,
Ts
&&
...
xs
)
{
append
(
buf
,
ts
.
value
);
append
(
buf
,
ts
.
value
);
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
template
<
class
...
Ts
>
void
append
(
prometheus
::
char_buffer
&
buf
,
const
prometheus
::
char_buffer
&
x
,
Ts
&&
...
xs
)
{
buf
.
insert
(
buf
.
end
(),
x
.
begin
(),
x
.
end
());
append
(
buf
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
// namespace
}
// namespace
string_view
prometheus
::
collect_from
(
const
metric_registry
&
registry
,
string_view
prometheus
::
collect_from
(
const
metric_registry
&
registry
,
...
@@ -170,6 +203,16 @@ void prometheus::operator()(const metric_family* family, const metric* instance,
...
@@ -170,6 +203,16 @@ void prometheus::operator()(const metric_family* family, const metric* instance,
'\n'
);
'\n'
);
}
}
void
prometheus
::
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
dbl_histogram
*
val
)
{
append_histogram
(
family
,
instance
,
val
);
}
void
prometheus
::
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
int_histogram
*
val
)
{
append_histogram
(
family
,
instance
,
val
);
}
void
prometheus
::
set_current_family
(
const
metric_family
*
family
,
void
prometheus
::
set_current_family
(
const
metric_family
*
family
,
string_view
prometheus_type
)
{
string_view
prometheus_type
)
{
if
(
current_family_
==
family
)
if
(
current_family_
==
family
)
...
@@ -185,4 +228,61 @@ void prometheus::set_current_family(const metric_family* family,
...
@@ -185,4 +228,61 @@ void prometheus::set_current_family(const metric_family* family,
buf_
.
insert
(
buf_
.
end
(),
i
->
second
.
begin
(),
i
->
second
.
end
());
buf_
.
insert
(
buf_
.
end
(),
i
->
second
.
begin
(),
i
->
second
.
end
());
}
}
namespace
{
template
<
class
ValueType
>
auto
make_virtual_metrics
(
const
metric_family
*
family
,
const
metric
*
instance
,
const
histogram
<
ValueType
>*
val
)
{
std
::
vector
<
prometheus
::
char_buffer
>
result
;
auto
add_result
=
[
&
](
auto
&&
...
xs
)
{
result
.
emplace_back
();
append
(
result
.
back
(),
std
::
forward
<
decltype
(
xs
)
>
(
xs
)...);
};
auto
buckets
=
val
->
buckets
();
auto
num_buckets
=
buckets
.
size
();
CAF_ASSERT
(
num_buckets
>
1
);
auto
labels
=
instance
->
labels
();
labels
.
emplace_back
(
"le"
,
""
);
result
.
reserve
(
num_buckets
+
2
);
size_t
index
=
0
;
// Create bucket variable names for 1..N-1.
for
(;
index
<
num_buckets
-
1
;
++
index
)
{
auto
upper_bound
=
std
::
to_string
(
buckets
[
index
].
upper_bound
);
labels
.
back
().
value
(
upper_bound
);
add_result
(
family
,
"_bucket"
,
labels
,
' '
);
}
// The last bucket always sets le="+Inf"
labels
.
back
().
value
(
"+Inf"
);
add_result
(
family
,
"_bucket"
,
labels
,
' '
);
labels
.
pop_back
();
add_result
(
family
,
"_sum"
,
labels
,
' '
);
add_result
(
family
,
"_count"
,
labels
,
' '
);
return
result
;
}
}
// namespace
template
<
class
ValueType
>
void
prometheus
::
append_histogram
(
const
metric_family
*
family
,
const
metric
*
instance
,
const
histogram
<
ValueType
>*
val
)
{
auto
i
=
virtual_metrics_
.
find
(
instance
);
if
(
i
==
virtual_metrics_
.
end
())
{
auto
metrics
=
make_virtual_metrics
(
family
,
instance
,
val
);
i
=
virtual_metrics_
.
emplace
(
instance
,
std
::
move
(
metrics
)).
first
;
}
set_current_family
(
family
,
"histogram"
);
auto
&
vm
=
i
->
second
;
auto
buckets
=
val
->
buckets
();
size_t
index
=
0
;
for
(;
index
<
buckets
.
size
()
-
1
;
++
index
)
{
append
(
buf_
,
vm
[
index
],
buckets
[
index
].
gauge
.
value
(),
' '
,
ms_timestamp
{
now_
},
'\n'
);
}
auto
count
=
buckets
[
index
].
gauge
.
value
();
append
(
buf_
,
vm
[
index
],
count
,
' '
,
ms_timestamp
{
now_
},
'\n'
);
append
(
buf_
,
vm
[
++
index
],
val
->
sum
(),
' '
,
ms_timestamp
{
now_
},
'\n'
);
append
(
buf_
,
vm
[
++
index
],
count
,
' '
,
ms_timestamp
{
now_
},
'\n'
);
}
}
// namespace caf::telemetry::collector
}
// namespace caf::telemetry::collector
libcaf_core/src/telemetry/label.cpp
View file @
4dc849fa
...
@@ -33,6 +33,11 @@ label::label(const label_view& view) : label(view.name(), view.value()) {
...
@@ -33,6 +33,11 @@ label::label(const label_view& view) : label(view.name(), view.value()) {
// nop
// nop
}
}
void
label
::
value
(
string_view
new_value
)
{
str_
.
erase
(
name_length_
+
1
);
str_
.
insert
(
str_
.
end
(),
new_value
.
begin
(),
new_value
.
end
());
}
int
label
::
compare
(
const
label
&
x
)
const
noexcept
{
int
label
::
compare
(
const
label
&
x
)
const
noexcept
{
return
str_
.
compare
(
x
.
str
());
return
str_
.
compare
(
x
.
str
());
}
}
...
...
libcaf_core/src/telemetry/metric_registry.cpp
View file @
4dc849fa
...
@@ -19,13 +19,21 @@
...
@@ -19,13 +19,21 @@
#include "caf/telemetry/metric_registry.hpp"
#include "caf/telemetry/metric_registry.hpp"
#include "caf/config.hpp"
#include "caf/config.hpp"
#include "caf/raise_error.hpp"
#include "caf/telemetry/dbl_gauge.hpp"
#include "caf/telemetry/dbl_gauge.hpp"
#include "caf/telemetry/int_gauge.hpp"
#include "caf/telemetry/int_gauge.hpp"
#include "caf/telemetry/metric_family_impl.hpp"
#include "caf/telemetry/metric_family_impl.hpp"
#include "caf/telemetry/metric_impl.hpp"
#include "caf/telemetry/metric_impl.hpp"
#include "caf/telemetry/metric_type.hpp"
#include "caf/telemetry/metric_type.hpp"
namespace
{
template
<
class
Range1
,
class
Range2
>
bool
equals
(
Range1
&&
xs
,
Range2
&&
ys
)
{
return
std
::
equal
(
xs
.
begin
(),
xs
.
end
(),
ys
.
begin
(),
ys
.
end
());
}
}
// namespace
namespace
caf
::
telemetry
{
namespace
caf
::
telemetry
{
metric_registry
::
metric_registry
()
{
metric_registry
::
metric_registry
()
{
...
@@ -38,19 +46,26 @@ metric_registry::~metric_registry() {
...
@@ -38,19 +46,26 @@ metric_registry::~metric_registry() {
metric_family
*
metric_registry
::
fetch
(
const
string_view
&
prefix
,
metric_family
*
metric_registry
::
fetch
(
const
string_view
&
prefix
,
const
string_view
&
name
)
{
const
string_view
&
name
)
{
auto
matches
=
[
&
](
const
auto
&
ptr
)
{
auto
eq
=
[
&
](
const
auto
&
ptr
)
{
return
ptr
->
prefix
()
==
prefix
&&
ptr
->
name
()
==
name
;
return
ptr
->
prefix
()
==
prefix
&&
ptr
->
name
()
==
name
;
};
};
if
(
auto
i
=
std
::
find_if
(
dbl_gauges_
.
begin
(),
dbl_gauges_
.
end
(),
matches
);
if
(
auto
i
=
std
::
find_if
(
dbl_gauges_
.
begin
(),
dbl_gauges_
.
end
(),
eq
);
i
!=
dbl_gauges_
.
end
())
i
!=
dbl_gauges_
.
end
())
return
i
->
get
();
return
i
->
get
();
if
(
auto
i
=
std
::
find_if
(
int_gauges_
.
begin
(),
int_gauges_
.
end
(),
matches
);
if
(
auto
i
=
std
::
find_if
(
int_gauges_
.
begin
(),
int_gauges_
.
end
(),
eq
);
i
!=
int_gauges_
.
end
())
i
!=
int_gauges_
.
end
())
return
i
->
get
();
return
i
->
get
();
if
(
auto
i
=
std
::
find_if
(
dbl_histograms_
.
begin
(),
dbl_histograms_
.
end
(),
eq
);
i
!=
dbl_histograms_
.
end
())
return
i
->
get
();
if
(
auto
i
=
std
::
find_if
(
int_histograms_
.
begin
(),
int_histograms_
.
end
(),
eq
);
i
!=
int_histograms_
.
end
())
return
i
->
get
();
return
nullptr
;
return
nullptr
;
}
}
void
metric_registry
::
assert_properties
(
metric_family
*
ptr
,
metric_type
type
,
void
metric_registry
::
assert_properties
(
const
metric_family
*
ptr
,
metric_type
type
,
span
<
const
string_view
>
label_names
,
span
<
const
string_view
>
label_names
,
string_view
unit
,
bool
is_sum
)
{
string_view
unit
,
bool
is_sum
)
{
auto
labels_match
=
[
&
]
{
auto
labels_match
=
[
&
]
{
...
...
libcaf_core/test/telemetry/collector/prometheus.cpp
View file @
4dc849fa
...
@@ -32,7 +32,6 @@ using namespace caf::telemetry;
...
@@ -32,7 +32,6 @@ using namespace caf::telemetry;
namespace
{
namespace
{
struct
fixture
{
struct
fixture
{
using
ig
=
int_gauge
;
collector
::
prometheus
exporter
;
collector
::
prometheus
exporter
;
metric_registry
registry
;
metric_registry
registry
;
};
};
...
@@ -42,16 +41,23 @@ struct fixture {
...
@@ -42,16 +41,23 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
prometheus_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
prometheus_tests
,
fixture
)
CAF_TEST
(
the
Prometheus
collector
generates
text
output
)
{
CAF_TEST
(
the
Prometheus
collector
generates
text
output
)
{
auto
fb
=
registry
.
family
<
ig
>
(
"foo"
,
"bar"
,
{},
"Some value without labels."
,
auto
fb
=
registry
.
gauge_family
(
"foo"
,
"bar"
,
{},
"seconds"
);
"Some value without labels."
,
"seconds"
);
auto
sv
=
registry
.
family
<
ig
>
(
"some"
,
"value"
,
{
"a"
,
"b"
},
auto
sv
=
registry
.
gauge_family
(
"some"
,
"value"
,
{
"a"
,
"b"
},
"Some (total) value with two labels."
,
"1"
,
"Some (total) value with two labels."
,
"1"
,
true
);
true
);
auto
ov
=
registry
.
family
<
ig
>
(
"other"
,
"value"
,
{
"x"
},
""
,
"seconds"
,
true
);
auto
ov
=
registry
.
gauge_family
(
"other"
,
"value"
,
{
"x"
},
""
,
"seconds"
,
true
);
std
::
vector
<
int64_t
>
upper_bounds
{
1
,
2
,
4
};
auto
sr
=
registry
.
histogram_family
(
"some"
,
"request-duration"
,
{
"x"
},
upper_bounds
,
"Some help."
,
"seconds"
);
fb
->
get_or_add
({})
->
value
(
123
);
fb
->
get_or_add
({})
->
value
(
123
);
sv
->
get_or_add
({{
"a"
,
"1"
},
{
"b"
,
"2"
}})
->
value
(
12
);
sv
->
get_or_add
({{
"a"
,
"1"
},
{
"b"
,
"2"
}})
->
value
(
12
);
sv
->
get_or_add
({{
"b"
,
"1"
},
{
"a"
,
"2"
}})
->
value
(
21
);
sv
->
get_or_add
({{
"b"
,
"1"
},
{
"a"
,
"2"
}})
->
value
(
21
);
ov
->
get_or_add
({{
"x"
,
"true"
}})
->
value
(
31337
);
ov
->
get_or_add
({{
"x"
,
"true"
}})
->
value
(
31337
);
auto
h
=
sr
->
get_or_add
({{
"x"
,
"get"
}});
h
->
observe
(
3
);
h
->
observe
(
4
);
h
->
observe
(
7
);
CAF_CHECK_EQUAL
(
exporter
.
collect_from
(
registry
,
42
),
CAF_CHECK_EQUAL
(
exporter
.
collect_from
(
registry
,
42
),
R"(# HELP foo_bar_seconds Some value without labels.
R"(# HELP foo_bar_seconds Some value without labels.
# TYPE foo_bar_seconds gauge
# TYPE foo_bar_seconds gauge
...
@@ -62,6 +68,14 @@ some_value_total{a="1",b="2"} 12 42000
...
@@ -62,6 +68,14 @@ some_value_total{a="1",b="2"} 12 42000
some_value_total{a="2",b="1"} 21 42000
some_value_total{a="2",b="1"} 21 42000
# TYPE other_value_seconds_total gauge
# TYPE other_value_seconds_total gauge
other_value_seconds_total{x="true"} 31337 42000
other_value_seconds_total{x="true"} 31337 42000
# HELP some_request_duration_seconds Some help.
# TYPE some_request_duration_seconds histogram
some_request_duration_seconds_bucket{x="get",le="1"} 0 42000
some_request_duration_seconds_bucket{x="get",le="2"} 0 42000
some_request_duration_seconds_bucket{x="get",le="4"} 2 42000
some_request_duration_seconds_bucket{x="get",le="+Inf"} 1 42000
some_request_duration_seconds_sum{x="get"} 14 42000
some_request_duration_seconds_count{x="get"} 1 42000
)"
_sv
);
)"
_sv
);
CAF_MESSAGE
(
"multiple runs generate the same output"
);
CAF_MESSAGE
(
"multiple runs generate the same output"
);
std
::
string
res1
;
std
::
string
res1
;
...
...
libcaf_core/test/telemetry/metric_registry.cpp
View file @
4dc849fa
...
@@ -48,17 +48,24 @@ struct test_collector {
...
@@ -48,17 +48,24 @@ struct test_collector {
result
+=
std
::
to_string
(
wrapped
->
value
());
result
+=
std
::
to_string
(
wrapped
->
value
());
}
}
template
<
class
T
>
void
operator
()(
const
metric_family
*
family
,
const
metric
*
instance
,
const
histogram
<
T
>*
wrapped
)
{
concat
(
family
,
instance
);
result
+=
std
::
to_string
(
wrapped
->
sum
());
}
void
concat
(
const
metric_family
*
family
,
const
metric
*
instance
)
{
void
concat
(
const
metric_family
*
family
,
const
metric
*
instance
)
{
result
+=
'\n'
;
result
+=
'\n'
;
result
+=
family
->
prefix
();
result
+=
family
->
prefix
();
result
+=
'
_
'
;
result
+=
'
.
'
;
result
+=
family
->
name
();
result
+=
family
->
name
();
if
(
family
->
unit
()
!=
"1"
)
{
if
(
family
->
unit
()
!=
"1"
)
{
result
+=
'
_
'
;
result
+=
'
.
'
;
result
+=
family
->
unit
();
result
+=
family
->
unit
();
}
}
if
(
family
->
is_sum
())
if
(
family
->
is_sum
())
result
+=
"
_
total"
;
result
+=
"
.
total"
;
if
(
!
instance
->
labels
().
empty
())
{
if
(
!
instance
->
labels
().
empty
())
{
result
+=
'{'
;
result
+=
'{'
;
auto
i
=
instance
->
labels
().
begin
();
auto
i
=
instance
->
labels
().
begin
();
...
@@ -81,7 +88,6 @@ struct test_collector {
...
@@ -81,7 +88,6 @@ struct test_collector {
};
};
struct
fixture
{
struct
fixture
{
using
ig
=
int_gauge
;
metric_registry
registry
;
metric_registry
registry
;
test_collector
collector
;
test_collector
collector
;
};
};
...
@@ -91,8 +97,11 @@ struct fixture {
...
@@ -91,8 +97,11 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
metric_registry_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
metric_registry_tests
,
fixture
)
CAF_TEST
(
registries
lazily
create
metrics
)
{
CAF_TEST
(
registries
lazily
create
metrics
)
{
auto
f
=
registry
.
family
<
ig
>
(
"caf"
,
"running_actors"
,
{
"var1"
,
"var2"
},
std
::
vector
<
int64_t
>
upper_bounds
{
1
,
2
,
4
,
8
};
"How many actors are currently running?"
);
auto
f
=
registry
.
gauge_family
(
"caf"
,
"running-actors"
,
{
"var1"
,
"var2"
},
"How many actors are currently running?"
);
auto
g
=
registry
.
histogram_family
(
"caf"
,
"response-time"
,
{
"var1"
,
"var2"
},
upper_bounds
,
"How long take requests?"
);
std
::
vector
<
label_view
>
v1
{{
"var1"
,
"foo"
},
{
"var2"
,
"bar"
}};
std
::
vector
<
label_view
>
v1
{{
"var1"
,
"foo"
},
{
"var2"
,
"bar"
}};
std
::
vector
<
label_view
>
v1_reversed
{{
"var2"
,
"bar"
},
{
"var1"
,
"foo"
}};
std
::
vector
<
label_view
>
v1_reversed
{{
"var2"
,
"bar"
},
{
"var1"
,
"foo"
}};
std
::
vector
<
label_view
>
v2
{{
"var1"
,
"bar"
},
{
"var2"
,
"foo"
}};
std
::
vector
<
label_view
>
v2
{{
"var1"
,
"bar"
},
{
"var2"
,
"foo"
}};
...
@@ -103,27 +112,33 @@ CAF_TEST(registries lazily create metrics) {
...
@@ -103,27 +112,33 @@ CAF_TEST(registries lazily create metrics) {
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v1_reversed
)
->
value
(),
42
);
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v1_reversed
)
->
value
(),
42
);
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v2
)
->
value
(),
23
);
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v2
)
->
value
(),
23
);
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v2_reversed
)
->
value
(),
23
);
CAF_CHECK_EQUAL
(
f
->
get_or_add
(
v2_reversed
)
->
value
(),
23
);
g
->
get_or_add
(
v1
)
->
observe
(
3
);
g
->
get_or_add
(
v2
)
->
observe
(
7
);
CAF_CHECK_EQUAL
(
g
->
get_or_add
(
v1
)
->
sum
(),
3
);
CAF_CHECK_EQUAL
(
g
->
get_or_add
(
v1_reversed
)
->
sum
(),
3
);
CAF_CHECK_EQUAL
(
g
->
get_or_add
(
v2
)
->
sum
(),
7
);
CAF_CHECK_EQUAL
(
g
->
get_or_add
(
v2_reversed
)
->
sum
(),
7
);
}
}
CAF_TEST
(
registries
allow
users
to
collect
all
registered
metrics
)
{
CAF_TEST
(
registries
allow
users
to
collect
all
registered
metrics
)
{
auto
fb
=
registry
.
family
<
ig
>
(
"foo"
,
"bar"
,
{},
"Some value without labels."
,
auto
fb
=
registry
.
gauge_family
(
"foo"
,
"bar"
,
{}
,
"seconds"
);
"Some value without labels."
,
"seconds"
);
auto
sv
=
registry
.
family
<
ig
>
(
"some"
,
"value"
,
{
"a"
,
"b"
},
auto
sv
=
registry
.
gauge_family
(
"some"
,
"value"
,
{
"a"
,
"b"
},
"Some (total) value with two labels."
,
"1"
,
"Some (total) value with two labels."
,
"1"
,
true
);
true
);
auto
ov
=
registry
.
family
<
ig
>
(
"other"
,
"value"
,
{
"x"
},
auto
ov
=
registry
.
gauge_family
(
"other"
,
"value"
,
{
"x"
},
"Some (total) seconds with one label."
,
"Some (total) seconds with one label."
,
"seconds"
,
true
);
"seconds"
,
true
);
auto
ra
=
registry
.
family
<
ig
>
(
"caf"
,
"running_
actors"
,
{
"node"
},
auto
ra
=
registry
.
gauge_family
(
"caf"
,
"running-
actors"
,
{
"node"
},
"How many actors are running?"
);
"How many actors are running?"
);
auto
ms
=
registry
.
family
<
ig
>
(
"caf"
,
"mailbox_
size"
,
{
"name"
},
auto
ms
=
registry
.
gauge_family
(
"caf"
,
"mailbox-
size"
,
{
"name"
},
"How full is the mailbox?"
);
"How full is the mailbox?"
);
CAF_MESSAGE
(
"the registry always returns the same family object"
);
CAF_MESSAGE
(
"the registry always returns the same family object"
);
CAF_CHECK_EQUAL
(
fb
,
registry
.
family
<
ig
>
(
"foo"
,
"bar"
,
{},
""
,
"seconds"
));
CAF_CHECK_EQUAL
(
fb
,
registry
.
gauge_family
(
"foo"
,
"bar"
,
{},
""
,
"seconds"
));
CAF_CHECK_EQUAL
(
sv
,
registry
.
family
<
ig
>
(
"some"
,
"value"
,
{
"a"
,
"b"
},
""
,
"1
"
,
CAF_CHECK_EQUAL
(
sv
,
registry
.
gauge_family
(
"some"
,
"value"
,
{
"a"
,
"b"
},
"
"
,
true
));
"1"
,
true
));
CAF_CHECK_EQUAL
(
sv
,
registry
.
family
<
ig
>
(
"some"
,
"value"
,
{
"b"
,
"a"
},
""
,
"1
"
,
CAF_CHECK_EQUAL
(
sv
,
registry
.
gauge_family
(
"some"
,
"value"
,
{
"b"
,
"a"
},
"
"
,
true
));
"1"
,
true
));
CAF_MESSAGE
(
"families always return the same metric object for given labels"
);
CAF_MESSAGE
(
"families always return the same metric object for given labels"
);
CAF_CHECK_EQUAL
(
fb
->
get_or_add
({}),
fb
->
get_or_add
({}));
CAF_CHECK_EQUAL
(
fb
->
get_or_add
({}),
fb
->
get_or_add
({}));
CAF_CHECK_EQUAL
(
sv
->
get_or_add
({{
"a"
,
"1"
},
{
"b"
,
"2"
}}),
CAF_CHECK_EQUAL
(
sv
->
get_or_add
({{
"a"
,
"1"
},
{
"b"
,
"2"
}}),
...
@@ -138,13 +153,13 @@ CAF_TEST(registries allow users to collect all registered metrics) {
...
@@ -138,13 +153,13 @@ CAF_TEST(registries allow users to collect all registered metrics) {
ms
->
get_or_add
({{
"name"
,
"parser"
}})
->
value
(
12
);
ms
->
get_or_add
({{
"name"
,
"parser"
}})
->
value
(
12
);
registry
.
collect
(
collector
);
registry
.
collect
(
collector
);
CAF_CHECK_EQUAL
(
collector
.
result
,
R"(
CAF_CHECK_EQUAL
(
collector
.
result
,
R"(
foo
_bar_
seconds 123
foo
.bar.
seconds 123
some
_value_
total{a="1",b="2"} 12
some
.value.
total{a="1",b="2"} 12
some
_value_
total{a="2",b="1"} 21
some
.value.
total{a="2",b="1"} 21
other
_value_seconds_
total{x="true"} 31337
other
.value.seconds.
total{x="true"} 31337
caf
_running_
actors{node="localhost"} 42
caf
.running-
actors{node="localhost"} 42
caf
_mailbox_
size{name="printer"} 3
caf
.mailbox-
size{name="printer"} 3
caf
_mailbox_
size{name="parser"} 12)"
);
caf
.mailbox-
size{name="parser"} 12)"
);
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_io/src/detail/prometheus_broker.cpp
View file @
4dc849fa
...
@@ -192,12 +192,12 @@ prometheus_broker::prometheus_broker(actor_config& cfg) : io::broker(cfg) {
...
@@ -192,12 +192,12 @@ prometheus_broker::prometheus_broker(actor_config& cfg) : io::broker(cfg) {
using
telemetry
::
dbl_gauge
;
using
telemetry
::
dbl_gauge
;
using
telemetry
::
int_gauge
;
using
telemetry
::
int_gauge
;
auto
&
reg
=
system
().
telemetry
();
auto
&
reg
=
system
().
telemetry
();
cpu_time_
=
reg
.
singleton
<
dbl_gaug
e
>
(
cpu_time_
=
reg
.
gauge_singleton
<
doubl
e
>
(
"process"
,
"cpu"
,
"Total user and system CPU time spent."
,
"seconds"
,
true
);
"process"
,
"cpu"
,
"Total user and system CPU time spent."
,
"seconds"
,
true
);
mem_size_
=
reg
.
singleton
<
int_gauge
>
(
"process"
,
"resident_memory"
,
mem_size_
=
reg
.
gauge_singleton
(
"process"
,
"resident_memory"
,
"Resident memory size."
,
"bytes"
);
"Resident memory size."
,
"bytes"
);
virt_mem_size_
=
reg
.
singleton
<
int_gauge
>
(
"process"
,
"virtual_memory"
,
virt_mem_size_
=
reg
.
gauge_singleton
(
"process"
,
"virtual_memory"
,
"Virtual memory size."
,
"bytes"
);
"Virtual memory size."
,
"bytes"
);
#endif // HAS_PROCESS_METRICS
#endif // HAS_PROCESS_METRICS
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment