Skip to content

Commit 473edfe

Browse files
committed
fix: correct node_exporter_build_info metric to show actual node_exporter version
The prometheus.exporter.unix component was registering a node_exporter_build_info metric that incorrectly showed Alloy's version information instead of the actual node_exporter version (v1.9.1). This fix: - Adds a custom buildInfoCollector that exposes the correct node_exporter version - Uses the version from the github.com/prometheus/node_exporter dependency (v1.9.1) - Includes the revision from the grafana/node_exporter fork being used - Adds a test to verify the metric contains the correct version Fixes #4330
1 parent 9052910 commit 473edfe

File tree

2 files changed

+65
-8
lines changed

2 files changed

+65
-8
lines changed

internal/static/integrations/node_exporter/node_exporter.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"fmt"
88
"log/slog"
99
"net/http"
10+
"runtime"
1011
"sort"
1112

1213
"github.com/go-kit/log"
1314
"github.com/go-kit/log/level"
14-
"github.com/grafana/alloy/internal/build"
1515
"github.com/grafana/alloy/internal/runtime/logging"
1616
"github.com/grafana/alloy/internal/static/integrations/config"
1717
"github.com/prometheus/client_golang/prometheus"
@@ -29,6 +29,45 @@ type Integration struct {
2929
exporterMetricsRegistry *prometheus.Registry
3030
}
3131

32+
// nodeExporterVersion holds the version information for the node_exporter library.
33+
// This should match the version of github.com/prometheus/node_exporter being used.
34+
const (
35+
nodeExporterVersion = "v1.9.1"
36+
nodeExporterRevision = "318b01780c89" // From grafana/node_exporter fork
37+
nodeExporterBranch = "HEAD"
38+
)
39+
40+
// buildInfoCollector is a custom collector that exposes node_exporter version information.
41+
type buildInfoCollector struct {
42+
desc *prometheus.Desc
43+
}
44+
45+
func newBuildInfoCollector() *buildInfoCollector {
46+
return &buildInfoCollector{
47+
desc: prometheus.NewDesc(
48+
"node_exporter_build_info",
49+
"A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build.",
50+
nil,
51+
prometheus.Labels{
52+
"version": nodeExporterVersion,
53+
"revision": nodeExporterRevision,
54+
"branch": nodeExporterBranch,
55+
"goversion": runtime.Version(),
56+
"goos": runtime.GOOS,
57+
"goarch": runtime.GOARCH,
58+
},
59+
),
60+
}
61+
}
62+
63+
func (c *buildInfoCollector) Describe(ch chan<- *prometheus.Desc) {
64+
ch <- c.desc
65+
}
66+
67+
func (c *buildInfoCollector) Collect(ch chan<- prometheus.Metric) {
68+
ch <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, 1)
69+
}
70+
3271
// New creates a new node_exporter integration.
3372
func New(log log.Logger, c *Config) (*Integration, error) {
3473
cfg := c.mapConfigToNodeConfig()
@@ -62,6 +101,13 @@ func (i *Integration) MetricsHandler() (http.Handler, error) {
62101
if err := r.Register(i.nc); err != nil {
63102
return nil, fmt.Errorf("couldn't register node_exporter node collector: %w", err)
64103
}
104+
105+
// Register node_exporter_build_info metrics with correct node_exporter version,
106+
// generally useful for dashboards that depend on them for discovering targets.
107+
if err := r.Register(newBuildInfoCollector()); err != nil {
108+
return nil, fmt.Errorf("couldn't register node_exporter build info: %w", err)
109+
}
110+
65111
handler := promhttp.HandlerFor(
66112
prometheus.Gatherers{i.exporterMetricsRegistry, r},
67113
promhttp.HandlerOpts{
@@ -71,12 +117,6 @@ func (i *Integration) MetricsHandler() (http.Handler, error) {
71117
},
72118
)
73119

74-
// Register node_exporter_build_info metrics, generally useful for
75-
// dashboards that depend on them for discovering targets.
76-
if err := r.Register(build.NewCollector(i.c.Name())); err != nil {
77-
return nil, fmt.Errorf("couldn't register %s: %w", i.c.Name(), err)
78-
}
79-
80120
if i.c.IncludeExporterMetrics {
81121
// Note that we have to use reg here to use the same promhttp metrics for
82122
// all expositions.

internal/static/integrations/node_exporter/node_exporter_test.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/go-kit/log"
1212
"github.com/gorilla/mux"
13+
"github.com/prometheus/prometheus/model/labels"
1314
"github.com/prometheus/prometheus/model/textparse"
1415
"github.com/stretchr/testify/require"
1516
)
@@ -56,11 +57,27 @@ func TestNodeExporter(t *testing.T) {
5657
require.NoError(t, err)
5758

5859
p := textparse.NewPromParser(body, nil, false)
60+
foundBuildInfo := false
5961
for {
60-
_, err := p.Next()
62+
et, err := p.Next()
6163
if err == io.EOF {
6264
break
6365
}
6466
require.NoError(t, err)
67+
68+
// Check for node_exporter_build_info metric
69+
if et == textparse.EntrySeries {
70+
series, _, _ := p.Series()
71+
var lbls labels.Labels
72+
p.Labels(&lbls)
73+
metricName := lbls.Get("__name__")
74+
if metricName == "node_exporter_build_info" {
75+
foundBuildInfo = true
76+
// Verify the version label contains the correct node_exporter version
77+
version := lbls.Get("version")
78+
require.Equal(t, nodeExporterVersion, version, "node_exporter_build_info should have correct version, series: %s", string(series))
79+
}
80+
}
6581
}
82+
require.True(t, foundBuildInfo, "node_exporter_build_info metric should be present")
6683
}

0 commit comments

Comments
 (0)