Skip to content

Commit

Permalink
feat: Add dropped events/attributes/links counts to zipkin + jaeger e…
Browse files Browse the repository at this point in the history
…xporters (#1235)

* feat: Add dropped events/attributes/links counts to zipkin + jaeger exporters

I was reviewing for spec compliance today, and noticed that we are not
adding `otel.dropped_{events,attributes,links}_count` to span tags as
required by the Zipkin and Jaeger exporter specs. We regret the omission.

https://github.com/open-telemetry/opentelemetry-specification/blob/f04897c16c9b9690076d52fdd94235e92ceefc96/specification/trace/sdk_exporters/jaeger.md
https://github.com/open-telemetry/opentelemetry-specification/blob/f04897c16c9b9690076d52fdd94235e92ceefc96/specification/trace/sdk_exporters/zipkin.md

* Fix the cop

Co-authored-by: Francis Bogsanyi <francis.bogsanyi@shopify.com>
  • Loading branch information
ahayworth and fbogsany authored Jun 21, 2022
1 parent 5295423 commit 321d1d4
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 14 deletions.
20 changes: 15 additions & 5 deletions exporter/jaeger/lib/opentelemetry/exporter/jaeger/encoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,23 @@ def encoded_tag(key, value)
)
end

def encoded_span(span_data) # rubocop:disable Metrics/AbcSize
def encoded_span(span_data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
start_time = span_data.start_timestamp / 1_000
duration = span_data.end_timestamp / 1_000 - start_time

tags = encoded_tags(span_data.attributes) +
encoded_status(span_data.status) +
encoded_kind(span_data.kind) +
encoded_instrumentation_library(span_data.instrumentation_library)

dropped_attributes_count = span_data.total_recorded_attributes - span_data.attributes&.size.to_i
dropped_events_count = span_data.total_recorded_events - span_data.events&.size.to_i
dropped_links_count = span_data.total_recorded_links - span_data.links&.size.to_i

tags << encoded_tag('otel.dropped_attributes_count', dropped_attributes_count) if dropped_attributes_count.positive?
tags << encoded_tag('otel.dropped_events_count', dropped_events_count) if dropped_events_count.positive?
tags << encoded_tag('otel.dropped_links_count', dropped_links_count) if dropped_links_count.positive?

Thrift::Span.new(
'traceIdLow' => int64(span_data.trace_id[8, 8]),
'traceIdHigh' => int64(span_data.trace_id[0, 8]),
Expand All @@ -78,10 +91,7 @@ def encoded_span(span_data) # rubocop:disable Metrics/AbcSize
'flags' => span_data.trace_flags.sampled? ? 1 : 0,
'startTime' => start_time,
'duration' => duration,
'tags' => encoded_tags(span_data.attributes) +
encoded_status(span_data.status) +
encoded_kind(span_data.kind) +
encoded_instrumentation_library(span_data.instrumentation_library),
'tags' => tags,
'logs' => encoded_logs(span_data.events)
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,28 @@
)
end

it 'records dropped attribute, event, and links counts when things were dropped' do
span_data = create_span_data(total_recorded_attributes: 1, total_recorded_events: 1, total_recorded_links: 1)
encoded_span = Encoder.encoded_span(span_data)
expected_tags = [
{ key: 'otel.dropped_attributes_count', value: 1, type: OpenTelemetry::Exporter::Jaeger::Thrift::TagType::LONG },
{ key: 'otel.dropped_events_count', value: 1, type: OpenTelemetry::Exporter::Jaeger::Thrift::TagType::LONG },
{ key: 'otel.dropped_links_count', value: 1, type: OpenTelemetry::Exporter::Jaeger::Thrift::TagType::LONG }
]
expected_tags.each_with_index do |expected_tag, idx|
tag = encoded_span.tags[idx]
_(tag.key).must_equal(expected_tag[:key])
_(tag.vType).must_equal(expected_tag[:type])
_(tag.vLong).must_equal(expected_tag[:value])
end
end

it 'does not record dropped attribute, event, or link counts when things were not dropped' do
span_data = create_span_data
encoded_span = Encoder.encoded_span(span_data)
_(encoded_span.tags).must_equal([])
end

it 'encodes array attribute values in events and the span as JSON strings' do
attributes = { 'akey' => ['avalue'] }
events = [
Expand Down Expand Up @@ -128,15 +150,15 @@
end
end

def create_span_data(status: nil, kind: nil, attributes: nil, events: nil, links: nil, instrumentation_library: nil, trace_id: OpenTelemetry::Trace.generate_trace_id, trace_flags: OpenTelemetry::Trace::TraceFlags::DEFAULT, tracestate: nil)
def create_span_data(status: nil, kind: nil, attributes: nil, total_recorded_attributes: 0, events: nil, total_recorded_events: 0, links: nil, total_recorded_links: 0, instrumentation_library: nil, trace_id: OpenTelemetry::Trace.generate_trace_id, trace_flags: OpenTelemetry::Trace::TraceFlags::DEFAULT, tracestate: nil)
OpenTelemetry::SDK::Trace::SpanData.new(
'',
kind,
status,
OpenTelemetry::Trace::INVALID_SPAN_ID,
0,
0,
0,
total_recorded_attributes,
total_recorded_events,
total_recorded_links,
OpenTelemetry::TestHelpers.exportable_timestamp,
OpenTelemetry::TestHelpers.exportable_timestamp,
attributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ def add_status_tags(span_data, tags)
end
end

def add_conditional_tags(zipkin_span, span_data, tags, service_name)
def add_conditional_tags(zipkin_span, span_data, tags, service_name) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
dropped_attributes_count = span_data.total_recorded_attributes - span_data.attributes&.size.to_i
dropped_events_count = span_data.total_recorded_events - span_data.events&.size.to_i
dropped_links_count = span_data.total_recorded_links - span_data.links&.size.to_i
tags['otel.dropped_attributes_count'] = dropped_attributes_count.to_s if dropped_attributes_count.positive?
tags['otel.dropped_events_count'] = dropped_events_count.to_s if dropped_events_count.positive?
tags['otel.dropped_links_count'] = dropped_links_count.to_s if dropped_links_count.positive?

zipkin_span['tags'] = tags unless tags.empty?
zipkin_span['kind'] = KIND_MAP[span_data.kind] unless span_data.kind.nil?
zipkin_span['parentId'] = span_data.hex_parent_span_id unless span_data.parent_span_id == OpenTelemetry::Trace::INVALID_SPAN_ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@
_(tags).must_equal('akey' => 'avalue', 'bar' => 'baz', 'otel.library.version' => '0.0.0', 'otel.library.name' => 'vendorlib')
end

it 'records dropped attribute, event, and links counts when things were dropped' do
resource = OpenTelemetry::SDK::Resources::Resource.create('service.name' => 'foo')
span_data = create_span_data(total_recorded_attributes: 1, total_recorded_events: 1, total_recorded_links: 1)
encoded_span = Transformer.to_zipkin_span(span_data, resource)
tags = encoded_span['tags']
_(tags).must_equal(
'otel.library.version' => '0.0.0',
'otel.library.name' => 'vendorlib',
'otel.dropped_attributes_count' => '1',
'otel.dropped_events_count' => '1',
'otel.dropped_links_count' => '1'
)
end

it 'does not record dropped attribute, event, or link counts when things were not dropped' do
resource = OpenTelemetry::SDK::Resources::Resource.create('service.name' => 'foo')
span_data = create_span_data
encoded_span = Transformer.to_zipkin_span(span_data, resource)
tags = encoded_span['tags']
_(tags).must_equal('otel.library.version' => '0.0.0', 'otel.library.name' => 'vendorlib')
end

it 'encodes array attribute values in events and the span as JSON strings' do
attributes = { 'akey' => ['avalue'], 'bar' => 'baz' }
events = [
Expand Down
8 changes: 4 additions & 4 deletions exporter/zipkin/test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
require 'minitest/autorun'
require 'webmock/minitest'

def create_span_data(status: nil, kind: nil, attributes: nil, events: nil, links: nil, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('vendorlib', '0.0.0'), trace_id: OpenTelemetry::Trace.generate_trace_id, trace_flags: OpenTelemetry::Trace::TraceFlags::DEFAULT, tracestate: nil)
def create_span_data(status: nil, kind: nil, attributes: nil, total_recorded_attributes: 0, events: nil, total_recorded_events: 0, links: nil, total_recorded_links: 0, instrumentation_library: OpenTelemetry::SDK::InstrumentationLibrary.new('vendorlib', '0.0.0'), trace_id: OpenTelemetry::Trace.generate_trace_id, trace_flags: OpenTelemetry::Trace::TraceFlags::DEFAULT, tracestate: nil)
OpenTelemetry::SDK::Trace::SpanData.new(
'',
kind,
status,
OpenTelemetry::Trace::INVALID_SPAN_ID,
0,
0,
0,
total_recorded_attributes,
total_recorded_events,
total_recorded_links,
OpenTelemetry::TestHelpers.exportable_timestamp,
OpenTelemetry::TestHelpers.exportable_timestamp,
attributes,
Expand Down

0 comments on commit 321d1d4

Please sign in to comment.