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
|
// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
// SPDX-License-Identifier: Apache-2.0
package microprom_test
import (
"context"
"fmt"
"io"
"maps"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.xyrillian.de/gg/assert"
"go.xyrillian.de/gg/microprom"
)
func TestHandlerFunctionallyIdenticalToPromhttp(t *testing.T) {
// build a microprom.Handler rendering two metric families
h1 := microprom.Handler{
Families: map[microprom.MetricFamilyName]microprom.MetricFamilyInfo{
"events": {
Type: microprom.MetricTypeCounter,
Help: "Counts events that happened.",
},
"memory_usage_bytes": {
Type: microprom.MetricTypeGauge,
Help: "How much memory is currently used.",
},
},
SortOutput: true,
Collect: func(ctx context.Context, ms *microprom.MetricSet) error {
labelNames := microprom.NewLabelNames("shard", "type")
for idx := range 5 {
labels := ms.FormatLabels(labelNames, fmt.Sprintf("node%d", idx), "update")
ms.Add("events", labels, float64(10*idx))
}
ms.Add("memory_usage_bytes", "", 42<<20)
return nil
},
}
// build a promhttp.Handler rendering the same metric families
eventsCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "events_total",
Help: "Counts events that happened.",
}, []string{"type", "shard"})
for idx := range 5 {
eventsCounter.With(prometheus.Labels{
"type": "update",
"shard": fmt.Sprintf("node%d", idx),
}).Add(float64(10 * idx))
}
memoryUsageBytesGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "How much memory is currently used.",
})
memoryUsageBytesGauge.Set(42 << 20)
r := prometheus.NewRegistry()
r.MustRegister(eventsCounter)
r.MustRegister(memoryUsageBytesGauge)
h2 := promhttp.HandlerFor(r, promhttp.HandlerOpts{EnableOpenMetrics: true})
// test identical behavior for Prometheus Text Format
body1, headers1 := getMetrics(t, h1, nil)
body2, headers2 := getMetrics(t, h2, nil)
assert.Equal(t, strings.Split(body1, "\n"), strings.Split(body2, "\n"))
assert.Equal(t, headers1, headers2)
// test identical behavior for OpenMetrics 1.0 text format
body1, headers1 = getMetrics(t, h1, http.Header{"Accept": {"application/openmetrics-text; version=1.0.0"}})
body2, headers2 = getMetrics(t, h2, http.Header{"Accept": {"application/openmetrics-text; version=1.0.0"}})
assert.Equal(t, strings.Split(body1, "\n"), strings.Split(body2, "\n"))
assert.Equal(t, headers1, headers2)
}
func getMetrics(t *testing.T, h http.Handler, requestHeaders http.Header) (responseBody string, responseHeaders http.Header) {
t.Helper()
r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", nil)
maps.Copy(r.Header, requestHeaders)
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
resp := w.Result()
buf, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err.Error())
}
return string(buf), resp.Header
}
|