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

[enrichments] Add processor.event for span events #85

Merged
merged 1 commit into from
Aug 19, 2024
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
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