master
go 170 lines 4.93 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package nagios
4
5 import (
6 "context"
7 "runtime"
8 "time"
9
10 "github.com/netdata/netdata/go/plugins/pkg/metrix"
11 "github.com/netdata/netdata/go/plugins/plugin/framework/chartengine"
12 )
13
14 func (c *Collector) collect(ctx context.Context) error {
15 execMetrics, err := c.collectIfDue(ctx)
16 if err != nil {
17 return err
18 }
19 c.emitMetrics(execMetrics)
20 return nil
21 }
22
23 func (c *Collector) collectIfDue(ctx context.Context) (executionMetrics, error) {
24 now := c.now()
25 if !c.state.due(now) {
26 return executionMetrics{}, nil
27 }
28
29 if c.skipDisallowedPeriod(now) {
30 return executionMetrics{}, nil
31 }
32
33 res, err := c.executeDueCheck(ctx, now)
34 if err != nil {
35 return executionMetrics{}, err
36 }
37
38 c.completeDueCheck(now, res)
39 return executionMetricsFromResult(res), nil
40 }
41
42 func (c *Collector) skipDisallowedPeriod(now time.Time) bool {
43 if c.job.period == nil || c.job.period.Allows(now) {
44 return false
45 }
46 c.state.recordPeriodBlocked()
47 c.state.scheduleNextAllowed(now, c.job.config.CheckInterval.Duration(), c.job.period)
48 return true
49 }
50
51 func (c *Collector) executeDueCheck(ctx context.Context, now time.Time) (checkRunResult, error) {
52 res, err := c.runner.Run(ctx, checkRunRequest{
53 Job: c.job.config,
54 Vnode: vnodeInfoFromVirtualNode(c.VirtualNode(), c.job.config.Vnode),
55 MacroState: c.state.macroState(),
56 Now: now,
57 Log: c.Logger,
58 })
59 if err != nil {
60 if runErr := classifyRunError(ctx, res.ExitCode, err); runErr != nil {
61 return checkRunResult{}, runErr
62 }
63 }
64 return res, nil
65 }
66
67 func (c *Collector) completeDueCheck(now time.Time, res checkRunResult) {
68 c.state.completeRun(now, res.ServiceState, res.JobState, c.router.route(c.job.config.CheckName, res.Parsed.Perfdata), c.job.config)
69 }
70
71 func (c *Collector) emitMetrics(execMetrics executionMetrics) {
72 // Empty meter prefix: the namespace comes from charts.yaml context_namespace (nagios),
73 // which also prefixes autogen perfdata contexts. A "nagios" meter prefix would double it.
74 sm := c.store.Write().SnapshotMeter("")
75
76 jobName := c.job.config.Name
77 if jobName == "" {
78 jobName = c.Config.JobConfig.Name
79 }
80
81 jobLbl := sm.LabelSet(metrix.Label{Key: "nagios_job", Value: jobName})
82 jobMeter := sm.WithLabelSet(jobLbl)
83 jobStatePoint := projectJobExecutionState(c.state.currentJobState(), c.state.isRetrying())
84
85 jobMeter.StateSet(
86 "job.execution_state",
87 metrix.WithStateSetMode(metrix.ModeBitSet),
88 metrix.WithStateSetStates(jobExecutionStateNames...),
89 metrix.WithUnit("state"),
90 ).ObserveStateSet(jobStatePoint)
91
92 checkName := perfSourceFromCheckName(c.job.config.CheckName)
93 jobMeter.StateSet(
94 "perfdata."+checkName+".job.execution_state",
95 metrix.WithStateSetMode(metrix.ModeBitSet),
96 metrix.WithStateSetStates(jobExecutionStateNames...),
97 metrix.WithChartFamily(perfdataFamily(checkName)),
98 metrix.WithChartPriority(chartengine.Priority-10),
99 metrix.WithUnit("state"),
100 ).ObserveStateSet(jobStatePoint)
101
102 jobMeter.Gauge(
103 "job.execution_duration",
104 metrix.WithUnit("seconds"),
105 metrix.WithFloat(true),
106 ).Observe(execMetrics.durationSeconds)
107
108 if runtime.GOOS != "windows" {
109 jobMeter.Gauge(
110 "job.execution_cpu_total",
111 metrix.WithUnit("seconds"),
112 metrix.WithFloat(true),
113 ).Observe(execMetrics.cpuTotalSeconds)
114
115 jobMeter.Gauge(
116 "job.execution_max_rss",
117 metrix.WithUnit("bytes"),
118 ).Observe(execMetrics.maxRSSBytes)
119 }
120
121 for _, measureSet := range c.state.perfValueSets() {
122 fields := perfMeasureSetValues(measureSet.value)
123 if measureSet.counter {
124 jobMeter.MeasureSetCounter(
125 measureSet.name,
126 metrix.WithMeasureSetFields(perfMeasureSetFieldSpecs()...),
127 metrix.WithChartFamily(perfdataFamily(measureSet.checkName)),
128 metrix.WithUnit(measureSet.unit),
129 metrix.WithFloat(true),
130 ).ObserveTotalFields(fields)
131 } else {
132 jobMeter.MeasureSetGauge(
133 measureSet.name,
134 metrix.WithMeasureSetFields(perfMeasureSetFieldSpecs()...),
135 metrix.WithChartFamily(perfdataFamily(measureSet.checkName)),
136 metrix.WithUnit(measureSet.unit),
137 metrix.WithFloat(true),
138 ).ObserveFields(fields)
139 }
140 }
141
142 for _, thresholdState := range c.state.perfThresholdStates() {
143 inst := jobMeter.StateSet(
144 thresholdState.name,
145 metrix.WithStateSetMode(metrix.ModeBitSet),
146 metrix.WithStateSetStates(perfThresholdStateNames...),
147 metrix.WithChartFamily(perfdataFamily(thresholdState.checkName)),
148 metrix.WithUnit("state"),
149 )
150 if thresholdState.state == "" {
151 inst.ObserveStateSet(perfThresholdStatePoint(""))
152 } else {
153 inst.Enable(thresholdState.state)
154 }
155
156 jobMeter.WithLabels(metrix.Label{
157 Key: perfdataValueLabelKey,
158 Value: thresholdState.perfdataValue,
159 }).StateSet(
160 jobPerfdataThresholdMetricName,
161 metrix.WithStateSetMode(metrix.ModeBitSet),
162 metrix.WithStateSetStates(perfThresholdAlertStateNames...),
163 metrix.WithUnit("state"),
164 ).ObserveStateSet(projectPerfThresholdAlertState(thresholdState.state, c.state.isRetrying()))
165 }
166 }
167
168 func perfdataFamily(checkName string) string {
169 return "Perfdata/" + checkName
170 }