-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from stefanotorresi/feature/instrumented-coll…
…ectors Instrumented collectors
- Loading branch information
Showing
18 changed files
with
518 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package collector | ||
|
||
import ( | ||
"github.com/prometheus/client_golang/prometheus" | ||
log "github.com/sirupsen/logrus" | ||
|
||
"github.com/ClusterLabs/ha_cluster_exporter/internal/clock" | ||
) | ||
|
||
//go:generate mockgen -destination ../test/mock_collector/instrumented_collector.go github.com/ClusterLabs/ha_cluster_exporter/collector InstrumentableCollector | ||
|
||
// describes a collector that can return errors from collection cycles, | ||
// instead of the default Prometheus one, which has void Collect returns | ||
type InstrumentableCollector interface { | ||
prometheus.Collector | ||
SubsystemCollector | ||
CollectWithError(ch chan<- prometheus.Metric) error | ||
} | ||
|
||
type InstrumentedCollector struct { | ||
collector InstrumentableCollector | ||
Clock clock.Clock | ||
scrapeDurationDesc *prometheus.Desc | ||
scrapeSuccessDesc *prometheus.Desc | ||
} | ||
|
||
func NewInstrumentedCollector(collector InstrumentableCollector) *InstrumentedCollector { | ||
return &InstrumentedCollector{ | ||
collector, | ||
&clock.SystemClock{}, | ||
prometheus.NewDesc( | ||
prometheus.BuildFQName(NAMESPACE, "scrape", "duration_seconds"), | ||
"Duration of a collector scrape.", | ||
nil, | ||
prometheus.Labels{ | ||
"collector": collector.GetSubsystem(), | ||
}, | ||
), | ||
prometheus.NewDesc( | ||
prometheus.BuildFQName(NAMESPACE, "scrape", "success"), | ||
"Whether a collector succeeded.", | ||
nil, | ||
prometheus.Labels{ | ||
"collector": collector.GetSubsystem(), | ||
}, | ||
), | ||
} | ||
} | ||
|
||
func (ic *InstrumentedCollector) Collect(ch chan<- prometheus.Metric) { | ||
var success float64 | ||
begin := ic.Clock.Now() | ||
err := ic.collector.CollectWithError(ch) | ||
duration := ic.Clock.Since(begin) | ||
if err == nil { | ||
success = 1 | ||
} else { | ||
log.Warnf("'%s' collector scrape failed: %s", ic.collector.GetSubsystem(), err) | ||
} | ||
ch <- prometheus.MustNewConstMetric(ic.scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds()) | ||
ch <- prometheus.MustNewConstMetric(ic.scrapeSuccessDesc, prometheus.GaugeValue, success) | ||
} | ||
|
||
func (ic *InstrumentedCollector) Describe(ch chan<- *prometheus.Desc) { | ||
ic.collector.Describe(ch) | ||
ch <- ic.scrapeDurationDesc | ||
ch <- ic.scrapeSuccessDesc | ||
} | ||
|
||
func (ic *InstrumentedCollector) GetSubsystem() string { | ||
return ic.collector.GetSubsystem() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package collector | ||
|
||
import ( | ||
"errors" | ||
"io/ioutil" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
"github.com/prometheus/client_golang/prometheus/testutil" | ||
"github.com/sirupsen/logrus" | ||
testlog "github.com/sirupsen/logrus/hooks/test" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/ClusterLabs/ha_cluster_exporter/internal/clock" | ||
"github.com/ClusterLabs/ha_cluster_exporter/test/mock_collector" | ||
) | ||
|
||
func init() { | ||
logrus.SetOutput(ioutil.Discard) | ||
} | ||
|
||
func TestInstrumentedCollector(t *testing.T) { | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
mockCollector := mock_collector.NewMockInstrumentableCollector(ctrl) | ||
mockCollector.EXPECT().GetSubsystem().Return("mock_collector").AnyTimes() | ||
mockCollector.EXPECT().Describe(gomock.Any()) | ||
mockCollector.EXPECT().CollectWithError(gomock.Any()) | ||
|
||
SUT := NewInstrumentedCollector(mockCollector) | ||
SUT.Clock = &clock.StoppedClock{} | ||
|
||
metrics := `# HELP ha_cluster_scrape_duration_seconds Duration of a collector scrape. | ||
# TYPE ha_cluster_scrape_duration_seconds gauge | ||
ha_cluster_scrape_duration_seconds{collector="mock_collector"} 1.234 | ||
# HELP ha_cluster_scrape_success Whether a collector succeeded. | ||
# TYPE ha_cluster_scrape_success gauge | ||
ha_cluster_scrape_success{collector="mock_collector"} 1 | ||
` | ||
|
||
err := testutil.CollectAndCompare(SUT, strings.NewReader(metrics)) | ||
assert.NoError(t, err) | ||
} | ||
|
||
func TestInstrumentedCollectorScrapeFailure(t *testing.T) { | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
logHook := testlog.NewGlobal() | ||
defer logHook.Reset() | ||
|
||
mockCollector := mock_collector.NewMockInstrumentableCollector(ctrl) | ||
mockCollector.EXPECT().GetSubsystem().Return("mock_collector").AnyTimes() | ||
mockCollector.EXPECT().Describe(gomock.Any()) | ||
mockCollector.EXPECT().CollectWithError(gomock.Any()).Return(errors.New("test error")) | ||
|
||
SUT := NewInstrumentedCollector(mockCollector) | ||
|
||
metrics := `# HELP ha_cluster_scrape_success Whether a collector succeeded. | ||
# TYPE ha_cluster_scrape_success gauge | ||
ha_cluster_scrape_success{collector="mock_collector"} 0 | ||
` | ||
|
||
err := testutil.CollectAndCompare(SUT, strings.NewReader(metrics), "ha_cluster_scrape_success") | ||
assert.NoError(t, err) | ||
|
||
assert.Len(t, logHook.Entries, 1) | ||
assert.Contains(t, logHook.LastEntry().Message, "test error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.