Skip to content

Commit

Permalink
match spans on instrumentation library
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Jul 28, 2020
1 parent 239fca8 commit 81d8a12
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 33 deletions.
15 changes: 15 additions & 0 deletions internal/processor/filterspan/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ type MatchProperties struct {
// This is an optional field.
Resources []Attribute `mapstructure:"resource"`

Libraries []InstrumentationLibrary `mapstructure:"libraries"`

// SpanNames specify the list of items to match span name against.
// A match occurs if the span name matches at least one item in this list.
// This is an optional field.
Expand All @@ -102,3 +104,16 @@ type Attribute struct {
// If it is not set, any value will match.
Value interface{} `mapstructure:"value"`
}

type InstrumentationLibrary struct {
Name string `mapstructure:"name"`
// version match
// expected actual match
// nil <blank> yes
// nil 1 yes
// <blank> <blank> yes
// <blank> 1 no
// 1 <blank> no
// 1 1 yes
Version *string `mapstructure:"version"`
}
52 changes: 44 additions & 8 deletions internal/processor/filterspan/filterspan.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
// TODO Add processor type invoking the NewMatcher in error text.
errAtLeastOneMatchFieldNeeded = errors.New(
`error creating processor. At least one ` +
`of "services", "span_names" or "attributes" field must be specified"`)
`of "services", "span_names", "libraries", "attributes" or "resources" field must be specified"`)

