[metrics] Introduce metrics API framework
This commit introduces the shared code for the metrics API framework: - Metrics API definitions - Code to set up the varlink server - The describe method which shows all the metrics families - The list method which lists all the metrics - Type definitions related to MetricFamily - Common code to build json objects
This commit is contained in:
@@ -126,6 +126,7 @@ shared_sources = files(
|
||||
'machine-id-setup.c',
|
||||
'macvlan-util.c',
|
||||
'main-func.c',
|
||||
'metrics.c',
|
||||
'mkdir-label.c',
|
||||
'mkfs-util.c',
|
||||
'module-util.c',
|
||||
@@ -206,6 +207,7 @@ shared_sources = files(
|
||||
'varlink-io.systemd.MachineImage.c',
|
||||
'varlink-io.systemd.ManagedOOM.c',
|
||||
'varlink-io.systemd.Manager.c',
|
||||
'varlink-io.systemd.Metrics.c',
|
||||
'varlink-io.systemd.MountFileSystem.c',
|
||||
'varlink-io.systemd.MuteConsole.c',
|
||||
'varlink-io.systemd.NamespaceResource.c',
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
#include "metrics.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "varlink-io.systemd.Metrics.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
static void metric_family_context_done(MetricFamilyContext *ctx) {
|
||||
assert(ctx);
|
||||
|
||||
sd_json_variant_unref(ctx->previous);
|
||||
}
|
||||
|
||||
int metrics_setup_varlink_server(
|
||||
sd_varlink_server **server, /* in and out param */
|
||||
sd_varlink_server_flags_t flags,
|
||||
sd_event *event,
|
||||
sd_varlink_method_t vl_method_list_cb,
|
||||
sd_varlink_method_t vl_method_describe_cb,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(server);
|
||||
assert(event);
|
||||
|
||||
if (*server)
|
||||
return 0;
|
||||
|
||||
r = varlink_server_new(&s, flags, userdata);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Metrics);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add varlink metrics interface to varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.Metrics.List",
|
||||
vl_method_list_cb,
|
||||
"io.systemd.Metrics.Describe",
|
||||
vl_method_describe_cb);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to register varlink metrics methods: %m");
|
||||
|
||||
r = sd_varlink_server_set_description(s, "systemd varlink metrics server");
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to set varlink metrics server description: %m");
|
||||
|
||||
r = sd_varlink_server_attach_event(s, event, SD_EVENT_PRIORITY_NORMAL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to attach varlink metrics connection to event loop: %m");
|
||||
|
||||
*server = TAKE_PTR(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const metric_family_type_table[_METRIC_FAMILY_TYPE_MAX] = {
|
||||
[METRIC_FAMILY_TYPE_COUNTER] = "counter",
|
||||
[METRIC_FAMILY_TYPE_GAUGE] = "gauge",
|
||||
[METRIC_FAMILY_TYPE_STRING] = "string",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(metric_family_type, MetricFamilyType);
|
||||
|
||||
static int metric_family_build_send(sd_varlink *link, const MetricFamily *mf, bool more) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(mf);
|
||||
|
||||
r = sd_json_buildo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", mf->name),
|
||||
SD_JSON_BUILD_PAIR_STRING("description", mf->description),
|
||||
SD_JSON_BUILD_PAIR_STRING("type", metric_family_type_to_string(mf->type)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (more)
|
||||
return sd_varlink_notify(link, v);
|
||||
|
||||
return sd_varlink_reply(link, v);
|
||||
}
|
||||
|
||||
int metrics_method_describe(
|
||||
const MetricFamily metric_family_table[],
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(metric_family_table);
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
const MetricFamily *previous = NULL;
|
||||
for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) {
|
||||
if (previous) {
|
||||
r = metric_family_build_send(link, previous, /* more= */ true);
|
||||
if (r < 0)
|
||||
return log_debug_errno(
|
||||
r, "Failed to describe metric family '%s': %m", previous->name);
|
||||
}
|
||||
|
||||
previous = mf;
|
||||
}
|
||||
|
||||
if (!previous)
|
||||
return sd_varlink_error(link, "io.systemd.Metrics.NoSuchMetric", NULL);
|
||||
|
||||
r = metric_family_build_send(link, previous, /* more= */ false);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to describe metric family '%s': %m", previous->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metrics_method_list(
|
||||
const MetricFamily metric_family_table[],
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(metric_family_table);
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
_cleanup_(metric_family_context_done) MetricFamilyContext ctx = { .link = link };
|
||||
for (const MetricFamily *mf = metric_family_table; mf && mf->name; mf++) {
|
||||
assert(mf->generate_cb);
|
||||
|
||||
ctx.metric_family = mf;
|
||||
r = mf->generate_cb(&ctx, userdata);
|
||||
if (r < 0)
|
||||
return log_debug_errno(
|
||||
r, "Failed to list metrics for metric family '%s': %m", mf->name);
|
||||
}
|
||||
|
||||
if (!ctx.previous)
|
||||
return sd_varlink_error(link, "io.systemd.Metrics.NoSuchMetric", NULL);
|
||||
|
||||
/* produce the last metric */
|
||||
return sd_varlink_reply(link, ctx.previous);
|
||||
}
|
||||
|
||||
static int metric_set_fields(sd_json_variant **v, char **field_pairs) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
|
||||
n = strv_length(field_pairs);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (n % 2 != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ERANGE), "Odd number of field pairs: %zu", n);
|
||||
|
||||
sd_json_variant **array = new0(sd_json_variant *, n);
|
||||
if (!array)
|
||||
return log_oom();
|
||||
|
||||
CLEANUP_ARRAY(array, n, sd_json_variant_unref_many);
|
||||
|
||||
int i = 0;
|
||||
STRV_FOREACH_PAIR(key, value, field_pairs) {
|
||||
r = sd_json_variant_new_string(&array[i++], *key);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create key variant: %m");
|
||||
|
||||
r = sd_json_variant_new_string(&array[i++], *value);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create value variant: %m");
|
||||
}
|
||||
|
||||
r = sd_json_variant_new_object(&w, array, n);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to allocate JSON object: %m");
|
||||
|
||||
return sd_json_variant_set_field(v, "fields", w);
|
||||
}
|
||||
|
||||
static int metric_build_send(MetricFamilyContext *context, const char *object, sd_json_variant *value, char **field_pairs) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(value);
|
||||
assert(context->link);
|
||||
assert(context->metric_family);
|
||||
|
||||
r = sd_json_buildo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", context->metric_family->name),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object),
|
||||
SD_JSON_BUILD_PAIR("value", SD_JSON_BUILD_VARIANT(value)));
|
||||
/* TODO JSON_BUILD_PAIR_OBJECT_STRV_NOT_NULL */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (field_pairs) { /* NULL => no fields object, empty strv => fields:{} */
|
||||
r = metric_set_fields(&v, field_pairs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (context->previous) {
|
||||
r = sd_varlink_notify(context->link, context->previous);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
context->previous = sd_json_variant_unref(context->previous);
|
||||
}
|
||||
|
||||
context->previous = TAKE_PTR(v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metric_build_send_string(MetricFamilyContext *context, const char *object, const char *value, char **field_pairs) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(value);
|
||||
|
||||
r = sd_json_variant_new_string(&v, value);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to allocate JSON string: %m");
|
||||
|
||||
return metric_build_send(context, object, v, field_pairs);
|
||||
}
|
||||
|
||||
int metric_build_send_unsigned(MetricFamilyContext *context, const char *object, uint64_t value, char **field_pairs) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_json_variant_new_unsigned(&v, value);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to allocate JSON unsigned: %m");
|
||||
|
||||
return metric_build_send(context, object, v, field_pairs);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "macro-fundamental.h"
|
||||
|
||||
typedef enum MetricFamilyType {
|
||||
METRIC_FAMILY_TYPE_COUNTER,
|
||||
METRIC_FAMILY_TYPE_GAUGE,
|
||||
METRIC_FAMILY_TYPE_STRING,
|
||||
_METRIC_FAMILY_TYPE_MAX,
|
||||
_METRIC_FAMILY_TYPE_INVALID = -EINVAL,
|
||||
} MetricFamilyType;
|
||||
|
||||
typedef struct MetricFamily MetricFamily;
|
||||
|
||||
typedef struct MetricFamilyContext {
|
||||
const MetricFamily* metric_family;
|
||||
sd_varlink *link;
|
||||
sd_json_variant *previous;
|
||||
} MetricFamilyContext;
|
||||
|
||||
typedef int (*metric_family_generate_cb_t) (MetricFamilyContext *mfc, void *userdata);
|
||||
|
||||
typedef struct MetricFamily {
|
||||
const char *name;
|
||||
const char *description;
|
||||
MetricFamilyType type;
|
||||
metric_family_generate_cb_t generate_cb;
|
||||
} MetricFamily;
|
||||
|
||||
int metrics_setup_varlink_server(
|
||||
sd_varlink_server **server, /* in and out param */
|
||||
sd_varlink_server_flags_t flags,
|
||||
sd_event *event,
|
||||
sd_varlink_method_t vl_method_list_cb,
|
||||
sd_varlink_method_t vl_method_describe_cb,
|
||||
void *userdata);
|
||||
|
||||
const char* metric_family_type_to_string(MetricFamilyType i) _const_;
|
||||
int metrics_method_describe(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
|
||||
int metrics_method_list(const MetricFamily metric_family_table[], sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
|
||||
|
||||
int metric_build_send_string(MetricFamilyContext* context, const char *object, const char *value, char **field_pairs);
|
||||
int metric_build_send_unsigned(MetricFamilyContext* context, const char *object, uint64_t value, char **field_pairs);
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
#include "varlink-io.systemd.Metrics.h"
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
MetricFamilyType,
|
||||
SD_VARLINK_FIELD_COMMENT("A counter metric family type which is a monotonically increasing value"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(counter),
|
||||
SD_VARLINK_FIELD_COMMENT("A gauge metric family type which is a value that can go up and down"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(gauge),
|
||||
SD_VARLINK_FIELD_COMMENT("A string metric family type"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(string));
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(NoSuchMetric);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
List,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_FIELD_COMMENT("Metric name, e.g. io.systemd.Manager.units_by_type_total or io.systemd.Manager.unit_active_state"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0),
|
||||
/* metric value has various types depending on MetricFamilyType and actual data double/int/uint */
|
||||
SD_VARLINK_FIELD_COMMENT("Metric value"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(value, SD_VARLINK_ANY, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Metric object name can be unit name, process name, etc, e.g. dev-hvc0.device"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(object, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Metric fields are values to deferentiate between different metrics in the same metric family"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(fields, SD_VARLINK_OBJECT, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
Describe,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_FIELD_COMMENT("Metric family name"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Metric family description"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(description, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Metric family type"),
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(type, MetricFamilyType, 0));
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Metrics,
|
||||
"io.systemd.Metrics",
|
||||
SD_VARLINK_INTERFACE_COMMENT("Metrics APIs"),
|
||||
SD_VARLINK_SYMBOL_COMMENT("An enum representing various metric family types"),
|
||||
&vl_type_MetricFamilyType,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Method to get a list of metrics among which the collection of related metrics forms a metric family"),
|
||||
&vl_method_List,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Method to get the metric families"),
|
||||
&vl_method_Describe,
|
||||
SD_VARLINK_SYMBOL_COMMENT("No such metric found"),
|
||||
&vl_error_NoSuchMetric);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
extern const sd_varlink_interface vl_interface_io_systemd_Metrics;
|
||||
Reference in New Issue
Block a user