Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow non-string values to be used as tags in event-to-tag processor #591

Merged
merged 3 commits into from
Jan 26, 2025
Merged
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
45 changes: 40 additions & 5 deletions pkg/formatters/event_to_tag/event_to_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package event_to_tag

import (
"encoding/json"
"fmt"
"io"
"log"
"os"
Expand Down Expand Up @@ -87,13 +88,18 @@ func (t *toTag) Apply(es ...*formatters.EventMsg) []*formatters.EventMsg {
if e == nil {
continue
}
if e.Tags == nil {
e.Tags = make(map[string]string)
}
for k, v := range e.Values {
for _, re := range t.valueNames {
if re.MatchString(k) {
if e.Tags == nil {
e.Tags = make(map[string]string)
switch v := v.(type) {
case string:
e.Tags[k] = v
default:
e.Tags[k] = fmt.Sprint(v)
}
e.Tags[k] = v.(string)
if !t.Keep {
delete(e.Values, k)
}
Expand All @@ -102,9 +108,38 @@ func (t *toTag) Apply(es ...*formatters.EventMsg) []*formatters.EventMsg {
for _, re := range t.values {
if vs, ok := v.(string); ok {
if re.MatchString(vs) {
if e.Tags == nil {
e.Tags = make(map[string]string)
e.Tags[k] = vs
if !t.Keep {
delete(e.Values, k)
}
}
}
}
}
}
return es
}

func (t *toTag) Apply2(es ...*formatters.EventMsg) []*formatters.EventMsg {
for _, e := range es {
if e == nil {
continue
}
if e.Tags == nil {
e.Tags = make(map[string]string)
}
for k, v := range e.Values {
for _, re := range t.valueNames {
if re.MatchString(k) {
e.Tags[k] = fmt.Sprint(v) // always cast v results on extra allocations: Apply > Apply2
if !t.Keep {
delete(e.Values, k)
}
}
}
for _, re := range t.values {
if vs, ok := v.(string); ok {
if re.MatchString(vs) {
e.Tags[k] = vs
if !t.Keep {
delete(e.Values, k)
Expand Down
113 changes: 112 additions & 1 deletion pkg/formatters/event_to_tag/event_to_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
package event_to_tag

import (
"fmt"
"reflect"
"regexp"
"testing"

"github.com/openconfig/gnmic/pkg/formatters"
Expand Down Expand Up @@ -42,6 +44,7 @@ var testset = map[string]struct {
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{},
Values: map[string]interface{}{}},
},
},
Expand Down Expand Up @@ -76,6 +79,7 @@ var testset = map[string]struct {
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{},
Values: map[string]interface{}{}},
},
},
Expand Down Expand Up @@ -109,6 +113,7 @@ var testset = map[string]struct {
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{},
Values: map[string]interface{}{}},
},
},
Expand Down Expand Up @@ -148,6 +153,7 @@ var testset = map[string]struct {
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{},
Values: map[string]interface{}{}},
},
},
Expand All @@ -171,6 +177,51 @@ var testset = map[string]struct {
},
},
},
"match_integer_value": {
processorType: processorType,
processor: map[string]interface{}{
"value-names": []string{".*peer-as$"},
"keep": true,
},
tests: []item{
{
input: nil,
output: nil,
},
{
input: []*formatters.EventMsg{
{
Values: map[string]interface{}{}},
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{},
Values: map[string]interface{}{}},
},
},
{
input: []*formatters.EventMsg{
{
Values: map[string]interface{}{
"name": "dummy",
"peer-as": 65000,
},
},
},
output: []*formatters.EventMsg{
{
Tags: map[string]string{
"peer-as": "65000",
},
Values: map[string]interface{}{
"name": "dummy",
"peer-as": 65000,
},
},
},
},
},
},
}

func TestEventToTag(t *testing.T) {
Expand All @@ -185,7 +236,7 @@ func TestEventToTag(t *testing.T) {
}
t.Logf("processor: %+v", p)
for i, item := range ts.tests {
t.Run("uint_convert", func(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Logf("running test item %d", i)
outs := p.Apply(item.input...)
for j := range outs {
Expand All @@ -203,3 +254,63 @@ func TestEventToTag(t *testing.T) {
}
}
}

// Helper function to generate test messages
func generateTestMessages(count int) []*formatters.EventMsg {
messages := make([]*formatters.EventMsg, count)
for i := 0; i < count; i++ {
messages[i] = &formatters.EventMsg{
Name: fmt.Sprintf("event%d", i),
Timestamp: int64(i),
Values: map[string]interface{}{
fmt.Sprintf("key%d", i): fmt.Sprintf("value%d", i),
"staticKey": "staticValue",
fmt.Sprintf("tagw%d", i): fmt.Sprintf("value%d", i),
},
}
}
return messages
}

// Benchmark test for the Apply function
func BenchmarkApply(b *testing.B) {
// Create a toTag instance with sample regex patterns
toTagInstance := &toTag{
valueNames: []*regexp.Regexp{
regexp.MustCompile(`^key\d+$`), // Matches keys like "key1", "key2", etc.
},
values: []*regexp.Regexp{
regexp.MustCompile(`^value\d+$`), // Matches values like "value1", "value2", etc.
},
Keep: false,
}

// Generate a sample EventMsg array
eventMessages := generateTestMessages(10000)
// Benchmark the Apply function
b.ResetTimer()
for i := 0; i < b.N; i++ {
toTagInstance.Apply(eventMessages...)
}
}

func BenchmarkApply2(b *testing.B) {
// Create a toTag instance with sample regex patterns
toTagInstance := &toTag{
valueNames: []*regexp.Regexp{
regexp.MustCompile(`^key\d+$`), // Matches keys like "key1", "key2", etc.
},
values: []*regexp.Regexp{
regexp.MustCompile(`^value\d+$`), // Matches values like "value1", "value2", etc.
},
Keep: false,
}

// Generate a sample EventMsg array
eventMessages := generateTestMessages(10000)
// Benchmark the Apply function
b.ResetTimer()
for i := 0; i < b.N; i++ {
toTagInstance.Apply2(eventMessages...)
}
}