feat(inputs.system): collect unique user count logged in (#12147)
This commit is contained in:
parent
9155ae718d
commit
8221ece4ca
|
|
@ -28,6 +28,10 @@ The `n_users` field requires read access to `/var/run/utmp`, and may require the
|
||||||
`telegraf` user to be added to the `utmp` group on some systems. If this file
|
`telegraf` user to be added to the `utmp` group on some systems. If this file
|
||||||
does not exist `n_users` will be skipped.
|
does not exist `n_users` will be skipped.
|
||||||
|
|
||||||
|
The `n_unique_users` shows the count of unique usernames logged in. This way if
|
||||||
|
a user has multiple sessions open/started they would only get counted once. The
|
||||||
|
same requirements for `n_users` apply.
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
- system
|
- system
|
||||||
|
|
@ -36,6 +40,7 @@ does not exist `n_users` will be skipped.
|
||||||
- load15 (float)
|
- load15 (float)
|
||||||
- load5 (float)
|
- load5 (float)
|
||||||
- n_users (integer)
|
- n_users (integer)
|
||||||
|
- n_unique_users (integer)
|
||||||
- n_cpus (integer)
|
- n_cpus (integer)
|
||||||
- uptime (integer, seconds)
|
- uptime (integer, seconds)
|
||||||
- uptime_format (string, deprecated in 1.10, use `uptime` field)
|
- uptime_format (string, deprecated in 1.10, use `uptime` field)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ func (s *SystemStats) Gather(acc telegraf.Accumulator) error {
|
||||||
users, err := host.Users()
|
users, err := host.Users()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fields["n_users"] = len(users)
|
fields["n_users"] = len(users)
|
||||||
|
fields["n_unique_users"] = findUniqueUsers(users)
|
||||||
} else if os.IsNotExist(err) {
|
} else if os.IsNotExist(err) {
|
||||||
s.Log.Debugf("Reading users: %s", err.Error())
|
s.Log.Debugf("Reading users: %s", err.Error())
|
||||||
} else if os.IsPermission(err) {
|
} else if os.IsPermission(err) {
|
||||||
|
|
@ -74,6 +75,17 @@ func (s *SystemStats) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findUniqueUsers(userStats []host.UserStat) int {
|
||||||
|
uniqueUsers := make(map[string]bool)
|
||||||
|
for _, userstat := range userStats {
|
||||||
|
if _, ok := uniqueUsers[userstat.User]; !ok {
|
||||||
|
uniqueUsers[userstat.User] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(uniqueUsers)
|
||||||
|
}
|
||||||
|
|
||||||
func formatUptime(uptime uint64) string {
|
func formatUptime(uptime uint64) string {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
w := bufio.NewWriter(buf)
|
w := bufio.NewWriter(buf)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v3/host"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUniqueUsers(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expected int
|
||||||
|
data []host.UserStat
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single entry",
|
||||||
|
expected: 1,
|
||||||
|
data: []host.UserStat{
|
||||||
|
{User: "root"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emptry entry",
|
||||||
|
expected: 0,
|
||||||
|
data: []host.UserStat{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all duplicates",
|
||||||
|
expected: 1,
|
||||||
|
data: []host.UserStat{
|
||||||
|
{User: "root"},
|
||||||
|
{User: "root"},
|
||||||
|
{User: "root"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all unique",
|
||||||
|
expected: 3,
|
||||||
|
data: []host.UserStat{
|
||||||
|
{User: "root"},
|
||||||
|
{User: "ubuntu"},
|
||||||
|
{User: "ec2-user"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mix of dups",
|
||||||
|
expected: 3,
|
||||||
|
data: []host.UserStat{
|
||||||
|
{User: "root"},
|
||||||
|
{User: "ubuntu"},
|
||||||
|
{User: "ubuntu"},
|
||||||
|
{User: "ubuntu"},
|
||||||
|
{User: "ec2-user"},
|
||||||
|
{User: "ec2-user"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actual := findUniqueUsers(tt.data)
|
||||||
|
require.Equal(t, tt.expected, actual, tt.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue