From 2828cecd771d000991dfb76c6807053bf22355cf Mon Sep 17 00:00:00 2001 From: "A. Stoewer" Date: Mon, 27 May 2024 17:01:48 +1000 Subject: [PATCH] Initialize event attribute values during initialization Allows definition of random attributes with pre-defined cardinality --- examples/template/template.js | 11 +- pkg/tracegen/templated.go | 218 ++++++++++++++++----------------- pkg/tracegen/templated_test.go | 6 +- 3 files changed, 117 insertions(+), 118 deletions(-) diff --git a/examples/template/template.js b/examples/template/template.js index be6563f..d846c20 100644 --- a/examples/template/template.js +++ b/examples/template/template.js @@ -44,6 +44,7 @@ const traceTemplates = [ defaults: { attributes: {"numbers": ["one", "two", "three"]}, attributeSemantics: tracing.SEMANTICS_HTTP, + randomEvents: {count: 2, randomAttributes: {count: 3, cardinality: 10}}, }, spans: [ {service: "shop-backend", name: "article-to-cart", duration: {min: 400, max: 1200}}, @@ -63,17 +64,17 @@ const traceTemplates = [ spans: [ {service: "shop-backend", attributes: {"http.status_code": 403}}, {service: "shop-backend", name: "authenticate", attributes: {"http.request.header.accept": ["application/json"]}}, - {service: "auth-service", name: "authenticate", attributes: {"http.status_code": 403}}, + {service: "auth-service", name: "authenticate", attributes: {"http.status_code": 403}, randomEvents: {count: 0.5, exceptionCount: 2, randomAttributes: {count: 5, cardinality: 5}}}, ] }, { defaults: traceDefaults, spans: [ - {service: "shop-backend", attributes: {"http.status_code": 403}}, + {service: "shop-backend"}, {service: "shop-backend", name: "authenticate", attributes: {"http.request.header.accept": ["application/json"]}}, - {service: "auth-service", name: "authenticate", attributes: {"http.status_code": 403}}, - {service: "cart-service", name: "checkout", randomEvents: {exceptionRate: 1, rate: 2, randomAttributes: {count: 5, cardinality: 2}}}, - {service: "billing-service", name: "payment", randomLinks: {count: 0.5, randomAttributes: {count: 3, cardinality: 10}}} + {service: "auth-service", name: "authenticate"}, + {service: "cart-service", name: "checkout", randomEvents: {count: 0.5, exceptionCount: 2, exceptionOnError: true, randomAttributes: {count: 5, cardinality: 5}}}, + {service: "billing-service", name: "payment", randomLinks: {count: 0.5, randomAttributes: {count: 3, cardinality: 10}}, randomEvents: {exceptionOnError: true, randomAttributes: {count: 4}}} ] }, ] diff --git a/pkg/tracegen/templated.go b/pkg/tracegen/templated.go index be3d148..134d793 100644 --- a/pkg/tracegen/templated.go +++ b/pkg/tracegen/templated.go @@ -2,7 +2,6 @@ package tracegen import ( "fmt" - "math/rand" "net/http" "net/url" "strings" @@ -52,7 +51,7 @@ type SpanDefaults struct { // RandomAttributes random attributes generated for each span. RandomAttributes *AttributeParams `js:"randomAttributes"` // Random events generated for each span - RandomEvents EventParams `js:"randomEvents"` + RandomEvents *EventParams `js:"randomEvents"` // Random links generated for each span RandomLinks *LinkParams `js:"randomLinks"` } @@ -82,7 +81,7 @@ type SpanTemplate struct { // List of links for the span with specific parameters Links []Link `js:"links"` // Generate random events for the span - RandomEvents EventParams `js:"randomEvents"` + RandomEvents *EventParams `js:"randomEvents"` // Generate random links for the span RandomLinks *LinkParams `js:"randomLinks"` } @@ -119,12 +118,12 @@ type LinkParams struct { } type EventParams struct { - // Generate exception event if status code of the span is >= 400 - GenerateExceptionOnError bool `js:"generateExceptionOnError"` - // Count of exception events per each span - ExceptionRate float32 `js:"exceptionRate"` // Count of random events per each span Count float32 `js:"count"` + // ExceptionCount indicates how many exception events to add to the span + ExceptionCount float32 `js:"exceptionCount"` + // ExceptionOnError generates exceptions if status code of the span is >= 400 + ExceptionOnError bool `js:"exceptionOnError"` // Generate random attributes for this event RandomAttributes *AttributeParams `js:"randomAttributes"` } @@ -149,18 +148,17 @@ type TemplatedGenerator struct { } type internalSpanTemplate struct { - idx int - resource *internalResourceTemplate - parent *internalSpanTemplate - name string - kind ptrace.SpanKind - duration *Range - attributeSemantics *OTelSemantics - attributes map[string]interface{} - randomAttributes map[string][]interface{} - events []Event - links []internalLinkTemplate - generateExceptionEvents bool + idx int + resource *internalResourceTemplate + parent *internalSpanTemplate + name string + kind ptrace.SpanKind + duration *Range + attributeSemantics *OTelSemantics + attributes map[string]interface{} + randomAttributes map[string][]interface{} + events []internalEventTemplate + links []internalLinkTemplate } type internalResourceTemplate struct { @@ -178,9 +176,11 @@ type internalLinkTemplate struct { } type internalEventTemplate struct { - count int - exceptionCount int - randomAttributes *AttributeParams + rate float32 + exceptionOnError bool + name string + attributes map[string]interface{} + randomAttributes map[string][]interface{} } // Traces implements Generator for TemplatedGenerator @@ -288,40 +288,35 @@ func (g *TemplatedGenerator) generateSpan(scopeSpans ptrace.ScopeSpans, tmpl *in } } - // events + // generate events + var hasError bool + if st, found := span.Attributes().Get("http.status_code"); found { + hasError = st.Int() >= 400 + } else if st, found = span.Attributes().Get("http.response.status_code"); found { + hasError = st.Int() >= 400 + } span.Events().EnsureCapacity(len(tmpl.events)) for _, e := range tmpl.events { - event := span.Events().AppendEmpty() - event.SetName(e.Name) - event.SetTimestamp(pcommon.NewTimestampFromTime(end)) - for k, v := range e.Attributes { - _ = event.Attributes().PutEmpty(k).FromRaw(v) + if e.rate > 0 && random.Rand().Float32() > e.rate { + continue } - } - - if tmpl.generateExceptionEvents { - var status int64 - parentAttr := pcommon.NewMap() - if parent != nil { - parentAttr = parent.Attributes() + if e.exceptionOnError && !hasError { + continue } - if st, found := span.Attributes().Get("http.status_code"); found { - status = st.Int() - } else if st, found = parentAttr.Get("http.status_code"); found { - status = st.Int() - } else { - status = random.HTTPStatusSuccess() - span.Attributes().PutInt("http.status_code", status) + + event := span.Events().AppendEmpty() + event.Attributes().EnsureCapacity(len(e.attributes) + len(e.randomAttributes)) + + event.SetName(e.name) + eventTime := start.Add(random.Duration(0, duration)) + event.SetTimestamp(pcommon.NewTimestampFromTime(eventTime)) + + for k, v := range e.attributes { + _ = event.Attributes().PutEmpty(k).FromRaw(v) } - if status >= 400 { - exceptionEvent := generateExceptionEvent() - event := span.Events().AppendEmpty() - event.SetName(exceptionEvent.Name) - event.SetTimestamp(pcommon.NewTimestampFromTime(end)) - for k, v := range exceptionEvent.Attributes { - _ = event.Attributes().PutEmpty(k).FromRaw(v) - } + for k, v := range e.randomAttributes { + _ = event.Attributes().PutEmpty(k).FromRaw(random.SelectElement(v)) } } @@ -533,45 +528,6 @@ func (g *TemplatedGenerator) initializeSpan(idx int, parent *internalSpanTemplat } span.attributes = util.MergeMaps(defaults.Attributes, tmpl.Attributes) - eventDefaultsRate := defaults.RandomEvents.Count - var eventDefaults internalEventTemplate - // if rate is more than 1, use whole integers - if eventDefaultsRate > 1 { - eventDefaults = internalEventTemplate{ - randomAttributes: defaults.RandomEvents.RandomAttributes, - count: int(eventDefaultsRate), - exceptionCount: int(defaults.RandomEvents.ExceptionRate), - } - } else { - var count, exeptionCount int - if rand.Float32() < eventDefaultsRate { - count = 1 - } - - if rand.Float32() < eventDefaultsRate { - exeptionCount = 1 - } - - // if rate is less than one - eventDefaults = internalEventTemplate{ - randomAttributes: defaults.RandomEvents.RandomAttributes, - count: count, - exceptionCount: exeptionCount, - } - } - - randomEvents := internalEventTemplate{ - randomAttributes: tmpl.RandomEvents.RandomAttributes, - count: int(tmpl.RandomEvents.Count), - exceptionCount: int(tmpl.RandomEvents.ExceptionRate), - } - - // generate all non-exception events - span.events = g.initializeEvents(tmpl.Events, randomEvents, eventDefaults) - - // need span status to determine if an exception event should occur - span.generateExceptionEvents = defaults.RandomEvents.GenerateExceptionOnError - // set span name if tmpl.Name != nil { span.name = *tmpl.Name @@ -590,6 +546,9 @@ func (g *TemplatedGenerator) initializeSpan(idx int, parent *internalSpanTemplat // initialize links for span span.links = g.initializeLinks(tmpl.Links, tmpl.RandomLinks, defaults.RandomLinks) + // initialize events for the span + span.events = g.initializeEvents(tmpl.Events, tmpl.RandomEvents, defaults.RandomEvents) + return &span, nil } @@ -670,36 +629,75 @@ func initializeRandomAttributes(attributeParams *AttributeParams) map[string][]i return attributes } -func (g *TemplatedGenerator) initializeEvents(tmplEvents []Event, randomEvents internalEventTemplate, eventDefaults internalEventTemplate) []Event { - count := len(tmplEvents) + randomEvents.count + eventDefaults.count - - if count == 0 { - return []Event{} - } - - events := make([]Event, 0, count) - +func (g *TemplatedGenerator) initializeEvents(tmplEvents []Event, randomEvents, defaultRandomEvents *EventParams) []internalEventTemplate { + internalEvents := make([]internalEventTemplate, 0, len(tmplEvents)) for _, e := range tmplEvents { - event := generateEvent(e.Name, e.Attributes, e.RandomAttributes) - events = append(events, event) + event := internalEventTemplate{ + name: e.Name, + attributes: e.Attributes, + randomAttributes: initializeRandomAttributes(e.RandomAttributes), + } + internalEvents = append(internalEvents, event) } - for i := 0; i < randomEvents.exceptionCount; i++ { - event := generateExceptionEvent() - events = append(events, event) + if randomEvents == nil { + if defaultRandomEvents == nil { + return internalEvents + } + randomEvents = defaultRandomEvents } - for i := 0; i < randomEvents.count; i++ { - event := generateEvent("", nil, randomEvents.randomAttributes) - events = append(events, event) + // normal random events + if randomEvents.Count == 0 { // default count is 1 + randomEvents.Count = 1 + } + if randomEvents.Count < 1 { + event := internalEventTemplate{ + rate: randomEvents.Count, + name: "event_" + random.K6String(10), + randomAttributes: initializeRandomAttributes(randomEvents.RandomAttributes), + } + internalEvents = append(internalEvents, event) + } else { + for i := 0; i < int(randomEvents.Count); i++ { + event := internalEventTemplate{ + name: "event_" + random.K6String(10), + randomAttributes: initializeRandomAttributes(randomEvents.RandomAttributes), + } + internalEvents = append(internalEvents, event) + } } - for i := 0; i < eventDefaults.count; i++ { - event := generateEvent("", nil, eventDefaults.randomAttributes) - events = append(events, event) + // random exception events + if randomEvents.ExceptionCount == 0 && randomEvents.ExceptionOnError { + randomEvents.ExceptionCount = 1 // default exception count is 1, if ExceptionOnError is true + } + if randomEvents.ExceptionCount < 1 { + event := internalEventTemplate{ + rate: randomEvents.Count, + name: "event_" + random.K6String(10), + randomAttributes: initializeRandomAttributes(randomEvents.RandomAttributes), + exceptionOnError: randomEvents.ExceptionOnError, + } + internalEvents = append(internalEvents, event) + } else { + for i := 0; i < int(randomEvents.ExceptionCount); i++ { + event := internalEventTemplate{ + name: "exception", + attributes: map[string]interface{}{ + "exception.escape": false, + "exception.message": generateRandomExceptionMsg(), + "exception.stacktrace": generateRandomExceptionStackTrace(), + "exception.type": random.K6String(10) + ".error", + }, + randomAttributes: initializeRandomAttributes(randomEvents.RandomAttributes), + exceptionOnError: randomEvents.ExceptionOnError, + } + internalEvents = append(internalEvents, event) + } } - return events + return internalEvents } func generateExceptionEvent() Event { diff --git a/pkg/tracegen/templated_test.go b/pkg/tracegen/templated_test.go index 97e4e20..23608bf 100644 --- a/pkg/tracegen/templated_test.go +++ b/pkg/tracegen/templated_test.go @@ -59,14 +59,14 @@ func TestTemplatedGenerator_EventsLinks(t *testing.T) { Attributes: map[string]interface{}{"fixed.attr": "some-value"}, RandomAttributes: &AttributeParams{Count: 3}, RandomLinks: &LinkParams{Count: 0.5, RandomAttributes: &AttributeParams{Count: 3}}, - RandomEvents: EventParams{GenerateExceptionOnError: true, Count: 0.5, RandomAttributes: &AttributeParams{Count: 3}}, + RandomEvents: &EventParams{ExceptionOnError: true, Count: 0.5, RandomAttributes: &AttributeParams{Count: 3}}, }, Spans: []SpanTemplate{ // do not change order of the first one {Service: "test-service", Name: ptr("only_default")}, {Service: "test-service", Name: ptr("default_and_template"), Events: []Event{{Name: "event-name", RandomAttributes: &AttributeParams{Count: 2}}}, Links: []Link{{Attributes: map[string]interface{}{"link-attr-key": "link-attr-value"}}}}, - {Service: "test-service", Name: ptr("default_and_random"), RandomEvents: EventParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}, RandomLinks: &LinkParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}}, - {Service: "test-service", Name: ptr("default_template_random"), Events: []Event{{Name: "event-name", RandomAttributes: &AttributeParams{Count: 2}}}, Links: []Link{{Attributes: map[string]interface{}{"link-attr-key": "link-attr-value"}}}, RandomEvents: EventParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}, RandomLinks: &LinkParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}}, + {Service: "test-service", Name: ptr("default_and_random"), RandomEvents: &EventParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}, RandomLinks: &LinkParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}}, + {Service: "test-service", Name: ptr("default_template_random"), Events: []Event{{Name: "event-name", RandomAttributes: &AttributeParams{Count: 2}}}, Links: []Link{{Attributes: map[string]interface{}{"link-attr-key": "link-attr-value"}}}, RandomEvents: &EventParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}, RandomLinks: &LinkParams{Count: 2, RandomAttributes: &AttributeParams{Count: 1}}}, {Service: "test-service", Name: ptr("default_generate_on_error"), Attributes: map[string]interface{}{"http.status_code": 400}}, }, }