diff --git a/plugins/inputs/systemd_units/README.md b/plugins/inputs/systemd_units/README.md index 838786d11c8d1..5364f43ef574c 100644 --- a/plugins/inputs/systemd_units/README.md +++ b/plugins/inputs/systemd_units/README.md @@ -31,6 +31,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## automount, swap, timer, path, slice and scope # unittype = "service" + ## Collect system or user scoped units + ## ex: scope = "user" + # scope = "system" + ## Collect also units not loaded by systemd, i.e. disabled or static units ## Enabling this feature might introduce significant load when used with ## unspecific patterns (such as '*') as systemd will need to load all @@ -69,6 +73,7 @@ These metrics are available in both modes: - load (string, load state) - active (string, active state) - sub (string, sub state) + - user (string, username only for user scope) - fields: - load_code (int, see below) - active_code (int, see below) @@ -198,15 +203,15 @@ were removed, tables are hex aligned to keep some space for future values ### Output in non-detailed mode ```text -systemd_units,host=host1.example.com,name=dbus.service,load=loaded,active=active,sub=running load_code=0i,active_code=0i,sub_code=0i 1533730725000000000 -systemd_units,host=host1.example.com,name=networking.service,load=loaded,active=failed,sub=failed load_code=0i,active_code=3i,sub_code=12i 1533730725000000000 -systemd_units,host=host1.example.com,name=ssh.service,load=loaded,active=active,sub=running load_code=0i,active_code=0i,sub_code=0i 1533730725000000000 +systemd_units,host=host1.example.com,name=dbus.service,load=loaded,active=active,sub=running,user=telegraf load_code=0i,active_code=0i,sub_code=0i 1533730725000000000 +systemd_units,host=host1.example.com,name=networking.service,load=loaded,active=failed,sub=failed,user=telegraf load_code=0i,active_code=3i,sub_code=12i 1533730725000000000 +systemd_units,host=host1.example.com,name=ssh.service,load=loaded,active=active,sub=running,user=telegraf load_code=0i,active_code=0i,sub_code=0i 1533730725000000000 ``` ### Output in detailed mode ```text -systemd_units,active=active,host=host1.example.com,load=loaded,name=dbus.service,sub=running,preset=disabled,state=static active_code=0i,load_code=0i,mem_avail=6470856704i,mem_current=2691072i,mem_peak=3895296i,pid=481i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=794624i,swap_peak=884736i 1533730725000000000 -systemd_units,active=inactive,host=host1.example.com,load=not-found,name=networking.service,sub=dead active_code=2i,load_code=2i,pid=0i,restarts=0i,status_errno=0i,sub_code=1i 1533730725000000000 -systemd_units,active=active,host=host1.example.com,load=loaded,name=pcscd.service,sub=running,preset=disabled,state=indirect active_code=0i,load_code=0i,mem_avail=6370541568i,mem_current=512000i,mem_peak=4399104i,pid=1673i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=3149824i,swap_peak=3149824i 1533730725000000000 +systemd_units,active=active,host=host1.example.com,load=loaded,name=dbus.service,sub=running,preset=disabled,state=static,user=telegraf active_code=0i,load_code=0i,mem_avail=6470856704i,mem_current=2691072i,mem_peak=3895296i,pid=481i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=794624i,swap_peak=884736i 1533730725000000000 +systemd_units,active=inactive,host=host1.example.com,load=not-found,name=networking.service,sub=dead,user=telegraf active_code=2i,load_code=2i,pid=0i,restarts=0i,status_errno=0i,sub_code=1i 1533730725000000000 +systemd_units,active=active,host=host1.example.com,load=loaded,name=pcscd.service,sub=running,preset=disabled,state=indirect,user=telegraf active_code=0i,load_code=0i,mem_avail=6370541568i,mem_current=512000i,mem_peak=4399104i,pid=1673i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=3149824i,swap_peak=3149824i 1533730725000000000 ``` diff --git a/plugins/inputs/systemd_units/sample.conf b/plugins/inputs/systemd_units/sample.conf index 3c28db6c18d4b..73193a8084435 100644 --- a/plugins/inputs/systemd_units/sample.conf +++ b/plugins/inputs/systemd_units/sample.conf @@ -12,6 +12,10 @@ ## automount, swap, timer, path, slice and scope # unittype = "service" + ## Collect system or user scoped units + ## ex: scope = "user" + # scope = "system" + ## Collect also units not loaded by systemd, i.e. disabled or static units ## Enabling this feature might introduce significant load when used with ## unspecific patterns (such as '*') as systemd will need to load all diff --git a/plugins/inputs/systemd_units/systemd_units.go b/plugins/inputs/systemd_units/systemd_units.go index 2e08639b528c6..870bfc1aff3a1 100644 --- a/plugins/inputs/systemd_units/systemd_units.go +++ b/plugins/inputs/systemd_units/systemd_units.go @@ -17,6 +17,7 @@ var sampleConfig string type SystemdUnits struct { Pattern string `toml:"pattern"` UnitType string `toml:"unittype"` + Scope string `toml:"scope"` Details bool `toml:"details"` CollectDisabled bool `toml:"collect_disabled_units"` Timeout config.Duration `toml:"timeout"` diff --git a/plugins/inputs/systemd_units/systemd_units_linux.go b/plugins/inputs/systemd_units/systemd_units_linux.go index f43aa1bc9151f..443095ee203d8 100644 --- a/plugins/inputs/systemd_units/systemd_units_linux.go +++ b/plugins/inputs/systemd_units/systemd_units_linux.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "math" + "os/user" "path" "strings" "time" @@ -131,6 +132,8 @@ type archParams struct { pattern []string filter filter.Filter unitTypeDBus string + scope string + user string } func (s *SystemdUnits) Init() error { @@ -158,15 +161,38 @@ func (s *SystemdUnits) Init() error { } s.filter = f + switch s.Scope { + case "", "system": + s.scope = "system" + case "user": + u, err := user.Current() + if err != nil { + return fmt.Errorf("unable to determine user: %w", err) + } + + s.scope = "user" + s.user = u.Username + default: + return fmt.Errorf("invalid 'scope' %q", s.Scope) + } + return nil } func (s *SystemdUnits) Start(telegraf.Accumulator) error { ctx := context.Background() - client, err := dbus.NewSystemConnectionContext(ctx) + + var client *dbus.Conn + var err error + if s.scope == "user" { + client, err = dbus.NewUserConnectionContext(ctx) + } else { + client, err = dbus.NewSystemConnectionContext(ctx) + } if err != nil { return err } + s.client = client return nil @@ -321,6 +347,9 @@ func (s *SystemdUnits) Gather(acc telegraf.Accumulator) error { "active": state.ActiveState, "sub": state.SubState, } + if s.scope == "user" { + tags["user"] = s.user + } fields := map[string]interface{}{ "load_code": load, diff --git a/plugins/inputs/systemd_units/systemd_units_test.go b/plugins/inputs/systemd_units/systemd_units_test.go index f762cdfe3d551..4397ad88e3b28 100644 --- a/plugins/inputs/systemd_units/systemd_units_test.go +++ b/plugins/inputs/systemd_units/systemd_units_test.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "math" + "os/user" "strings" "testing" "time" @@ -38,6 +39,50 @@ func TestDefaultPattern(t *testing.T) { require.Equal(t, "*", plugin.Pattern) } +func TestDefaultScope(t *testing.T) { + u, err := user.Current() + if err != nil { + return + } + + tests := []struct { + name string + scope string + expectedScope string + expectedUser string + }{ + { + name: "default scope", + scope: "", + expectedScope: "system", + expectedUser: "", + }, + { + name: "system scope", + scope: "system", + expectedScope: "system", + expectedUser: "", + }, + { + name: "user scope", + scope: "user", + expectedScope: "user", + expectedUser: u.Username, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + plugin := &SystemdUnits{ + Scope: tt.scope, + } + require.NoError(t, plugin.Init()) + require.Equal(t, tt.expectedScope, plugin.scope) + require.Equal(t, tt.expectedUser, plugin.user) + }) + } +} + func TestListFiles(t *testing.T) { tests := []struct { name string