From 0b554dc00732b72819a6a8806861713ee9db79d5 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sat, 2 Dec 2023 07:52:10 +0100 Subject: [PATCH] New utility: format OpenTelemetry. This new utility formats a metric family as an OpenTelemetry "ResourceMetric" and writes the serialized protocol buffer to the provided string buffer. --- Makefile.am | 27 +++- build.sh | 4 + .../format_open_telemetry.cc | 142 ++++++++++++++++++ .../format_open_telemetry.h | 35 +++++ 4 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 src/utils/format_open_telemetry/format_open_telemetry.cc create mode 100644 src/utils/format_open_telemetry/format_open_telemetry.h diff --git a/Makefile.am b/Makefile.am index 18fdc3e33..02f942fe5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -132,6 +132,7 @@ noinst_LTLIBRARIES = \ libcommon.la \ libformat_graphite.la \ libformat_influxdb.la \ + libformat_open_telemetry.la \ libheap.la \ libignorelist.la \ liblatency.la \ @@ -454,10 +455,6 @@ libplugin_mock_la_SOURCES = \ libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME libplugin_mock_la_LIBADD = libmetric.la liboconfig.la libcommon.la libignorelist.la $(COMMON_LIBS) -libformat_influxdb_la_SOURCES = \ - src/utils/format_influxdb/format_influxdb.c \ - src/utils/format_influxdb/format_influxdb.h - libformat_graphite_la_SOURCES = \ src/utils/format_graphite/format_graphite.c \ src/utils/format_graphite/format_graphite.h @@ -472,6 +469,28 @@ test_format_graphite_LDADD = \ libstrbuf.la \ -lm +libformat_influxdb_la_SOURCES = \ + src/utils/format_influxdb/format_influxdb.c \ + src/utils/format_influxdb/format_influxdb.h + +BUILT_SOURCES += \ + src/opentelemetry/proto/common/v1/common.pb.cc \ + src/opentelemetry/proto/common/v1/common.pb.h \ + src/opentelemetry/proto/metrics/v1/metrics.pb.cc \ + src/opentelemetry/proto/metrics/v1/metrics.pb.h \ + src/opentelemetry/proto/resource/v1/resource.pb.cc \ + src/opentelemetry/proto/resource/v1/resource.pb.h +libformat_open_telemetry_la_SOURCES = \ + src/utils/format_open_telemetry/format_open_telemetry.cc \ + src/utils/format_open_telemetry/format_open_telemetry.h \ + src/opentelemetry/proto/common/v1/common.pb.cc \ + src/opentelemetry/proto/common/v1/common.pb.h \ + src/opentelemetry/proto/metrics/v1/metrics.pb.cc \ + src/opentelemetry/proto/metrics/v1/metrics.pb.h \ + src/opentelemetry/proto/resource/v1/resource.pb.cc \ + src/opentelemetry/proto/resource/v1/resource.pb.h +libformat_open_telemetry_la_CPPFLAGS = $(AM_CPPFLAGS) + if BUILD_WITH_LIBYAJL noinst_LTLIBRARIES += libformat_json.la libformat_json_la_SOURCES = \ diff --git a/build.sh b/build.sh index c0ccce3a5..0fe9142da 100755 --- a/build.sh +++ b/build.sh @@ -55,6 +55,10 @@ build() && $libtoolize --copy --force \ && automake --add-missing --copy \ && autoconf + + for f in common/v1/common.proto metrics/v1/metrics.proto resource/v1/resource.proto; do + protoc -Iopentelemetry-proto --cpp_out src/ "opentelemetry-proto/opentelemetry/proto/${f}" + done } build_cygwin() diff --git a/src/utils/format_open_telemetry/format_open_telemetry.cc b/src/utils/format_open_telemetry/format_open_telemetry.cc new file mode 100644 index 000000000..771332a8c --- /dev/null +++ b/src/utils/format_open_telemetry/format_open_telemetry.cc @@ -0,0 +1,142 @@ +/** + * collectd - src/utils_format_open_telemetry.c + * Copyright (C) 2023 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +extern "C" { +#include "collectd.h" +#include "metric.h" + +#include "utils/format_open_telemetry/format_open_telemetry.h" +} + +#include "opentelemetry/proto/common/v1/common.pb.h" +#include "opentelemetry/proto/metrics/v1/metrics.pb.h" +#include "opentelemetry/proto/resource/v1/resource.pb.h" + +using opentelemetry::proto::common::v1::AnyValue; +using opentelemetry::proto::common::v1::InstrumentationScope; +using opentelemetry::proto::common::v1::KeyValue; +using opentelemetry::proto::metrics::v1::AGGREGATION_TEMPORALITY_CUMULATIVE; +using opentelemetry::proto::metrics::v1::Gauge; +using opentelemetry::proto::metrics::v1::Metric; +using opentelemetry::proto::metrics::v1::NumberDataPoint; +using opentelemetry::proto::metrics::v1::ResourceMetrics; +using opentelemetry::proto::metrics::v1::ScopeMetrics; +using opentelemetry::proto::metrics::v1::Sum; + +static void metric_to_number_data_point(NumberDataPoint *dp, metric_t const *m) { + for (size_t i = 0; i < m->label.num; i++) { + label_pair_t *l = m->label.ptr + i; + + KeyValue *kv = dp->add_attributes(); + kv->set_key(l->name); + AnyValue *v = kv->mutable_value(); + v->set_string_value(l->value); + } + + dp->set_time_unix_nano(CDTIME_T_TO_NS(m->time)); + // TODO(): also set "start time". We may need to use the cache to determine + // when we've seen a metric for the first time. + + switch (m->family->type) { + case METRIC_TYPE_COUNTER: + dp->set_as_int(m->value.derive); + case METRIC_TYPE_GAUGE: + dp->set_as_double(m->value.gauge); + case METRIC_TYPE_UNTYPED: + // TODO + assert(0); + } +} + +static void set_sum(Metric *m, metric_family_t const *fam) { + Sum *s = m->mutable_sum(); + for (size_t i = 0; i < fam->metric.num; i++) { + NumberDataPoint *dp = s->add_data_points(); + metric_to_number_data_point(dp, fam->metric.ptr + i); + } + + s->set_aggregation_temporality(AGGREGATION_TEMPORALITY_CUMULATIVE); + s->set_is_monotonic(true); +} + +static void set_gauge(Metric *m, metric_family_t const *fam) { + Gauge *g = m->mutable_gauge(); + for (size_t i = 0; i < fam->metric.num; i++) { + NumberDataPoint *dp = g->add_data_points(); + metric_to_number_data_point(dp, fam->metric.ptr + i); + } +} + +static void add_metric(ScopeMetrics *sm, metric_family_t const *fam) { + Metric *m = sm->add_metrics(); + + m->set_name(fam->name); + if (fam->help != NULL) { + m->set_description(fam->help); + } + + switch (fam->type) { + case METRIC_TYPE_COUNTER: + set_sum(m, fam); + return; + case METRIC_TYPE_GAUGE: + set_gauge(m, fam); + return; + case METRIC_TYPE_UNTYPED: + // TODO + assert(0); + } +} + +static void set_instrumentation_scope(ScopeMetrics *sm) { + InstrumentationScope *is = sm->mutable_scope(); + is->set_name(PACKAGE_NAME); + is->set_version(PACKAGE_VERSION); +} + +static void set_scope_metrics(ResourceMetrics *rm, metric_family_t const *fam) { + ScopeMetrics *sm = rm->add_scope_metrics(); + + set_instrumentation_scope(sm); + + add_metric(sm, fam); +} + +int format_open_telemetry(strbuf_t *sb, metric_family_t const *fam) { + ResourceMetrics rm; + + set_scope_metrics(&rm, fam); + + std::string serialization; + bool ok = rm.SerializeToString(&serialization); + if (!ok) { + return -1; + } + + strbuf_print(sb, serialization.c_str()); + return 0; +} + diff --git a/src/utils/format_open_telemetry/format_open_telemetry.h b/src/utils/format_open_telemetry/format_open_telemetry.h new file mode 100644 index 000000000..4120dc9b6 --- /dev/null +++ b/src/utils/format_open_telemetry/format_open_telemetry.h @@ -0,0 +1,35 @@ +/** + * collectd - src/utils_format_open_telemetry.h + * Copyright (C) 2023 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_FORMAT_OPEN_TELEMETRY_H +#define UTILS_FORMAT_OPEN_TELEMETRY_H 1 + +#include "collectd.h" +#include "metric.h" + +int format_open_telemetry(strbuf_t *sb, metric_family_t const *fam); // TODO: lacks return value + +#endif /* UTILS_FORMAT_OPEN_TELEMETRY_H */ -- 2.39.5