From 60ddfc241ec6c81e1fa51c3c993e63585bc1403f Mon Sep 17 00:00:00 2001 From: gmega Date: Wed, 17 Jun 2026 19:18:20 -0300 Subject: [PATCH] add nim-metrics -> logos openmetrics serializer --- Makefile | 6 +++- build.nims | 20 +++++++------ library/logosmetrics.nim | 38 +++++++++++++++++++++++++ tests/libstorage/logosmetrics.nim | 47 +++++++++++++++++++++++++++++++ tests/testLibstorage.nim | 2 ++ 5 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 library/logosmetrics.nim create mode 100644 tests/libstorage/logosmetrics.nim create mode 100644 tests/testLibstorage.nim diff --git a/Makefile b/Makefile index 22dc70fc..2220f87f 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ testIntegration: | build deps $(ENV_SCRIPT) nim testIntegration $(TEST_PARAMS) $(NIM_PARAMS) build.nims # Builds a C example that uses the libstorage C library and runs it -testLibstorage: | build deps +testLibstorageC: | build deps $(MAKE) $(if $(ncpu),-j$(ncpu),) libstorage cd tests/cbindings && \ if [ "$(detected_OS)" = "Windows" ]; then \ @@ -163,6 +163,10 @@ testLibstorage: | build deps LD_LIBRARY_PATH=../../build ./storage; \ fi +testLibstorage: | testLibstorageC + echo -e $(BUILD_MSG) "build/$@" && \ + $(ENV_SCRIPT) nim testLibstorage $(TEST_PARAMS) $(NIM_PARAMS) build.nims + # Builds and runs all tests testAll: | build deps echo -e $(BUILD_MSG) "build/$@" && \ diff --git a/build.nims b/build.nims index 081e3912..60d48301 100644 --- a/build.nims +++ b/build.nims @@ -69,18 +69,17 @@ task storage, "build logos storage binary": task mixTools, "build mix tools (mix_pool, mix_relay_dht)": let (desc, ec) = gorgeEx("git describe --always --dirty") let mixVersion = - if ec == 0 and desc.strip().len > 0: desc.strip() else: "unknown" + if ec == 0 and desc.strip().len > 0: + desc.strip() + else: + "unknown" let mixParams = - "-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE " & - "-d:mixVersion:" & mixVersion + "-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE " & "-d:mixVersion:" & + mixVersion buildBinary "mix_pool", - outName = "mix_pool", - srcDir = "tools/mix/", - params = mixParams + outName = "mix_pool", srcDir = "tools/mix/", params = mixParams buildBinary "mix_relay_dht", - outName = "mix_relay_dht", - srcDir = "tools/mix/", - params = mixParams + outName = "mix_relay_dht", srcDir = "tools/mix/", params = mixParams task testStorage, "Build & run Logos Storage tests": test "testStorage", outName = "testStorage" @@ -94,6 +93,9 @@ task testIntegration, "Run integration tests": # test "testIntegration", params = "-d:chronicles_sinks=textlines[notimestamps,stdout],textlines[dynamic] " & # "-d:chronicles_enabled_topics:integration:TRACE" +task testLibstorage, "Run libstorage Nim tests": + test "testLibstorage", outName = "testLibstorage" + task build, "build Logos Storage binary": storageTask() diff --git a/library/logosmetrics.nim b/library/logosmetrics.nim new file mode 100644 index 00000000..a5082abe --- /dev/null +++ b/library/logosmetrics.nim @@ -0,0 +1,38 @@ +import std/[json, locks, times, sets] + +import pkg/metrics + +proc toJson(collector: Collector, metrics: var seq[JsonNode] = @[]) = + # We know the closure won't outlive `metrics` so this is + # an acceptable hack. + let metricsPtr = addr metrics + + proc serializeMetric( + name: string, + value: float64, + labels: openArray[string] = [], + labelValues: openArray[string] = [], + timestamp: Time, + ) {.raises: [].} = + # The logos openmetrics format (https://github.com/logos-co/openmetrics-module) + # does not include the timestamp, so we don't include it either. + var labelMap = newJObject() + for i in 0 ..< labels.len: + labelMap[labels[i]] = %labelValues[i] + + metricsPtr[].add(%*{"name": name, "value": value, "labels": labelMap}) + + collector.collect(serializeMetric) + +# Serializes all collectors in a given registry to a Logos openmetrics-compatible +# format. Allows including only specific collectors by name. +proc toJson*(registry: Registry, includeOnly: openArray[string] = []): JsonNode = + var metrics = newSeq[JsonNode]() + withLock registry.lock: + for collector in registry.collectors: + if includeOnly.len > 0: + if collector.name notin includeOnly: + continue + collector.toJson(metrics) + + result = %*{"metrics": metrics} diff --git a/tests/libstorage/logosmetrics.nim b/tests/libstorage/logosmetrics.nim new file mode 100644 index 00000000..a3e85bc4 --- /dev/null +++ b/tests/libstorage/logosmetrics.nim @@ -0,0 +1,47 @@ +import std/json + +import pkg/unittest2 +import pkg/metrics + +import ../../library/logosmetrics + +declareCounter myCounter, "Parts Counter", ["part_type"] +declareGauge myGauge, "My gauge" +declareHistogram myHistogram, "My histogram", buckets = [0.0, 1.0, 2.0] + +suite "Metrics": + test "should serialize Nim metrics to Logos Metrics format": + myCounter.inc(labelValues = ["screws"]) + myCounter.inc(labelValues = ["washers"]) + + myGauge.set(42.0) + + myHistogram.observe(1) + myHistogram.observe(2) + myHistogram.observe(3) + myHistogram.observe(4) + myHistogram.observe(5) + + let metrics = defaultRegistry.toJson(@["myCounter", "myGauge", "myHistogram"]) + + # Remove "created" metrics as we can't pin those down. + var filteredMetrics = %*{"metrics": @[]} + for metric in metrics["metrics"]: + if metric["name"].getStr() != "myCounter_created" and + metric["name"].getStr() != "myHistogram_created": + filteredMetrics["metrics"].add(metric) + + check filteredMetrics == + %*{ + "metrics": [ + {"name": "myCounter_total", "value": 1.0, "labels": {"part_type": "screws"}}, + {"name": "myCounter_total", "value": 1.0, "labels": {"part_type": "washers"}}, + {"name": "myGauge", "value": 42.0, "labels": {}}, + {"name": "myHistogram_sum", "value": 15.0, "labels": {}}, + {"name": "myHistogram_count", "value": 5.0, "labels": {}}, + {"name": "myHistogram_bucket", "value": 0.0, "labels": {"le": "0.0"}}, + {"name": "myHistogram_bucket", "value": 1.0, "labels": {"le": "1.0"}}, + {"name": "myHistogram_bucket", "value": 2.0, "labels": {"le": "2.0"}}, + {"name": "myHistogram_bucket", "value": 5.0, "labels": {"le": "+Inf"}}, + ] + } diff --git a/tests/testLibstorage.nim b/tests/testLibstorage.nim new file mode 100644 index 00000000..a0669c5e --- /dev/null +++ b/tests/testLibstorage.nim @@ -0,0 +1,2 @@ +# Tests the Nim side of libstorage. +import ./libstorage/logosmetrics