errUnexpectedAttributeType = errors.New("unexpected attribute type")
)
Expand All @@ -39,7 +39,7 @@ var (
// Matcher is an interface that allows matching a span against a configuration
// of a match.
type Matcher interface {
MatchSpan(span pdata.Span, resource pdata.Resource) bool
MatchSpan(span pdata.Span, resource pdata.Resource, library pdata.InstrumentationLibrary) bool
}

// propertiesMatcher allows matching a span against various span properties.
Expand All @@ -50,12 +50,20 @@ type propertiesMatcher struct {
// Span names to compare to.
nameFilters filterset.FilterSet

// Instrumentation libraries to compare against
Libraries []instrumentationLibraryMatcher

// The attribute values are stored in the internal format.
Attributes attributesMatcher

Resources attributesMatcher
}

type instrumentationLibraryMatcher struct {
Name filterset.FilterSet
Version filterset.FilterSet
}

type attributesMatcher []attributeMatcher

// attributeMatcher is a attribute key/value pair to match to.
Expand All @@ -72,12 +80,30 @@ func NewMatcher(mp *MatchProperties) (Matcher, error) {
return nil, nil
}

if len(mp.Services) == 0 && len(mp.SpanNames) == 0 && len(mp.Attributes) == 0 && len(mp.Resources) == 0 {
if len(mp.Libraries) == 0 && len(mp.Services) == 0 && len(mp.SpanNames) == 0 && len(mp.Attributes) == 0 && len(mp.Resources) == 0 {
return nil, errAtLeastOneMatchFieldNeeded
}

var err error
var lm []instrumentationLibraryMatcher
for _, library := range mp.Libraries {
name, err := filterset.CreateFilterSet([]string{library.Name}, &mp.Config)
if err != nil {
return nil, fmt.Errorf("error creating library name filters: %v", err)
}

var version filterset.FilterSet
if library.Version != nil {
filter, err := filterset.CreateFilterSet([]string{*library.Version}, &mp.Config)
if err != nil {
return nil, fmt.Errorf("error creating library version filters: %v", err)
}
version = filter
}

lm = append(lm, instrumentationLibraryMatcher{Name: name, Version: version})
}

var err error
var am attributesMatcher
if len(mp.Attributes) > 0 {
am, err = newAttributesMatcher(mp.Config, mp.Attributes)
Expand Down Expand Up @@ -113,6 +139,7 @@ func NewMatcher(mp *MatchProperties) (Matcher, error) {
return &propertiesMatcher{
serviceFilters: serviceFS,
nameFilters: nameFS,
Libraries: lm,
Attributes: am,
Resources: rm,
}, nil
Expand Down Expand Up @@ -165,17 +192,17 @@ func newAttributesMatcher(config filterset.Config, attributes []Attribute) (attr
// The logic determining if a span should be processed is set
// in the attribute configuration with the include and exclude settings.
// Include properties are checked before exclude settings are checked.
func SkipSpan(include Matcher, exclude Matcher, span pdata.Span, resource pdata.Resource) bool {
func SkipSpan(include Matcher, exclude Matcher, span pdata.Span, resource pdata.Resource, library pdata.InstrumentationLibrary) bool {
if include != nil {
// A false returned in this case means the span should not be processed.
if i := include.MatchSpan(span, resource); !i {
if i := include.MatchSpan(span, resource, library); !i {
return true
}
}

if exclude != nil {
// A true returned in this case means the span should not be processed.
if e := exclude.MatchSpan(span, resource); e {
if e := exclude.MatchSpan(span, resource, library); e {
return true
}
}
Expand All @@ -190,7 +217,7 @@ func SkipSpan(include Matcher, exclude Matcher, span pdata.Span, resource pdata.
// At least one of services, span names or attributes must be specified. It is supported
// to have more than one of these specified, and all specified must evaluate
// to true for a match to occur.
func (mp *propertiesMatcher) MatchSpan(span pdata.Span, resource pdata.Resource) bool {
func (mp *propertiesMatcher) MatchSpan(span pdata.Span, resource pdata.Resource, library pdata.InstrumentationLibrary) bool {
// If a set of properties was not in the mp, all spans are considered to match on that property
if mp.serviceFilters != nil {
serviceName := serviceNameForResource(resource)
Expand All @@ -203,6 +230,15 @@ func (mp *propertiesMatcher) MatchSpan(span pdata.Span, resource pdata.Resource)
return false
}

for _, matcher := range mp.Libraries {
if !matcher.Name.Matches(library.Name()) {
return false
}
if matcher.Version != nil && !matcher.Version.Matches(library.Version()) {
return false
}
}

if mp.Resources != nil && !mp.Resources.match(span) {
return false
}
Expand Down
102 changes: 79 additions & 23 deletions internal/processor/filterspan/filterspan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func createConfig(matchType filterset.MatchType) *filterset.Config {
}

func TestSpan_validateMatchesConfiguration_InvalidConfig(t *testing.T) {
version := "["
testcases := []struct {
name string
property MatchProperties
Expand Down Expand Up @@ -86,23 +87,23 @@ func TestSpan_validateMatchesConfiguration_InvalidConfig(t *testing.T) {
errorString: `error creating attribute filters: error unsupported value type "[]string"`,
},
{
name: "invalid_regexp_pattern",
name: "invalid_regexp_pattern_service",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
Services: []string{"["},
},
errorString: "error creating service name filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "invalid_regexp_pattern2",
name: "invalid_regexp_pattern_span",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
SpanNames: []string{"["},
},
errorString: "error creating span name filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "invalid_regexp_pattern3",
name: "invalid_regexp_pattern_attribute",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
SpanNames: []string{"["},
Expand All @@ -111,13 +112,29 @@ func TestSpan_validateMatchesConfiguration_InvalidConfig(t *testing.T) {
errorString: "error creating attribute filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "invalid_regexp_pattern4",
name: "invalid_regexp_pattern_resource",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
Resources: []Attribute{{Key: "key", Value: "["}},
},
errorString: "error creating resource filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "invalid_regexp_pattern_library_name",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
Libraries: []InstrumentationLibrary{{Name: "["}},
},
errorString: "error creating library name filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "invalid_regexp_pattern_library_version",
property: MatchProperties{
Config: *createConfig(filterset.Regexp),
Libraries: []InstrumentationLibrary{{Name: "lib", Version: &version}},
},
errorString: "error creating library version filters: error parsing regexp: missing closing ]: `[`",
},
{
name: "empty_key_name_in_attributes_list",
property: MatchProperties{
Expand All @@ -142,6 +159,7 @@ func TestSpan_validateMatchesConfiguration_InvalidConfig(t *testing.T) {
}

func TestSpan_Matching_False(t *testing.T) {
version := "wrong"
testcases := []struct {
name string
properties *MatchProperties
Expand Down Expand Up @@ -186,6 +204,23 @@ func TestSpan_Matching_False(t *testing.T) {
},
},

{
name: "wrong_library_name",
properties: &MatchProperties{
Config: *createConfig(filterset.Strict),
Services: []string{},
Libraries: []InstrumentationLibrary{{Name: "wrong"}},
},
},
{
name: "wrong_library_version",
properties: &MatchProperties{
Config: *createConfig(filterset.Strict),
Services: []string{},
Libraries: []InstrumentationLibrary{{Name: "lib", Version: &version}},
},
},

{
name: "wrong_attribute_value",
properties: &MatchProperties{
Expand Down Expand Up @@ -260,13 +295,19 @@ func TestSpan_Matching_False(t *testing.T) {
"keyInt": pdata.NewAttributeValueInt(123),
"keyMap": pdata.NewAttributeValueMap(),
})

library := pdata.NewInstrumentationLibrary()
library.InitEmpty()
library.SetName("lib")
library.SetVersion("ver")

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
matcher, err := NewMatcher(tc.properties)
assert.Nil(t, err)
assert.NotNil(t, matcher)

assert.False(t, matcher.MatchSpan(span, resource("wrongSvc")))
assert.False(t, matcher.MatchSpan(span, resource("wrongSvc"), library))
})
}
}
Expand Down Expand Up @@ -296,7 +337,7 @@ func TestSpan_MatchingCornerCases(t *testing.T) {

emptySpan := pdata.NewSpan()
emptySpan.InitEmpty()
assert.False(t, mp.MatchSpan(emptySpan, resource("svcA")))
assert.False(t, mp.MatchSpan(emptySpan, resource("svcA"), pdata.NewInstrumentationLibrary()))
}

func TestSpan_MissingServiceName(t *testing.T) {
Expand All @@ -311,36 +352,49 @@ func TestSpan_MissingServiceName(t *testing.T) {

emptySpan := pdata.NewSpan()
emptySpan.InitEmpty()
assert.False(t, mp.MatchSpan(emptySpan, resource("")))
assert.False(t, mp.MatchSpan(emptySpan, resource(""), pdata.NewInstrumentationLibrary()))
}

func TestSpan_Matching_True(t *testing.T) {
ver := "v.*"

testcases := []struct {
name string
properties *MatchProperties
}{
{
name: "service_name_match_regexp",
properties: &MatchProperties{
Config: *createConfig(filterset.Regexp),
Services: []string{"svcA"},
Attributes: []Attribute{},
Config: *createConfig(filterset.Regexp),
Services: []string{"svcA"},
},
},
{
name: "service_name_match_strict",
properties: &MatchProperties{
Config: *createConfig(filterset.Strict),
Services: []string{"svcA"},
Attributes: []Attribute{},
Config: *createConfig(filterset.Strict),
Services: []string{"svcA"},
},
},
{
name: "library_match",
properties: &MatchProperties{
Config: *createConfig(filterset.Regexp),
Libraries: []InstrumentationLibrary{{Name: "li.*"}},
},
},
{
name: "library_match_with_version",
properties: &MatchProperties{
Config: *createConfig(filterset.Regexp),
Libraries: []InstrumentationLibrary{{Name: "li.*", Version: &ver}},
},
},
{
name: "span_name_match",
properties: &MatchProperties{
Config: *createConfig(filterset.Regexp),
SpanNames: []string{"span.*"},
Attributes: []Attribute{},
Config: *createConfig(filterset.Regexp),
SpanNames: []string{"span.*"},
},
},
{
Expand All @@ -353,14 +407,12 @@ func TestSpan_Matching_True(t *testing.T) {
"yet another?pattern",
"regularstring",
},
Attributes: []Attribute{},
},
},
{
name: "attribute_exact_value_match",
properties: &MatchProperties{
Config: *createConfig(filterset.Strict),
Services: []string{},
Config: *createConfig(filterset.Strict),
Attributes: []Attribute{
{
Key: "keyString",
Expand Down Expand Up @@ -408,8 +460,7 @@ func TestSpan_Matching_True(t *testing.T) {
{
name: "resource_exact_value_match",
properties: &MatchProperties{
Config: *createConfig(filterset.Strict),
Services: []string{},
Config: *createConfig(filterset.Strict),
Resources: []Attribute{
{
Key: "keyString",
Expand Down Expand Up @@ -460,6 +511,7 @@ func TestSpan_Matching_True(t *testing.T) {
"keyBool": pdata.NewAttributeValueBool(true),
"keyExists": pdata.NewAttributeValueString("present"),
})
assert.NotNil(t, span)

resource := pdata.NewResource()
resource.InitEmpty()
Expand All @@ -468,14 +520,18 @@ func TestSpan_Matching_True(t *testing.T) {
"keyString": pdata.NewAttributeValueString("arithmetic"),
})

library := pdata.NewInstrumentationLibrary()
library.InitEmpty()
library.SetName("lib")
library.SetVersion("ver")

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mp, err := NewMatcher(tc.properties)
assert.Nil(t, err)
assert.NotNil(t, mp)

assert.NotNil(t, span)
assert.True(t, mp.MatchSpan(span, resource))
assert.True(t, mp.MatchSpan(span, resource, library))
})
}
}
Expand Down
Loading

0 comments on commit 81d8a12

Please sign in to comment.