-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): add counting contacts metrics (#1002)
- Loading branch information
Showing
10 changed files
with
362 additions
and
27 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package stats | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/moira-alert/moira" | ||
"github.com/moira-alert/moira/metrics" | ||
) | ||
|
||
type contactStats struct { | ||
metrics *metrics.ContactsMetrics | ||
database moira.Database | ||
logger moira.Logger | ||
} | ||
|
||
// NewContactStats creates and initializes a new contactStats object. | ||
func NewContactStats( | ||
metricsRegistry metrics.Registry, | ||
database moira.Database, | ||
logger moira.Logger, | ||
) *contactStats { | ||
return &contactStats{ | ||
metrics: metrics.NewContactsMetrics(metricsRegistry), | ||
database: database, | ||
logger: logger, | ||
} | ||
} | ||
|
||
// StartReport starts reporting statistics about contacts. | ||
func (stats *contactStats) StartReport(stop <-chan struct{}) { | ||
checkTicker := time.NewTicker(time.Minute) | ||
defer checkTicker.Stop() | ||
|
||
stats.logger.Info().Msg("Start contact statistics reporter") | ||
|
||
for { | ||
select { | ||
case <-stop: | ||
stats.logger.Info().Msg("Stop contact statistics reporter") | ||
return | ||
|
||
case <-checkTicker.C: | ||
stats.checkContactsCount() | ||
} | ||
} | ||
} | ||
|
||
func (stats *contactStats) checkContactsCount() { | ||
contacts, err := stats.database.GetAllContacts() | ||
if err != nil { | ||
stats.logger.Warning(). | ||
Error(err). | ||
Msg("Failed to get all contacts") | ||
return | ||
} | ||
|
||
contactsCounter := make(map[string]int64) | ||
|
||
for _, contact := range contacts { | ||
if contact != nil { | ||
contactsCounter[contact.Type]++ | ||
} | ||
} | ||
|
||
for contact, count := range contactsCounter { | ||
stats.metrics.Mark(contact, count) | ||
} | ||
} |
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,107 @@ | ||
package stats | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
|
||
"github.com/moira-alert/moira" | ||
logging "github.com/moira-alert/moira/logging/zerolog_adapter" | ||
"github.com/moira-alert/moira/metrics" | ||
mock_moira_alert "github.com/moira-alert/moira/mock/moira-alert" | ||
mock_metrics "github.com/moira-alert/moira/mock/moira-alert/metrics" | ||
. "github.com/smartystreets/goconvey/convey" | ||
) | ||
|
||
const metricPrefix = "contacts" | ||
|
||
var testContacts = []*moira.ContactData{ | ||
{ | ||
Type: "test1", | ||
}, | ||
{ | ||
Type: "test1", | ||
}, | ||
{ | ||
Type: "test2", | ||
}, | ||
{ | ||
Type: "test3", | ||
}, | ||
{ | ||
Type: "test2", | ||
}, | ||
{ | ||
Type: "test1", | ||
}, | ||
} | ||
|
||
func TestNewContactsStats(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
defer mockCtrl.Finish() | ||
|
||
registry := mock_metrics.NewMockRegistry(mockCtrl) | ||
database := mock_moira_alert.NewMockDatabase(mockCtrl) | ||
logger, _ := logging.GetLogger("Test") | ||
|
||
Convey("Successfully created new contacts stats", t, func() { | ||
stats := NewContactStats(registry, database, logger) | ||
|
||
So(stats, ShouldResemble, &contactStats{ | ||
metrics: metrics.NewContactsMetrics(registry), | ||
database: database, | ||
logger: logger, | ||
}) | ||
}) | ||
} | ||
|
||
func TestCheckingContactsCount(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
defer mockCtrl.Finish() | ||
|
||
registry := mock_metrics.NewMockRegistry(mockCtrl) | ||
database := mock_moira_alert.NewMockDatabase(mockCtrl) | ||
logger := mock_moira_alert.NewMockLogger(mockCtrl) | ||
eventBuilder := mock_moira_alert.NewMockEventBuilder(mockCtrl) | ||
|
||
test1Meter := mock_metrics.NewMockMeter(mockCtrl) | ||
test2Meter := mock_metrics.NewMockMeter(mockCtrl) | ||
test3Meter := mock_metrics.NewMockMeter(mockCtrl) | ||
|
||
var test1ContactCount, test2ContactCount, test3ContactCount int64 | ||
var test1ContactType, test2ContactType, test3ContactType string | ||
test1ContactCount, test1ContactType = 3, "test1" | ||
test2ContactCount, test2ContactType = 2, "test2" | ||
test3ContactCount, test3ContactType = 1, "test3" | ||
|
||
getAllContactsErr := errors.New("failed to get all contacts") | ||
|
||
Convey("Test checking contacts count", t, func() { | ||
Convey("Successfully checking contacts count", func() { | ||
database.EXPECT().GetAllContacts().Return(testContacts, nil).Times(1) | ||
|
||
registry.EXPECT().NewMeter(metricPrefix, test1ContactType).Return(test1Meter).Times(1) | ||
registry.EXPECT().NewMeter(metricPrefix, test2ContactType).Return(test2Meter).Times(1) | ||
registry.EXPECT().NewMeter(metricPrefix, test3ContactType).Return(test3Meter).Times(1) | ||
|
||
test1Meter.EXPECT().Mark(test1ContactCount) | ||
test2Meter.EXPECT().Mark(test2ContactCount) | ||
test3Meter.EXPECT().Mark(test3ContactCount) | ||
|
||
stats := NewContactStats(registry, database, logger) | ||
stats.checkContactsCount() | ||
}) | ||
|
||
Convey("Get error from get all contacts", func() { | ||
database.EXPECT().GetAllContacts().Return(nil, getAllContactsErr).Times(1) | ||
|
||
logger.EXPECT().Warning().Return(eventBuilder).Times(1) | ||
eventBuilder.EXPECT().Error(getAllContactsErr).Return(eventBuilder).Times(1) | ||
eventBuilder.EXPECT().Msg("Failed to get all contacts").Times(1) | ||
|
||
stats := NewContactStats(registry, database, logger) | ||
stats.checkContactsCount() | ||
}) | ||
}) | ||
} |
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,40 @@ | ||
package stats | ||
|
||
import ( | ||
"gopkg.in/tomb.v2" | ||
) | ||
|
||
// StatsReporter represents an interface for objects that report statistics. | ||
type StatsReporter interface { | ||
StartReport(stop <-chan struct{}) | ||
} | ||
|
||
type statsManager struct { | ||
tomb tomb.Tomb | ||
reporters []StatsReporter | ||
} | ||
|
||
// NewStatsManager creates a new statsManager instance with the given StatsReporters. | ||
func NewStatsManager(reporters ...StatsReporter) *statsManager { | ||
return &statsManager{ | ||
reporters: reporters, | ||
} | ||
} | ||
|
||
// Start starts reporting statistics for all registered StatsReporters. | ||
func (manager *statsManager) Start() { | ||
for _, reporter := range manager.reporters { | ||
reporter := reporter | ||
|
||
manager.tomb.Go(func() error { | ||
reporter.StartReport(manager.tomb.Dying()) | ||
return nil | ||
}) | ||
} | ||
} | ||
|
||
// Stop stops all reporting activities and waits for the completion. | ||
func (manager *statsManager) Stop() error { | ||
manager.tomb.Kill(nil) | ||
return manager.tomb.Wait() | ||
} |
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,35 @@ | ||
package stats | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
mock_moira_alert "github.com/moira-alert/moira/mock/moira-alert" | ||
. "github.com/smartystreets/goconvey/convey" | ||
) | ||
|
||
func TestNewStatsManager(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
defer mockCtrl.Finish() | ||
|
||
triggerStats := mock_moira_alert.NewMockStatsReporter(mockCtrl) | ||
contactStats := mock_moira_alert.NewMockStatsReporter(mockCtrl) | ||
|
||
Convey("Test new stats manager", t, func() { | ||
Convey("Successfully create new stats manager", func() { | ||
manager := NewStatsManager(triggerStats, contactStats) | ||
|
||
So(manager.reporters, ShouldResemble, []StatsReporter{triggerStats, contactStats}) | ||
}) | ||
|
||
Convey("Successfully start stats manager", func() { | ||
manager := NewStatsManager(triggerStats, contactStats) | ||
|
||
triggerStats.EXPECT().StartReport(manager.tomb.Dying()).Times(1) | ||
contactStats.EXPECT().StartReport(manager.tomb.Dying()).Times(1) | ||
|
||
manager.Start() | ||
defer manager.Stop() //nolint | ||
}) | ||
}) | ||
} |
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
Oops, something went wrong.