master
go 137 lines 3.36 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux
4
5 package systemdunits
6
7 import (
8 "context"
9 _ "embed"
10 "errors"
11 "fmt"
12 "time"
13
14 "github.com/coreos/go-systemd/v22/dbus"
15
16 "github.com/netdata/netdata/go/plugins/pkg/confopt"
17 "github.com/netdata/netdata/go/plugins/pkg/matcher"
18 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
19 )
20
21 //go:embed "config_schema.json"
22 var configSchema string
23
24 func init() {
25 collectorapi.Register("systemdunits", collectorapi.Creator{
26 JobConfigSchema: configSchema,
27 Defaults: collectorapi.Defaults{
28 UpdateEvery: 10, // gathering systemd units can be a CPU intensive op
29 },
30 Create: func() collectorapi.CollectorV1 { return New() },
31 Config: func() any { return &Config{} },
32 })
33 }
34
35 func New() *Collector {
36 return &Collector{
37 Config: Config{
38 Timeout: confopt.Duration(time.Second * 2),
39 Include: []string{"*.service"},
40 SkipTransient: false,
41 CollectUnitFiles: false,
42 IncludeUnitFiles: []string{"*.service"},
43 CollectUnitFilesEvery: confopt.Duration(time.Minute * 5),
44 },
45 charts: &collectorapi.Charts{},
46 client: newSystemdDBusClient(),
47 seenUnits: make(map[string]bool),
48 unitTransient: make(map[string]bool),
49 seenUnitFiles: make(map[string]bool),
50 }
51 }
52
53 type Config struct {
54 UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
55 Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout"`
56 Include []string `yaml:"include,omitempty" json:"include"`
57 SkipTransient bool `yaml:"skip_transient" json:"skip_transient"`
58 CollectUnitFiles bool `yaml:"collect_unit_files" json:"collect_unit_files"`
59 IncludeUnitFiles []string `yaml:"include_unit_files,omitempty" json:"include_unit_files"`
60 CollectUnitFilesEvery confopt.Duration `yaml:"collect_unit_files_every,omitempty" json:"collect_unit_files_every"`
61 }
62
63 type Collector struct {
64 collectorapi.Base
65 Config `yaml:",inline" json:""`
66
67 client systemdClient
68 conn systemdConnection
69
70 systemdVersion int
71
72 seenUnits map[string]bool
73 unitTransient map[string]bool
74 unitSr matcher.Matcher
75
76 lastListUnitFilesTime time.Time
77 cachedUnitFiles []dbus.UnitFile
78 seenUnitFiles map[string]bool
79
80 charts *collectorapi.Charts
81 }
82
83 func (c *Collector) Configuration() any {
84 return c.Config
85 }
86
87 func (c *Collector) Init(context.Context) error {
88 if err := c.validateConfig(); err != nil {
89 return fmt.Errorf("config validation: %v", err)
90 }
91
92 sr, err := c.initUnitSelector()
93 if err != nil {
94 return fmt.Errorf("init unit selector: %v", err)
95 }
96 c.unitSr = sr
97
98 c.Debugf("timeout: %s", c.Timeout)
99 c.Debugf("units: patterns '%v'", c.Include)
100 c.Debugf("unit files: enabled '%v', every '%s', patterns: %v",
101 c.CollectUnitFiles, c.CollectUnitFilesEvery, c.IncludeUnitFiles)
102
103 return nil
104 }
105
106 func (c *Collector) Check(context.Context) error {
107 mx, err := c.collect()
108 if err != nil {
109 return err
110 }
111
112 if len(mx) == 0 {
113 return errors.New("no metrics collected")
114 }
115
116 return nil
117 }
118
119 func (c *Collector) Charts() *collectorapi.Charts {
120 return c.charts
121 }
122
123 func (c *Collector) Collect(context.Context) map[string]int64 {
124 mx, err := c.collect()
125 if err != nil {
126 c.Error(err)
127 }
128
129 if len(mx) == 0 {
130 return nil
131 }
132 return mx
133 }
134
135 func (c *Collector) Cleanup(context.Context) {
136 c.closeConnection()
137 }