Skip to content

Commit 49d395d

Browse files
committed
wip: Make node-exporter embeddable in OTel Collectors
Signed-off-by: Arthur Silva Sens <[email protected]>
1 parent 87ec73c commit 49d395d

File tree

6 files changed

+243
-31
lines changed

6 files changed

+243
-31
lines changed

collector/collector.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/alecthomas/kingpin/v2"
2525
"github.com/prometheus/client_golang/prometheus"
26+
"github.com/prometheus/procfs"
2627
)
2728

2829
// Namespace defines the common namespace to be used by all metrics.
@@ -53,7 +54,8 @@ var (
5354
initiatedCollectorsMtx = sync.Mutex{}
5455
initiatedCollectors = make(map[string]Collector)
5556
collectorState = make(map[string]*bool)
56-
forcedCollectors = map[string]bool{} // collectors which have been explicitly enabled or disabled
57+
forcedCollectors = map[string]bool{} // collectors which have been explicitly enabled or disabled
58+
collectorDefaults = make(map[string]bool) // tracks default enabled/disabled state for OTEL context
5759
)
5860

5961
func registerCollector(collector string, isDefaultEnabled bool, factory func(logger *slog.Logger) (Collector, error)) {
@@ -64,6 +66,9 @@ func registerCollector(collector string, isDefaultEnabled bool, factory func(log
6466
helpDefaultState = "disabled"
6567
}
6668

69+
// Store the default state for OTEL context initialization
70+
collectorDefaults[collector] = isDefaultEnabled
71+
6772
flagName := fmt.Sprintf("collector.%s", collector)
6873
flagHelp := fmt.Sprintf("Enable the %s collector (default: %s).", collector, helpDefaultState)
6974
defaultValue := fmt.Sprintf("%v", isDefaultEnabled)
@@ -243,3 +248,43 @@ func pushMetric(ch chan<- prometheus.Metric, fieldDesc *prometheus.Desc, name st
243248

244249
ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, fVal, labelValues...)
245250
}
251+
252+
// InitializeCollectorStateForOTEL initializes the collector state for OTEL context
253+
// without relying on kingpin flag parsing. This should be called before creating
254+
// any NodeCollector instances in the OTEL context.
255+
func InitializeCollectorStateForOTEL() {
256+
// Initialize collector enable/disable state from stored defaults
257+
for collectorName, defaultEnabled := range collectorDefaults {
258+
enabled := defaultEnabled
259+
collectorState[collectorName] = &enabled
260+
}
261+
262+
// Initialize collector-specific configuration flags with their defaults
263+
initializeCollectorFlags()
264+
}
265+
266+
// initializeCollectorFlags sets default values for collector-specific flags
267+
// that are normally initialized by kingpin parsing
268+
func initializeCollectorFlags() {
269+
// Initialize path flags with their defaults (from paths.go)
270+
if procPath == nil {
271+
defaultProcPath := procfs.DefaultMountPoint
272+
procPath = &defaultProcPath
273+
}
274+
if sysPath == nil {
275+
defaultSysPath := "/sys"
276+
sysPath = &defaultSysPath
277+
}
278+
if rootfsPath == nil {
279+
defaultRootfsPath := "/"
280+
rootfsPath = &defaultRootfsPath
281+
}
282+
if udevDataPath == nil {
283+
defaultUdevDataPath := "/run/udev/data"
284+
udevDataPath = &defaultUdevDataPath
285+
}
286+
287+
// Initialize other collector-specific flags with empty/default values
288+
// These will be nil pointers until kingpin.Parse() is called, but collectors
289+
// should handle nil gracefully or we can initialize them as needed
290+
}

otelcollector/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ package otelcollector
1616
import "fmt"
1717

1818
type Config struct {
19-
DisableDefaults bool
20-
EnableCollectors []string
21-
ExcludeCollectors []string
19+
DisableDefaults bool `mapstructure:"disable_defaults"`
20+
EnableCollectors []string `mapstructure:"enable_collectors"`
21+
ExcludeCollectors []string `mapstructure:"exclude_collectors"`
2222
}
2323

2424
func (c Config) Validate() error {

otelcollector/exporter.go

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ package otelcollector
1616
import (
1717
"context"
1818
"errors"
19+
"fmt"
20+
"log/slog"
1921

2022
"github.com/prometheus/client_golang/prometheus"
2123
versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version"
2224
"github.com/prometheus/exporter-toolkit/otlpreceiver"
25+
"github.com/prometheus/node_exporter/collector"
2326
)
2427

2528
type NodeExporter struct {
@@ -35,26 +38,90 @@ func NewNodeExporter(config *Config) *NodeExporter {
3538
}
3639

3740
func (ne *NodeExporter) Initialize(ctx context.Context, cfg otlpreceiver.Config) (*prometheus.Registry, error) {
38-
var exporterCfg *Config
39-
4041
if cfg != nil {
4142
var ok bool
42-
exporterCfg, ok = cfg.(*Config)
43+
ne.config, ok = cfg.(*Config)
4344
if !ok {
4445
return nil, errors.New("error reading configuration")
4546
}
4647
} else {
4748
// Use default configuration when none is provided
48-
exporterCfg = &Config{
49+
ne.config = &Config{
4950
DisableDefaults: false,
5051
EnableCollectors: []string{},
5152
ExcludeCollectors: []string{},
5253
}
5354
}
5455

56+
// Create logger for collectors
57+
logger := slog.Default().With("component", "node_exporter")
58+
59+
collector.InitializeCollectorStateForOTEL()
60+
61+
// Apply disable defaults configuration
62+
if ne.config.DisableDefaults {
63+
collector.DisableDefaultCollectors()
64+
}
65+
66+
// Determine which collectors to create based on configuration
67+
var filters []string
68+
69+
// Validate that both EnableCollectors and ExcludeCollectors are not used together
70+
if len(ne.config.EnableCollectors) > 0 && len(ne.config.ExcludeCollectors) > 0 {
71+
return nil, fmt.Errorf("enable_collectors and exclude_collectors cannot be used together")
72+
}
73+
74+
if len(ne.config.EnableCollectors) > 0 {
75+
// If specific collectors are enabled, use only those
76+
filters = ne.config.EnableCollectors
77+
} else if len(ne.config.ExcludeCollectors) > 0 {
78+
// If excludes are specified, we need to create a list of all enabled collectors
79+
// minus the excluded ones, similar to how the main node_exporter handles it
80+
filters = []string{}
81+
82+
// First, create a temporary NodeCollector to get the list of enabled collectors
83+
tempNC, err := collector.NewNodeCollector(logger)
84+
if err != nil {
85+
return nil, fmt.Errorf("failed to get available collectors: %w", err)
86+
}
87+
88+
// Get all enabled collector names
89+
for collectorName := range tempNC.Collectors {
90+
// Check if this collector is not in the exclude list
91+
excluded := false
92+
for _, excludeName := range ne.config.ExcludeCollectors {
93+
if collectorName == excludeName {
94+
excluded = true
95+
break
96+
}
97+
}
98+
if !excluded {
99+
filters = append(filters, collectorName)
100+
}
101+
}
102+
}
103+
// If neither EnableCollectors nor ExcludeCollectors are specified,
104+
// filters remains empty and NewNodeCollector will use all enabled collectors
105+
106+
// Create the node collector with all enabled collectors
107+
nc, err := collector.NewNodeCollector(logger, filters...)
108+
if err != nil {
109+
return nil, fmt.Errorf("failed to create node collector: %w", err)
110+
}
111+
112+
// Register version collector
55113
ne.registry.MustRegister(versioncollector.NewCollector("node_exporter"))
56114

57-
ne.config = exporterCfg
115+
// Register the node collector
116+
if err := ne.registry.Register(nc); err != nil {
117+
return nil, fmt.Errorf("failed to register node collector: %w", err)
118+
}
119+
120+
logger.Info("Node exporter initialized successfully",
121+
"disable_defaults", ne.config.DisableDefaults,
122+
"enable_collectors", ne.config.EnableCollectors,
123+
"exclude_collectors", ne.config.ExcludeCollectors)
124+
58125
return ne.registry, nil
59126
}
60127

otelcollector/go.mod

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,52 @@ go 1.25.0
44

55
require (
66
github.com/prometheus/client_golang v1.23.2
7-
github.com/prometheus/exporter-toolkit v0.0.0-00010101000000-000000000000
7+
github.com/prometheus/exporter-toolkit v0.14.1
8+
github.com/prometheus/node_exporter v1.10.2
89
go.opentelemetry.io/collector/component v1.44.0
910
go.opentelemetry.io/collector/receiver v1.44.0
1011
)
1112

1213
require (
14+
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
15+
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
16+
github.com/beevik/ntp v1.5.0 // indirect
1317
github.com/beorn7/perks v1.0.1 // indirect
1418
github.com/cespare/xxhash/v2 v2.3.0 // indirect
19+
github.com/coreos/go-systemd/v22 v22.6.0 // indirect
20+
github.com/dennwc/btrfs v0.0.0-20241002142654-12ae127e0bf6 // indirect
21+
github.com/dennwc/ioctl v1.0.0 // indirect
22+
github.com/ema/qdisc v1.0.0 // indirect
1523
github.com/go-logr/logr v1.4.3 // indirect
1624
github.com/go-logr/stdr v1.2.2 // indirect
25+
github.com/godbus/dbus/v5 v5.1.0 // indirect
1726
github.com/gogo/protobuf v1.3.2 // indirect
27+
github.com/google/go-cmp v0.7.0 // indirect
28+
github.com/hashicorp/go-envparse v0.1.0 // indirect
1829
github.com/hashicorp/go-version v1.7.0 // indirect
30+
github.com/hodgesds/perf-utils v0.7.0 // indirect
31+
github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973 // indirect
32+
github.com/jsimonetti/rtnetlink/v2 v2.0.5 // indirect
1933
github.com/json-iterator/go v1.1.12 // indirect
34+
github.com/lufia/iostat v1.2.1 // indirect
35+
github.com/mattn/go-xmlrpc v0.0.3 // indirect
36+
github.com/mdlayher/ethtool v0.5.0 // indirect
37+
github.com/mdlayher/genetlink v1.3.2 // indirect
38+
github.com/mdlayher/netlink v1.8.0 // indirect
39+
github.com/mdlayher/socket v0.5.1 // indirect
40+
github.com/mdlayher/wifi v0.6.0 // indirect
41+
github.com/mitchellh/mapstructure v1.5.0 // indirect
2042
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
2143
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
2244
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
45+
github.com/opencontainers/selinux v1.12.0 // indirect
46+
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
47+
github.com/prometheus-community/go-runit v0.1.0 // indirect
2348
github.com/prometheus/client_model v0.6.2 // indirect
24-
github.com/prometheus/common v0.66.1 // indirect
25-
github.com/prometheus/procfs v0.16.1 // indirect
49+
github.com/prometheus/common v0.67.1 // indirect
50+
github.com/prometheus/procfs v0.19.0 // indirect
51+
github.com/safchain/ethtool v0.6.2 // indirect
52+
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
2653
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
2754
go.opentelemetry.io/collector/consumer v1.44.0 // indirect
2855
go.opentelemetry.io/collector/featuregate v1.44.0 // indirect
@@ -37,12 +64,18 @@ require (
3764
go.uber.org/multierr v1.11.0 // indirect
3865
go.uber.org/zap v1.27.0 // indirect
3966
go.yaml.in/yaml/v2 v2.4.3 // indirect
67+
golang.org/x/crypto v0.43.0 // indirect
68+
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
4069
golang.org/x/net v0.45.0 // indirect
70+
golang.org/x/sync v0.17.0 // indirect
4171
golang.org/x/sys v0.37.0 // indirect
4272
golang.org/x/text v0.30.0 // indirect
4373
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
4474
google.golang.org/grpc v1.76.0 // indirect
4575
google.golang.org/protobuf v1.36.10 // indirect
76+
howett.net/plist v1.0.1 // indirect
4677
)
4778

4879
replace github.com/prometheus/exporter-toolkit => ../../exporter-toolkit
80+
81+
replace github.com/prometheus/node_exporter => ../

0 commit comments

Comments
 (0)