Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions collector/logind_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"slices"
"strconv"

"github.com/alecthomas/kingpin/v2"
"github.com/godbus/dbus/v5"
"github.com/prometheus/client_golang/prometheus"
)
Expand All @@ -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"),
Expand All @@ -46,6 +49,7 @@ var (
)

type logindCollector struct {
classFilter deviceFilter
logger *slog.Logger
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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}]

Expand Down
37 changes: 37 additions & 0 deletions collector/logind_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}