feat(inputs.systemd_units): Add active_enter_timestamp_us field (#16287)
This commit is contained in:
parent
2ccc79ce27
commit
e3ce01abf0
2
go.mod
2
go.mod
|
|
@ -89,7 +89,6 @@ require (
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/go-stomp/stomp v2.1.4+incompatible
|
github.com/go-stomp/stomp v2.1.4+incompatible
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
|
||||||
github.com/gofrs/uuid/v5 v5.3.0
|
github.com/gofrs/uuid/v5 v5.3.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec
|
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec
|
||||||
|
|
@ -348,6 +347,7 @@ require (
|
||||||
github.com/goburrow/serial v0.1.1-0.20211022031912-bfb69110f8dd // indirect
|
github.com/goburrow/serial v0.1.1-0.20211022031912-bfb69110f8dd // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ The following *additional* metrics are available with `details = true`:
|
||||||
- swap_current (uint, current swap usage)
|
- swap_current (uint, current swap usage)
|
||||||
- swap_peak (uint, peak swap usage)
|
- swap_peak (uint, peak swap usage)
|
||||||
- mem_avail (uint, available memory for this unit)
|
- mem_avail (uint, available memory for this unit)
|
||||||
|
- active_enter_timestamp_us (uint, timestamp in us when entered the state)
|
||||||
|
|
||||||
### Load
|
### Load
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,17 +123,18 @@ type client interface {
|
||||||
ListUnitFilesByPatternsContext(ctx context.Context, states, pattern []string) ([]dbus.UnitFile, error)
|
ListUnitFilesByPatternsContext(ctx context.Context, states, pattern []string) ([]dbus.UnitFile, error)
|
||||||
ListUnitsByNamesContext(ctx context.Context, units []string) ([]dbus.UnitStatus, error)
|
ListUnitsByNamesContext(ctx context.Context, units []string) ([]dbus.UnitStatus, error)
|
||||||
GetUnitTypePropertiesContext(ctx context.Context, unit, unitType string) (map[string]interface{}, error)
|
GetUnitTypePropertiesContext(ctx context.Context, unit, unitType string) (map[string]interface{}, error)
|
||||||
GetUnitPropertyContext(ctx context.Context, unit, propertyName string) (*dbus.Property, error)
|
GetUnitPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error)
|
||||||
ListUnitsContext(ctx context.Context) ([]dbus.UnitStatus, error)
|
ListUnitsContext(ctx context.Context) ([]dbus.UnitStatus, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type archParams struct {
|
type archParams struct {
|
||||||
client client
|
client client
|
||||||
pattern []string
|
pattern []string
|
||||||
filter filter.Filter
|
filter filter.Filter
|
||||||
unitTypeDBus string
|
unitTypeDBus string
|
||||||
scope string
|
scope string
|
||||||
user string
|
user string
|
||||||
|
warnUnitProps map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SystemdUnits) Init() error {
|
func (s *SystemdUnits) Init() error {
|
||||||
|
|
@ -176,6 +177,8 @@ func (s *SystemdUnits) Init() error {
|
||||||
return fmt.Errorf("invalid 'scope' %q", s.Scope)
|
return fmt.Errorf("invalid 'scope' %q", s.Scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.warnUnitProps = make(map[string]bool)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,26 +377,35 @@ func (s *SystemdUnits) Gather(acc telegraf.Accumulator) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get required unit file properties
|
// Get required unit file properties
|
||||||
var unitFileState string
|
unitProperties, err := s.client.GetUnitPropertiesContext(ctx, state.Name)
|
||||||
if v, err := s.client.GetUnitPropertyContext(ctx, state.Name, "UnitFileState"); err == nil {
|
if err != nil && !s.warnUnitProps[state.Name] {
|
||||||
unitFileState = strings.Trim(v.Value.String(), `'"`)
|
s.Log.Warnf("Cannot read unit properties for %q: %v", state.Name, err)
|
||||||
}
|
s.warnUnitProps[state.Name] = true
|
||||||
var unitFilePreset string
|
|
||||||
if v, err := s.client.GetUnitPropertyContext(ctx, state.Name, "UnitFilePreset"); err == nil {
|
|
||||||
unitFilePreset = strings.Trim(v.Value.String(), `'"`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tags["state"] = unitFileState
|
// Set tags
|
||||||
tags["preset"] = unitFilePreset
|
if v, found := unitProperties["UnitFileState"]; found {
|
||||||
|
tags["state"] = v.(string)
|
||||||
|
}
|
||||||
|
if v, found := unitProperties["UnitFilePreset"]; found {
|
||||||
|
tags["preset"] = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set fields
|
||||||
|
if v, found := unitProperties["ActiveEnterTimestamp"]; found {
|
||||||
|
fields["active_enter_timestamp_us"] = v
|
||||||
|
}
|
||||||
|
|
||||||
fields["status_errno"] = properties["StatusErrno"]
|
fields["status_errno"] = properties["StatusErrno"]
|
||||||
fields["restarts"] = properties["NRestarts"]
|
fields["restarts"] = properties["NRestarts"]
|
||||||
fields["pid"] = properties["MainPID"]
|
fields["pid"] = properties["MainPID"]
|
||||||
|
|
||||||
fields["mem_current"] = properties["MemoryCurrent"]
|
fields["mem_current"] = properties["MemoryCurrent"]
|
||||||
fields["mem_peak"] = properties["MemoryPeak"]
|
fields["mem_peak"] = properties["MemoryPeak"]
|
||||||
|
fields["mem_avail"] = properties["MemoryAvailable"]
|
||||||
|
|
||||||
fields["swap_current"] = properties["MemorySwapCurrent"]
|
fields["swap_current"] = properties["MemorySwapCurrent"]
|
||||||
fields["swap_peak"] = properties["MemorySwapPeak"]
|
fields["swap_peak"] = properties["MemorySwapPeak"]
|
||||||
fields["mem_avail"] = properties["MemoryAvailable"]
|
|
||||||
|
|
||||||
// Sanitize unset memory fields
|
// Sanitize unset memory fields
|
||||||
for k, value := range fields {
|
for k, value := range fields {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ package systemd_units
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
@ -13,7 +12,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sdbus "github.com/coreos/go-systemd/v22/dbus"
|
sdbus "github.com/coreos/go-systemd/v22/dbus"
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
|
@ -25,12 +23,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type properties struct {
|
type properties struct {
|
||||||
uf *sdbus.UnitFile
|
uf *sdbus.UnitFile
|
||||||
utype string
|
utype string
|
||||||
state *sdbus.UnitStatus
|
state *sdbus.UnitStatus
|
||||||
ufPreset string
|
ufPreset string
|
||||||
ufState string
|
ufState string
|
||||||
properties map[string]interface{}
|
ufActiveEnter uint64
|
||||||
|
properties map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultPattern(t *testing.T) {
|
func TestDefaultPattern(t *testing.T) {
|
||||||
|
|
@ -284,6 +283,7 @@ func TestListFiles(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShow(t *testing.T) {
|
func TestShow(t *testing.T) {
|
||||||
|
enter := time.Now().UnixMicro()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
properties map[string]properties
|
properties map[string]properties
|
||||||
|
|
@ -301,8 +301,9 @@ func TestShow(t *testing.T) {
|
||||||
ActiveState: "active",
|
ActiveState: "active",
|
||||||
SubState: "running",
|
SubState: "running",
|
||||||
},
|
},
|
||||||
ufPreset: "disabled",
|
ufPreset: "disabled",
|
||||||
ufState: "enabled",
|
ufState: "enabled",
|
||||||
|
ufActiveEnter: uint64(enter),
|
||||||
properties: map[string]interface{}{
|
properties: map[string]interface{}{
|
||||||
"Id": "example.service",
|
"Id": "example.service",
|
||||||
"StatusErrno": 0,
|
"StatusErrno": 0,
|
||||||
|
|
@ -328,17 +329,18 @@ func TestShow(t *testing.T) {
|
||||||
"preset": "disabled",
|
"preset": "disabled",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"load_code": 0,
|
"load_code": 0,
|
||||||
"active_code": 0,
|
"active_code": 0,
|
||||||
"sub_code": 0,
|
"sub_code": 0,
|
||||||
"status_errno": 0,
|
"status_errno": 0,
|
||||||
"restarts": 1,
|
"restarts": 1,
|
||||||
"mem_current": uint64(1000),
|
"mem_current": uint64(1000),
|
||||||
"mem_peak": uint64(2000),
|
"mem_peak": uint64(2000),
|
||||||
"swap_current": uint64(3000),
|
"swap_current": uint64(3000),
|
||||||
"swap_peak": uint64(4000),
|
"swap_peak": uint64(4000),
|
||||||
"mem_avail": uint64(5000),
|
"mem_avail": uint64(5000),
|
||||||
"pid": 9999,
|
"pid": 9999,
|
||||||
|
"active_enter_timestamp_us": uint64(enter),
|
||||||
},
|
},
|
||||||
time.Unix(0, 0),
|
time.Unix(0, 0),
|
||||||
),
|
),
|
||||||
|
|
@ -355,8 +357,9 @@ func TestShow(t *testing.T) {
|
||||||
ActiveState: "active",
|
ActiveState: "active",
|
||||||
SubState: "exited",
|
SubState: "exited",
|
||||||
},
|
},
|
||||||
ufPreset: "disabled",
|
ufPreset: "disabled",
|
||||||
ufState: "enabled",
|
ufState: "enabled",
|
||||||
|
ufActiveEnter: 0,
|
||||||
properties: map[string]interface{}{
|
properties: map[string]interface{}{
|
||||||
"Id": "example.service",
|
"Id": "example.service",
|
||||||
"StatusErrno": 0,
|
"StatusErrno": 0,
|
||||||
|
|
@ -376,16 +379,17 @@ func TestShow(t *testing.T) {
|
||||||
"preset": "disabled",
|
"preset": "disabled",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"load_code": 0,
|
"load_code": 0,
|
||||||
"active_code": 0,
|
"active_code": 0,
|
||||||
"sub_code": 4,
|
"sub_code": 4,
|
||||||
"status_errno": 0,
|
"status_errno": 0,
|
||||||
"restarts": 0,
|
"restarts": 0,
|
||||||
"mem_current": uint64(0),
|
"mem_current": uint64(0),
|
||||||
"mem_peak": uint64(0),
|
"mem_peak": uint64(0),
|
||||||
"swap_current": uint64(0),
|
"swap_current": uint64(0),
|
||||||
"swap_peak": uint64(0),
|
"swap_peak": uint64(0),
|
||||||
"mem_avail": uint64(0),
|
"mem_avail": uint64(0),
|
||||||
|
"active_enter_timestamp_us": uint64(0),
|
||||||
},
|
},
|
||||||
time.Unix(0, 0),
|
time.Unix(0, 0),
|
||||||
),
|
),
|
||||||
|
|
@ -402,8 +406,9 @@ func TestShow(t *testing.T) {
|
||||||
ActiveState: "failed",
|
ActiveState: "failed",
|
||||||
SubState: "failed",
|
SubState: "failed",
|
||||||
},
|
},
|
||||||
ufPreset: "disabled",
|
ufPreset: "disabled",
|
||||||
ufState: "enabled",
|
ufState: "enabled",
|
||||||
|
ufActiveEnter: uint64(enter),
|
||||||
properties: map[string]interface{}{
|
properties: map[string]interface{}{
|
||||||
"Id": "example.service",
|
"Id": "example.service",
|
||||||
"StatusErrno": 10,
|
"StatusErrno": 10,
|
||||||
|
|
@ -428,16 +433,17 @@ func TestShow(t *testing.T) {
|
||||||
"preset": "disabled",
|
"preset": "disabled",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"load_code": 0,
|
"load_code": 0,
|
||||||
"active_code": 3,
|
"active_code": 3,
|
||||||
"sub_code": 12,
|
"sub_code": 12,
|
||||||
"status_errno": 10,
|
"status_errno": 10,
|
||||||
"restarts": 1,
|
"restarts": 1,
|
||||||
"mem_current": uint64(1000),
|
"mem_current": uint64(1000),
|
||||||
"mem_peak": uint64(2000),
|
"mem_peak": uint64(2000),
|
||||||
"swap_current": uint64(3000),
|
"swap_current": uint64(3000),
|
||||||
"swap_peak": uint64(4000),
|
"swap_peak": uint64(4000),
|
||||||
"mem_avail": uint64(5000),
|
"mem_avail": uint64(5000),
|
||||||
|
"active_enter_timestamp_us": uint64(enter),
|
||||||
},
|
},
|
||||||
time.Unix(0, 0),
|
time.Unix(0, 0),
|
||||||
),
|
),
|
||||||
|
|
@ -454,8 +460,9 @@ func TestShow(t *testing.T) {
|
||||||
ActiveState: "inactive",
|
ActiveState: "inactive",
|
||||||
SubState: "dead",
|
SubState: "dead",
|
||||||
},
|
},
|
||||||
ufPreset: "disabled",
|
ufPreset: "disabled",
|
||||||
ufState: "enabled",
|
ufState: "enabled",
|
||||||
|
ufActiveEnter: uint64(0),
|
||||||
properties: map[string]interface{}{
|
properties: map[string]interface{}{
|
||||||
"Id": "example.service",
|
"Id": "example.service",
|
||||||
},
|
},
|
||||||
|
|
@ -473,14 +480,15 @@ func TestShow(t *testing.T) {
|
||||||
"preset": "disabled",
|
"preset": "disabled",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"load_code": 2,
|
"load_code": 2,
|
||||||
"active_code": 2,
|
"active_code": 2,
|
||||||
"sub_code": 1,
|
"sub_code": 1,
|
||||||
"mem_current": uint64(0),
|
"mem_current": uint64(0),
|
||||||
"mem_peak": uint64(0),
|
"mem_peak": uint64(0),
|
||||||
"swap_current": uint64(0),
|
"swap_current": uint64(0),
|
||||||
"swap_peak": uint64(0),
|
"swap_peak": uint64(0),
|
||||||
"mem_avail": uint64(0),
|
"mem_avail": uint64(0),
|
||||||
|
"active_enter_timestamp_us": uint64(0),
|
||||||
},
|
},
|
||||||
time.Unix(0, 0),
|
time.Unix(0, 0),
|
||||||
),
|
),
|
||||||
|
|
@ -517,8 +525,9 @@ func TestShow(t *testing.T) {
|
||||||
ActiveState: "inactive",
|
ActiveState: "inactive",
|
||||||
SubState: "dead",
|
SubState: "dead",
|
||||||
},
|
},
|
||||||
ufPreset: "disabled",
|
ufPreset: "disabled",
|
||||||
ufState: "disabled",
|
ufState: "disabled",
|
||||||
|
ufActiveEnter: uint64(0),
|
||||||
properties: map[string]interface{}{
|
properties: map[string]interface{}{
|
||||||
"Id": "example.service",
|
"Id": "example.service",
|
||||||
"StatusErrno": 0,
|
"StatusErrno": 0,
|
||||||
|
|
@ -543,16 +552,17 @@ func TestShow(t *testing.T) {
|
||||||
"preset": "disabled",
|
"preset": "disabled",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"load_code": 0,
|
"load_code": 0,
|
||||||
"active_code": int64(2),
|
"active_code": int64(2),
|
||||||
"sub_code": 1,
|
"sub_code": 1,
|
||||||
"status_errno": 0,
|
"status_errno": 0,
|
||||||
"restarts": 0,
|
"restarts": 0,
|
||||||
"mem_current": uint64(0),
|
"mem_current": uint64(0),
|
||||||
"mem_peak": uint64(0),
|
"mem_peak": uint64(0),
|
||||||
"swap_current": uint64(0),
|
"swap_current": uint64(0),
|
||||||
"swap_peak": uint64(0),
|
"swap_peak": uint64(0),
|
||||||
"mem_avail": uint64(0),
|
"mem_avail": uint64(0),
|
||||||
|
"active_enter_timestamp_us": uint64(0),
|
||||||
},
|
},
|
||||||
time.Unix(0, 0),
|
time.Unix(0, 0),
|
||||||
),
|
),
|
||||||
|
|
@ -974,19 +984,17 @@ func (c *fakeClient) GetUnitTypePropertiesContext(_ context.Context, unit, unitT
|
||||||
return u.properties, nil
|
return u.properties, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeClient) GetUnitPropertyContext(_ context.Context, unit, propertyName string) (*sdbus.Property, error) {
|
func (c *fakeClient) GetUnitPropertiesContext(_ context.Context, unit string) (map[string]interface{}, error) {
|
||||||
u, found := c.units[unit]
|
u, found := c.units[unit]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch propertyName {
|
return map[string]interface{}{
|
||||||
case "UnitFileState":
|
"UnitFileState": u.ufState,
|
||||||
return &sdbus.Property{Name: propertyName, Value: dbus.MakeVariant(u.ufState)}, nil
|
"UnitFilePreset": u.ufPreset,
|
||||||
case "UnitFilePreset":
|
"ActiveEnterTimestamp": u.ufActiveEnter,
|
||||||
return &sdbus.Property{Name: propertyName, Value: dbus.MakeVariant(u.ufPreset)}, nil
|
}, nil
|
||||||
}
|
|
||||||
return nil, errors.New("unknown property")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeClient) ListUnitsContext(_ context.Context) ([]sdbus.UnitStatus, error) {
|
func (c *fakeClient) ListUnitsContext(_ context.Context) ([]sdbus.UnitStatus, error) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue