diff --git a/collector/logind_linux.go b/collector/logind_linux.go index bd2c04848d..9d80ce1ee4 100644 --- a/collector/logind_linux.go +++ b/collector/logind_linux.go @@ -22,6 +22,7 @@ import ( "slices" "strconv" + "github.com/alecthomas/kingpin/v2" "github.com/godbus/dbus/v5" "github.com/prometheus/client_golang/prometheus" ) @@ -38,6 +39,8 @@ var ( attrRemoteValues = []string{"true", "false"} attrTypeValues = []string{"other", "unspecified", "tty", "x11", "wayland", "mir", "web"} attrClassValues = []string{"other", "user", "greeter", "lock-screen", "background"} + logindClassInclude = kingpin.Flag("collector.logind.class-include", "Regexp of logind session classes to include (mutually exclusive with class-exclude).").String() + logindClassExclude = kingpin.Flag("collector.logind.class-exclude", "Regexp of logind session classes to exclude (mutually exclusive with class-include).").String() sessionsDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, logindSubsystem, "sessions"), @@ -46,6 +49,7 @@ var ( ) type logindCollector struct { + classFilter deviceFilter logger *slog.Logger } @@ -87,7 +91,10 @@ func init() { // NewLogindCollector returns a new Collector exposing logind statistics. func NewLogindCollector(logger *slog.Logger) (Collector, error) { - return &logindCollector{logger}, nil + return &logindCollector{ + logger: logger, + classFilter: newDeviceFilter(*logindClassExclude, *logindClassInclude), + }, nil } func (lc *logindCollector) Update(ch chan<- prometheus.Metric) error { @@ -97,10 +104,10 @@ func (lc *logindCollector) Update(ch chan<- prometheus.Metric) error { } defer c.conn.Close() - return collectMetrics(ch, c) + return collectMetrics(ch, c, lc) } -func collectMetrics(ch chan<- prometheus.Metric, c logindInterface) error { +func collectMetrics(ch chan<- prometheus.Metric, c logindInterface, lc *logindCollector) error { seats, err := c.listSeats() if err != nil { return fmt.Errorf("unable to get seats: %w", err) @@ -123,6 +130,9 @@ func collectMetrics(ch chan<- prometheus.Metric, c logindInterface) error { for _, remote := range attrRemoteValues { for _, sessionType := range attrTypeValues { for _, class := range attrClassValues { + if lc.classFilter.ignored(class) { + continue + } for _, seat := range seats { count := sessions[logindSession{seat, remote, sessionType, class}] diff --git a/collector/logind_linux_test.go b/collector/logind_linux_test.go index b2592a74e0..ac863ef987 100644 --- a/collector/logind_linux_test.go +++ b/collector/logind_linux_test.go @@ -102,3 +102,40 @@ func TestLogindCollectorCollectMetrics(t *testing.T) { t.Errorf("collectMetrics did not generate the expected number of metrics: got %d, expected %d.", count, expected) } } + +func TestLogindCollectorCollectMetricsWithFilter(t *testing.T) { + ch := make(chan prometheus.Metric) + // Create a dummy collector with a classFilter that excludes "greeter" + collector := &logindCollector{ + classFilter: deviceFilter{ + ignorePattern: regexp.MustCompile("greeter"), + }, + } + + // Wrap collectMetrics call in a goroutine feeding the metrics channel + go func() { + err := collectMetrics(ch, &testLogindInterface{}) + if err != nil { + t.Errorf("collectMetrics returned error: %v", err) + } + close(ch) + }() + + seenClasses := make(map[string]bool) + count := 0 + for metric := range ch { + count++ + desc := metric.Desc().String() + // Extract class label from the metric Desc string (approximate) + // The label list appears as '{seat=...,remote=...,type=...,class=...}' + // We'll parse class label from the Desc string for simplicity here: + // For a formal approach, you can use a prometheus.Metric introspection library. + + if strings.Contains(desc, "class=\"greeter\"") { + t.Errorf("metric with excluded class 'greeter' was emitted") + } + } + if count == 0 { + t.Errorf("no metrics were emitted") + } +}