Skip to content

Commit

Permalink
refactor: abstract structs event.Raw and event.Slo into interfaces
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <[email protected]>
  • Loading branch information
FUSAKLA committed Aug 24, 2021
1 parent da169a9 commit acf416e
Show file tree
Hide file tree
Showing 49 changed files with 720 additions and 598 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- [#xxx]() Every event has a unique id set, so it can be filtered in logs.
The `kafkaIngester`, `tailer` and `envoyAccessLogServer` modules
has new config option `eventIdMetadataKey` to use value of this metadata key as the unique identifier.
(Mostly should be used with a trace id for example).

### Changed
- _Internal:_ Event structs `Raw` and `Slo` has been abstracted into interfaces.

## [v6.9.0] 2021-07-14
### Added
- [#60](https://github.com/seznam/slo-exporter/pull/60) New module eventMetadataRenamer
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/envoy_access_log_server.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,6 @@ Please note that some of the keys may not be present |
address: ":18090"
# gracefulShutdownTimeout for the GRPC server. Please note also the existence of 'maximumGracefulShutdownDuration' global config option which is effectively an upper boundary of here-specified timeout value.
gracefulShutdownTimeout: "5s"
# eventIdMetadataKey it's value will be used as a unique id for the generated event if present.
eventIdMetadataKey: <string>
```
2 changes: 2 additions & 0 deletions docs/modules/kafka_ingester.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ retentionTime: <duration>
# fallbackStartOffset determines from whence the consumer group should begin consuming when it finds a partition without a committed offset.
# Default: FirstOffset
fallbackStartOffset: <LastOffset|FirstOffset>
# eventIdMetadataKey it's value will be used as a unique id for the generated event if present.
eventIdMetadataKey: <string>
```
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/tailer.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ positionPersistenceInterval: "2s"
loglineParseRegexp: '^(?P<ip>[A-Fa-f0-9.:]{4,50}) \S+ \S+ \[(?P<time>.*?)\] "(?P<request>.*?)" (?P<statusCode>\d+) \d+ "(?P<referer>.*?)" uag="(?P<userAgent>[^"]+)" "[^"]+" ua="[^"]+" rt="(?P<requestDuration>\d+(\.\d+)??)".+ignore-slo="(?P<ignoreSloHeader>[^"]*)" slo-domain="(?P<sloDomain>[^"]*)" slo-app="(?P<sloApp>[^"]*)" slo-class="(?P<sloClass>[^"]*)" slo-endpoint="(?P<sloEndpoint>[^"]*)" slo-result="(?P<sloResult>[^"]*)"' # emptyGroupRE defines RE used to decide whether some of the RE match groups specified in loglineParseRegexp is empty and this its assigned variable should be kept unitialized
# Value, that will be treated as empty value.
emptyGroupRE: '^-$'
# eventIdMetadataKey it's value will be used as a unique id for the generated event if present.
eventIdMetadataKey: <string>
```
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/go-kit/kit v0.10.0
github.com/go-test/deep v1.0.6
github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.7.4
github.com/grafana/loki v1.5.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
Expand Down
32 changes: 16 additions & 16 deletions pkg/dynamic_classifier/dynamic_classifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ type DynamicClassifier struct {
unclassifiedEventMetadataKeys []string
eventsMetric *prometheus.CounterVec
observer pipeline.EventProcessingDurationObserver
inputChannel chan *event.Raw
outputChannel chan *event.Raw
inputChannel chan event.Raw
outputChannel chan event.Raw
logger logrus.FieldLogger
done bool
}
Expand Down Expand Up @@ -102,11 +102,11 @@ func (dc *DynamicClassifier) Stop() {
return
}

func (dc *DynamicClassifier) SetInputChannel(channel chan *event.Raw) {
func (dc *DynamicClassifier) SetInputChannel(channel chan event.Raw) {
dc.inputChannel = channel
}

func (dc *DynamicClassifier) OutputChannel() chan *event.Raw {
func (dc *DynamicClassifier) OutputChannel() chan event.Raw {
return dc.outputChannel
}

Expand All @@ -129,8 +129,8 @@ func New(conf classifierConfig, logger logrus.FieldLogger) (*DynamicClassifier,
exactMatches: newMemoryExactMatcher(logger),
regexpMatches: newRegexpMatcher(logger),
unclassifiedEventMetadataKeys: conf.UnclassifiedEventMetadataKeys,
inputChannel: make(chan *event.Raw),
outputChannel: make(chan *event.Raw),
inputChannel: make(chan event.Raw),
outputChannel: make(chan event.Raw),
done: false,
logger: logger,
}
Expand Down Expand Up @@ -235,7 +235,7 @@ func (dc *DynamicClassifier) loadMatchesFromCSV(matcher matcher, path string) er
sloApp := line[1]
sloClass := line[2]
sloEndpoint := line[3]
classification := &event.SloClassification{
classification := event.SloClassification{
Domain: sloDomain,
App: sloApp,
Class: sloClass,
Expand All @@ -251,14 +251,14 @@ func (dc *DynamicClassifier) loadMatchesFromCSV(matcher matcher, path string) er
}

// Classify classifies endpoint by updating its Classification field
func (dc *DynamicClassifier) Classify(newEvent *event.Raw) (bool, error) {
func (dc *DynamicClassifier) Classify(newEvent event.Raw) (bool, error) {
var (
classificationErrors error
classification *event.SloClassification
classification event.SloClassification
classifiedBy matcherType
)
if newEvent.IsClassified() {
if err := dc.exactMatches.set(newEvent.EventKey(), newEvent.SloClassification); err != nil {
if err := dc.exactMatches.set(newEvent.EventKey(), newEvent.SloClassification()); err != nil {
return true, fmt.Errorf("failed to set the exact matcher: %w", err)
}
return true, nil
Expand All @@ -272,20 +272,20 @@ func (dc *DynamicClassifier) Classify(newEvent *event.Raw) (bool, error) {
dc.logger.Errorf("error while classifying event: %+v", err)
classificationErrors = multierror.Append(classificationErrors, err)
}
if classification != nil {
if classification.IsClassified() {
classifiedBy = classifier.getType()
break
}
}

if classification == nil {
dc.reportEvent(unclassifiedEventLabel, string(classifiedBy), newEvent.Metadata)
if !classification.IsClassified() {
dc.reportEvent(unclassifiedEventLabel, string(classifiedBy), newEvent.Metadata())
return false, classificationErrors
}

dc.logger.Debugf("event '%s' matched by %s matcher", newEvent.EventKey(), classifiedBy)
newEvent.UpdateSLOClassification(classification)
dc.reportEvent(classifiedEventLabel, string(classifiedBy), newEvent.Metadata)
newEvent.SetSLOClassification(classification)
dc.reportEvent(classifiedEventLabel, string(classifiedBy), newEvent.Metadata())

// Those matched by regex we want to write to the exact matcher so it is cached
if classifiedBy == regexpMatcherType {
Expand All @@ -311,7 +311,7 @@ func (dc *DynamicClassifier) DumpCSV(w io.Writer, matcherType string) error {
return matcher.dumpCSV(w)
}

func (dc *DynamicClassifier) classifyByMatch(matcher matcher, event *event.Raw) (*event.SloClassification, error) {
func (dc *DynamicClassifier) classifyByMatch(matcher matcher, event event.Raw) (event.SloClassification, error) {
return matcher.get(event.EventKey())
}

Expand Down
75 changes: 33 additions & 42 deletions pkg/dynamic_classifier/dynamic_classifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ package dynamic_classifier
//revive:enable:var-naming

import (
"github.com/seznam/slo-exporter/pkg/event"
"github.com/seznam/slo-exporter/pkg/stringmap"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/seznam/slo-exporter/pkg/event"
"path/filepath"
"reflect"
"regexp"
Expand Down Expand Up @@ -68,17 +69,15 @@ func TestClassificationByExactMatches(t *testing.T) {

data := []struct {
endpoint string
expectedClassification *event.SloClassification
expectedClassification event.SloClassification
expectedOk bool
}{
{"GET:/testing-endpoint", newSloClassification("test-domain", "test-app", "test-class"), true},
{"non-classified-endpoint", nil, false},
{"non-classified-endpoint", event.SloClassification{}, false},
}

for _, ec := range data {
newEvent := &event.Raw{
SloClassification: ec.expectedClassification,
}
newEvent := event.NewRaw("", 1, stringmap.StringMap{}, &ec.expectedClassification)
newEvent.SetEventKey(ec.endpoint)

ok, err := classifier.Classify(newEvent)
Expand All @@ -87,8 +86,8 @@ func TestClassificationByExactMatches(t *testing.T) {
}

assert.Equal(t, ec.expectedOk, ok)
if !reflect.DeepEqual(ec.expectedClassification, newEvent.SloClassification) {
t.Errorf("Classification does not match %+v != %+v", ec.expectedClassification, newEvent.SloClassification)
if !reflect.DeepEqual(ec.expectedClassification, newEvent.SloClassification()) {
t.Errorf("Classification does not match %+v != %+v", ec.expectedClassification, newEvent.SloClassification())
}
}
}
Expand All @@ -101,18 +100,16 @@ func TestClassificationByRegexpMatches(t *testing.T) {

data := []struct {
endpoint string
expectedClassification *event.SloClassification
expectedClassification event.SloClassification
expectedOk bool
}{
{"/api/test/asdf", newSloClassification("test-domain", "test-app", "test-class"), true},
{"/api/asdf", newSloClassification("test-domain", "test-app", "test-class-all"), true},
{"non-classified-endpoint", nil, false},
{"non-classified-endpoint", event.SloClassification{}, false},
}

for _, ec := range data {
newEvent := &event.Raw{
SloClassification: ec.expectedClassification,
}
newEvent := event.NewRaw("", 1, stringmap.StringMap{}, &ec.expectedClassification)
newEvent.SetEventKey(ec.endpoint)

ok, err := classifier.Classify(newEvent)
Expand All @@ -121,21 +118,19 @@ func TestClassificationByRegexpMatches(t *testing.T) {
}

assert.Equal(t, ec.expectedOk, ok)
if !reflect.DeepEqual(ec.expectedClassification, newEvent.SloClassification) {
t.Errorf("Classification does not match %+v != %+v", ec.expectedClassification, newEvent.SloClassification)
if !reflect.DeepEqual(ec.expectedClassification, newEvent.SloClassification()) {
t.Errorf("Classification does not match %+v != %+v", ec.expectedClassification, newEvent.SloClassification())
}
}
}

func Test_DynamicClassifier_Classify_UpdatesEmptyCache(t *testing.T) {
eventKey := "GET:/testing-endpoint"
classifiedEvent := &event.Raw{
SloClassification: &event.SloClassification{
Domain: "domain",
App: "app",
Class: "class",
},
}
classifiedEvent := event.NewRaw("", 1, stringmap.StringMap{}, &event.SloClassification{
Domain: "domain",
App: "app",
Class: "class",
})
classifiedEvent.SetEventKey(eventKey)

// test that classified event updates an empty exact matches cache
Expand All @@ -148,21 +143,19 @@ func Test_DynamicClassifier_Classify_UpdatesEmptyCache(t *testing.T) {
if err != nil {
t.Fatalf("error while getting the tested event key from exact Matches classifier: %v", err)
}
if !reflect.DeepEqual(classifiedEvent.SloClassification, classification) {
t.Errorf("event classification '%+v' did not propagate to classifier exact matches cache: %+v", classifiedEvent.SloClassification, classification)
if !reflect.DeepEqual(classifiedEvent.SloClassification(), classification) {
t.Errorf("event classification '%+v' did not propagate to classifier exact matches cache: %+v", classifiedEvent.SloClassification(), classification)
}
}

// test that classified event updates dynamic classifier cache as initialized from golden file
func Test_DynamicClassifier_Classify_OverridesCacheFromConfig(t *testing.T) {
eventKey := "GET:/testing-endpoint"
classifiedEvent := &event.Raw{
SloClassification: &event.SloClassification{
Domain: "domain",
App: "app",
Class: "class",
},
}
classifiedEvent := event.NewRaw("", 1, stringmap.StringMap{}, &event.SloClassification{
Domain: "domain",
App: "app",
Class: "class",
})
classifiedEvent.SetEventKey(eventKey)

classifier := newClassifier(t, classifierConfig{RegexpMatchesCsvFiles: goldenFile(t)})
Expand All @@ -179,8 +172,8 @@ func Test_DynamicClassifier_Classify_OverridesCacheFromConfig(t *testing.T) {
if err != nil {
t.Fatalf("error while getting the tested event key from exact Matches classifier: %v", err)
}
if !reflect.DeepEqual(classifiedEvent.SloClassification, classification) {
t.Errorf("classifier cache '%+v' for event_key '%s' was not updated with classification from the classified event '%+v'.", classifiedEvent.SloClassification, eventKey, classification)
if !reflect.DeepEqual(classifiedEvent.SloClassification(), classification) {
t.Errorf("classifier cache '%+v' for event_key '%s' was not updated with classification from the classified event '%+v'.", classifiedEvent.SloClassification(), eventKey, classification)
}
}

Expand All @@ -191,13 +184,11 @@ func Test_DynamicClassifier_Classify_OverridesCacheFromPreviousClassifiedEvent(t

classifier := newClassifier(t, classifierConfig{})
for _, eventClass := range eventClasses {
classifiedEvent := &event.Raw{
SloClassification: &event.SloClassification{
Domain: "domain",
App: "app",
Class: eventClass,
},
}
classifiedEvent := event.NewRaw("", 1, stringmap.StringMap{}, &event.SloClassification{
Domain: "domain",
App: "app",
Class: eventClass,
})
classifiedEvent.SetEventKey(eventKey)

ok, err := classifier.Classify(classifiedEvent)
Expand All @@ -208,8 +199,8 @@ func Test_DynamicClassifier_Classify_OverridesCacheFromPreviousClassifiedEvent(t
if err != nil {
t.Fatalf("error while getting the tested event key from exact Matches classifier: %v", err)
}
if !reflect.DeepEqual(classifiedEvent.SloClassification, classification) {
t.Errorf("classifier cache '%+v' for event_key '%s' was not updated with classification from the classified event '%+v'.", classifiedEvent.SloClassification, eventKey, classification)
if !reflect.DeepEqual(classifiedEvent.SloClassification(), classification) {
t.Errorf("classifier cache '%+v' for event_key '%s' was not updated with classification from the classified event '%+v'.", classifiedEvent.SloClassification(), eventKey, classification)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/dynamic_classifier/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type matcherType string

type matcher interface {
getType() matcherType
set(key string, classification *event.SloClassification) error
get(key string) (*event.SloClassification, error)
set(key string, classification event.SloClassification) error
get(key string) (event.SloClassification, error)
dumpCSV(w io.Writer) error
}
12 changes: 6 additions & 6 deletions pkg/dynamic_classifier/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ package dynamic_classifier
import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"github.com/seznam/slo-exporter/pkg/event"
"github.com/sirupsen/logrus"
"io/ioutil"
"path/filepath"
"reflect"
Expand All @@ -17,8 +17,8 @@ import (
"github.com/stretchr/testify/assert"
)

func newSloClassification(domain string, app string, class string) *event.SloClassification {
return &event.SloClassification{
func newSloClassification(domain string, app string, class string) event.SloClassification {
return event.SloClassification{
Domain: domain,
App: app,
Class: class,
Expand All @@ -30,15 +30,15 @@ func TestMatcher(t *testing.T) {
cases := []struct {
matcher matcher
key string
value *event.SloClassification
value event.SloClassification
wantedKey string
wantedValue *event.SloClassification
wantedValue event.SloClassification
setErr string
getErr string
}{
{newMemoryExactMatcher(logger), "test", newSloClassification("test-domain", "test-app", "test-class"), "test", newSloClassification("test-domain", "test-app", "test-class"), "", ""},
{newMemoryExactMatcher(logger), "", newSloClassification("test-domain", "test-app", "test-class"), "", newSloClassification("test-domain", "test-app", "test-class"), "", ""},
{newMemoryExactMatcher(logger), "test", newSloClassification("test-domain", "test-app", "test-class"), "aaa", nil, "", ""},
{newMemoryExactMatcher(logger), "test", newSloClassification("test-domain", "test-app", "test-class"), "aaa", event.SloClassification{}, "", ""},
{newRegexpMatcher(logger), ".*", newSloClassification("test-domain", "test-app", "test-class"), "aaa", newSloClassification("test-domain", "test-app", "test-class"), "", ""},
{newRegexpMatcher(logger), ".*****", newSloClassification("test-domain", "test-app", "test-class"), "aaa", newSloClassification("test-domain", "test-app", "test-class"), "failed to create new regexp endpoint classification: error parsing regexp: invalid nested repetition operator: `**`", ""},
}
Expand Down
Loading

0 comments on commit acf416e

Please sign in to comment.