Skip to content

Commit

Permalink
[enrichments] Add processor.event for span events (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahsivjar authored Aug 19, 2024
1 parent 312322d commit 1df408d
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 22 deletions.
20 changes: 20 additions & 0 deletions enrichments/trace/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Config struct {
Scope ScopeConfig `mapstructure:"scope"`
Transaction ElasticTransactionConfig `mapstructure:"elastic_transaction"`
Span ElasticSpanConfig `mapstructure:"elastic_span"`
SpanEvent SpanEventConfig `mapstructure:"span_event"`
}

// ResourceConfig configures the enrichment of resource attributes.
Expand All @@ -40,6 +41,9 @@ type ScopeConfig struct {
// ElasticTransactionConfig configures the enrichment attributes for the
// spans which are identified as elastic transaction.
type ElasticTransactionConfig struct {
// TimestampUs is a temporary attribute to enable higher
// resolution timestamps in Elasticsearch. For more details see:
// https://github.com/elastic/opentelemetry-dev/issues/374.
TimestampUs AttributeConfig `mapstructure:"timestamp_us"`
Sampled AttributeConfig `mapstructure:"sampled"`
ID AttributeConfig `mapstructure:"id"`
Expand All @@ -56,6 +60,9 @@ type ElasticTransactionConfig struct {
// ElasticSpanConfig configures the enrichment attributes for the spans
// which are NOT identified as elastic transaction.
type ElasticSpanConfig struct {
// TimestampUs is a temporary attribute to enable higher
// resolution timestamps in Elasticsearch. For more details see:
// https://github.com/elastic/opentelemetry-dev/issues/374.
TimestampUs AttributeConfig `mapstructure:"timestamp_us"`
Name AttributeConfig `mapstructure:"name"`
ProcessorEvent AttributeConfig `mapstructure:"processor_event"`
Expand All @@ -67,6 +74,15 @@ type ElasticSpanConfig struct {
DestinationService AttributeConfig `mapstructure:"destination_service"`
}

// SpanEventConfig configures enrichment attributes for the span events.
type SpanEventConfig struct {
// TimestampUs is a temporary attribute to enable higher
// resolution timestamps in Elasticsearch. For more details see:
// https://github.com/elastic/opentelemetry-dev/issues/374.
TimestampUs AttributeConfig `mapstructure:"timestamp_us"`
ProcessorEvent AttributeConfig `mapstructure:"processor_event"`
}

// AttributeConfig is the configuration options for each attribute.
type AttributeConfig struct {
Enabled bool `mapstructure:"enabled"`
Expand Down Expand Up @@ -107,5 +123,9 @@ func Enabled() Config {
DestinationService: AttributeConfig{Enabled: true},
RepresentativeCount: AttributeConfig{Enabled: true},
},
SpanEvent: SpanEventConfig{
TimestampUs: AttributeConfig{Enabled: true},
ProcessorEvent: AttributeConfig{Enabled: true},
},
}
}
1 change: 1 addition & 0 deletions enrichments/trace/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestEnabled(t *testing.T) {
assertAllEnabled(t, reflect.ValueOf(config.Scope))
assertAllEnabled(t, reflect.ValueOf(config.Transaction))
assertAllEnabled(t, reflect.ValueOf(config.Span))
assertAllEnabled(t, reflect.ValueOf(config.SpanEvent))
}

func assertAllEnabled(t *testing.T, cfg reflect.Value) {
Expand Down
54 changes: 32 additions & 22 deletions enrichments/trace/internal/elastic/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,20 @@ func (s *spanEnrichmentContext) Enrich(span ptrace.Span, cfg config.Config) {
} else {
s.enrichSpan(span, cfg.Span)
}

spanEvents := span.Events()
for i := 0; i < spanEvents.Len(); i++ {
var c spanEventEnrichmentContext
c.enrich(spanEvents.At(i), cfg.SpanEvent)
}
}

func (s *spanEnrichmentContext) enrichTransaction(
span ptrace.Span,
cfg config.ElasticTransactionConfig,
) {
if cfg.TimestampUs.Enabled {
s.setTimestampUs(span)
span.Attributes().PutInt(AttributeTimestampUs, getTimestampUs(span.StartTimestamp()))
}
if cfg.Sampled.Enabled {
span.Attributes().PutBool(AttributeTransactionSampled, true)
Expand Down Expand Up @@ -204,7 +210,7 @@ func (s *spanEnrichmentContext) enrichSpan(
cfg config.ElasticSpanConfig,
) {
if cfg.TimestampUs.Enabled {
s.setTimestampUs(span)
span.Attributes().PutInt(AttributeTimestampUs, getTimestampUs(span.StartTimestamp()))
}
if cfg.Name.Enabled {
span.Attributes().PutStr(AttributeSpanName, span.Name())
Expand Down Expand Up @@ -241,26 +247,6 @@ func (s *spanEnrichmentContext) normalizeAttributes() {
}
}

// setTimestampUs sets the attribute timestamp.us for span and
// span events. This is a temporary function to enable higher
// resolution timestamps in Elasticsearch. For more details see:
// https://github.com/elastic/opentelemetry-dev/issues/374.
//
// TODO (lahsivjar): If more enrichments need to be added
// for span events then consider having a separate flow for span
// events enrichment with its own configuration.
func (s *spanEnrichmentContext) setTimestampUs(span ptrace.Span) {
startTsUs := int64(span.StartTimestamp()) / 1000
span.Attributes().PutInt(AttributeTimestampUs, startTsUs)

events := span.Events()
for i := 0; i < events.Len(); i++ {
event := events.At(i)
eventTsUs := int64(event.Timestamp()) / 1000
event.Attributes().PutInt(AttributeTimestampUs, eventTsUs)
}
}

func (s *spanEnrichmentContext) setTxnType(span ptrace.Span) {
txnType := "unknown"
switch {
Expand Down Expand Up @@ -430,6 +416,26 @@ func (s *spanEnrichmentContext) setDestinationService(span ptrace.Span) {
}
}

type spanEventEnrichmentContext struct {
exception bool
}

func (s *spanEventEnrichmentContext) enrich(
se ptrace.SpanEvent,
cfg config.SpanEventConfig,
) {
// Extract top level span event information.
s.exception = se.Name() == "exception"

// Enrich span event attributes.
if cfg.TimestampUs.Enabled {
se.Attributes().PutInt(AttributeTimestampUs, getTimestampUs(se.Timestamp()))
}
if cfg.ProcessorEvent.Enabled && s.exception {
se.Attributes().PutStr(AttributeProcessorEvent, "error")
}
}

// getRepresentativeCount returns the number of spans represented by an
// individually sampled span as per the passed tracestate header.
//
Expand Down Expand Up @@ -523,6 +529,10 @@ func getHostPort(urlFull *url.URL, urlDomain string, urlPort int64) string {
return ""
}

func getTimestampUs(ts pcommon.Timestamp) int64 {
return int64(ts) / 1000
}

var standardStatusCodeResults = [...]string{
"HTTP 1xx",
"HTTP 2xx",
Expand Down
55 changes: 55 additions & 0 deletions enrichments/trace/internal/elastic/span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,61 @@ func TestElasticSpanEnrich(t *testing.T) {
}
}

func TestSpanEventEnrich(t *testing.T) {
now := time.Unix(3600, 0)
ts := pcommon.NewTimestampFromTime(now)
for _, tc := range []struct {
name string
input ptrace.SpanEvent
config config.SpanEventConfig
enrichedAttrs map[string]any
}{
{
name: "not_exception",
input: func() ptrace.SpanEvent {
event := ptrace.NewSpanEvent()
event.SetTimestamp(ts)
return event
}(),
config: config.Enabled().SpanEvent,
enrichedAttrs: map[string]any{
AttributeTimestampUs: ts.AsTime().UnixMicro(),
},
},
{
name: "exception",
input: func() ptrace.SpanEvent {
event := ptrace.NewSpanEvent()
event.SetName("exception")
event.SetTimestamp(ts)
return event
}(),
config: config.Enabled().SpanEvent,
enrichedAttrs: map[string]any{
AttributeTimestampUs: ts.AsTime().UnixMicro(),
AttributeProcessorEvent: "error",
},
},
} {
t.Run(tc.name, func(t *testing.T) {
// Merge existing input attrs with the attrs added
// by enrichment to get the expected attributes.
expectedAttrs := tc.input.Attributes().AsRaw()
for k, v := range tc.enrichedAttrs {
expectedAttrs[k] = v
}

span := ptrace.NewSpan()
tc.input.MoveTo(span.Events().AppendEmpty())
EnrichSpan(span, config.Config{
SpanEvent: tc.config,
})

assert.Empty(t, cmp.Diff(expectedAttrs, span.Events().At(0).Attributes().AsRaw()))
})
}
}

func TestIsElasticTransaction(t *testing.T) {
for _, tc := range []struct {
name string
Expand Down

0 comments on commit 1df408d

Please sign in to comment.