Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion metricbeat/mb/module/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type metricSetWrapper struct {

periodic bool // Set to true if this metricset is a periodic fetcher
failureThreshold uint // threshold of consecutive errors needed to set the stream as degraded
optional bool // When true, fetch failures do not degrade agent health
}

// stats bundles common metricset stats.
Expand Down Expand Up @@ -119,6 +120,7 @@ func createWrapper(module mb.Module, metricSets []mb.MetricSet, monitoring beatm

var streamHealthSettings struct {
FailureThreshold *uint `config:"failure_threshold"`
Optional *bool `config:"optional"`
}

err := module.UnpackConfig(&streamHealthSettings)
Expand All @@ -131,12 +133,18 @@ func createWrapper(module mb.Module, metricSets []mb.MetricSet, monitoring beatm
failureThreshold = *streamHealthSettings.FailureThreshold
}

optional := false
if streamHealthSettings.Optional != nil {
optional = *streamHealthSettings.Optional
}

for i, metricSet := range metricSets {
wrapper.metricSets[i] = &metricSetWrapper{
MetricSet: metricSet,
module: wrapper,
stats: getMetricSetStats(monitoring, wrapper.Name(), metricSet.Name()),
failureThreshold: failureThreshold,
optional: optional,
}
}
return wrapper, nil
Expand Down Expand Up @@ -324,7 +332,7 @@ func (msw *metricSetWrapper) handleFetchError(err error, reporter mb.PushReporte
default:
reporter.Error(err)
msw.stats.consecutiveFailures.Inc()
if msw.failureThreshold > 0 && msw.stats.consecutiveFailures != nil && uint(msw.stats.consecutiveFailures.Get()) >= msw.failureThreshold {
if !msw.optional && msw.failureThreshold > 0 && msw.stats.consecutiveFailures != nil && uint(msw.stats.consecutiveFailures.Get()) >= msw.failureThreshold {
// mark it as degraded for any other issue encountered
msw.module.UpdateStatus(status.Degraded, fmt.Sprintf("Error fetching data for metricset %s.%s: %v", msw.module.Name(), msw.Name(), err))
}
Expand Down
44 changes: 44 additions & 0 deletions metricbeat/mb/module/wrapper_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,50 @@ func TestWrapperHandleFetchErrorSync(t *testing.T) {
}
},
},
{
name: "optional = true: status never becomes DEGRADED regardless of errors",
config: newConfig(t, map[string]interface{}{
"module": mockModuleName,
"metricsets": []string{mockMetricSetName},
"period": "100ms",
"hosts": []string{"testhost"},
"optional": true,
}),
setup: func(t *testing.T, fetcher *mockReportingFetcher, pushReporter *mockPushReporterV2, statusReporter *mockStatusReporter) {
fetcher.On("Fetch", pushReporter).Return(fetchError).Times(5)
pushReporter.On("Error", fetchError).Return(true).Times(5)
},
iterations: 5,
assertIteration: func(t *testing.T, i int, msWrapper *metricSetWrapper, fetcher *mockReportingFetcher, pushReporter *mockPushReporterV2, statusReporter *mockStatusReporter) {
t.Logf("Assertion after iteration %d", i)
assert.Truef(t, statusReporter.AssertNotCalled(t, "UpdateStatus", status.Degraded, mock.AnythingOfType("string")), "optional stream should not be degraded at iteration %d", i)
assert.Equal(t, uint64(i+1), msWrapper.stats.consecutiveFailures.Get(), "consecutive failures should be tracked even for optional metricsets")
},
},
{
name: "optional = true: status returns to Running after recovery",
config: newConfig(t, map[string]interface{}{
"module": mockModuleName,
"metricsets": []string{mockMetricSetName},
"period": "100ms",
"hosts": []string{"testhost"},
"optional": true,
}),
setup: func(t *testing.T, fetcher *mockReportingFetcher, pushReporter *mockPushReporterV2, statusReporter *mockStatusReporter) {
fetcher.On("Fetch", pushReporter).Return(fetchError).Times(3)
fetcher.On("Fetch", pushReporter).Return(nil).Once()
pushReporter.On("Error", fetchError).Return(true).Times(3)
statusReporter.On("UpdateStatus", status.Running, mock.AnythingOfType("string")).Once()
},
iterations: 4,
assertIteration: func(t *testing.T, i int, msWrapper *metricSetWrapper, fetcher *mockReportingFetcher, pushReporter *mockPushReporterV2, statusReporter *mockStatusReporter) {
t.Logf("Assertion after iteration %d", i)
assert.Truef(t, statusReporter.AssertNotCalled(t, "UpdateStatus", status.Degraded, mock.AnythingOfType("string")), "optional stream should never be degraded at iteration %d", i)
if i == 3 {
assert.Truef(t, statusReporter.AssertCalled(t, "UpdateStatus", status.Running, mock.AnythingOfType("string")), "stream should be running after recovery at iteration %d", i)
}
},
},
}

for _, tc := range testcases {
Expand Down
Loading