Class: Datadog::Tracing::Distributed::Baggage

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/tracing/distributed/baggage.rb

Overview

W3C Baggage propagator implementation. The baggage header is propagated through ‘baggage`.

Constant Summary collapse

BAGGAGE_KEY =
'baggage'
DD_TRACE_BAGGAGE_MAX_ITEMS =
64
DD_TRACE_BAGGAGE_MAX_BYTES =
8192
BAGGAGE_TAG_KEYS_MATCH_ALL =
['*'].freeze
SAFE_CHARACTERS_KEY =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$!#&'*+-.^_`|~"
SAFE_CHARACTERS_VALUE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$!#&'()*+-./:<>?@[]^_`{|}~"

Instance Method Summary collapse

Constructor Details

#initialize(fetcher:, baggage_key: BAGGAGE_KEY, baggage_tag_keys: ::Datadog.configuration.tracing.baggage_tag_keys) ⇒ Baggage

Returns a new instance of Baggage.



24
25
26
27
28
29
30
31
32
# File 'lib/datadog/tracing/distributed/baggage.rb', line 24

def initialize(
  fetcher:,
  baggage_key: BAGGAGE_KEY,
  baggage_tag_keys: ::Datadog.configuration.tracing.baggage_tag_keys
)
  @baggage_key = baggage_key
  @fetcher = fetcher
  @baggage_tag_keys = baggage_tag_keys
end

Instance Method Details

#extract(data) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/datadog/tracing/distributed/baggage.rb', line 84

def extract(data)
  fetcher = @fetcher.new(data)
  data = fetcher[@baggage_key]
  return unless data

  baggage = parse_baggage_header(fetcher[@baggage_key])
  return unless baggage

  # Convert selected baggage items to span tags based on configuration
  baggage_tags = build_baggage_tags(baggage)

  # Record telemetry for successful extraction only if baggage is not empty
  unless baggage.empty?
    record_telemetry_metric('context_header_style.extracted', 1, {'header_style' => 'baggage'})
  end

  TraceDigest.new(
    baggage: baggage,
    trace_distributed_tags: baggage_tags
  )
end

#inject!(digest, data) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/datadog/tracing/distributed/baggage.rb', line 34

def inject!(digest, data)
  return if digest.nil? || digest.baggage.nil?

  baggage_items = digest.baggage.reject { |k, v| k.nil? || v.nil? }
  return if baggage_items.empty?

  begin
    if baggage_items.size > DD_TRACE_BAGGAGE_MAX_ITEMS
      ::Datadog.logger.warn("Baggage item limit (#{DD_TRACE_BAGGAGE_MAX_ITEMS}) exceeded, dropping excess items")
      # Record telemetry for item count truncation
      record_telemetry_metric(
        'context_header.truncated',
        1,
        {'header_style' => 'baggage', 'truncation_reason' => 'baggage_item_count_exceeded'}
      )
      baggage_items = baggage_items.first(DD_TRACE_BAGGAGE_MAX_ITEMS)
    end

    encoded_items = []
    total_size = 0

    baggage_items.each do |key, value|
      item = "#{encode_item(key, SAFE_CHARACTERS_KEY)}=#{encode_item(value, SAFE_CHARACTERS_VALUE)}"
      item_size = item.bytesize + (encoded_items.empty? ? 0 : 1) # +1 for comma if not first item
      if total_size + item_size > DD_TRACE_BAGGAGE_MAX_BYTES
        ::Datadog.logger.warn("Baggage header size (#{DD_TRACE_BAGGAGE_MAX_BYTES}) exceeded, dropping excess items")
        # Record telemetry for byte count truncation
        record_telemetry_metric(
          'context_header.truncated',
          1,
          {'header_style' => 'baggage', 'truncation_reason' => 'baggage_byte_count_exceeded'}
        )
        break # stop adding items when size limit is reached
      end
      encoded_items << item
      total_size += item_size
    end

    # edge case where a single item is too large
    return if encoded_items.empty?

    data[@baggage_key] = encoded_items.join(',')

    # Record telemetry for successful injection
    record_telemetry_metric('context_header_style.injected', 1, {'header_style' => 'baggage'})
  rescue => e
    ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.class}: #{e}")
  end
end