Skip to content

Commit

Permalink
initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ZStriker19 committed Feb 11, 2025
1 parent fba70fd commit a4d5eb2
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 3 deletions.
4 changes: 3 additions & 1 deletion lib/datadog/tracing/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def self.extended(base)
#
# The tracer will try to find distributed headers in the order they are present in the list provided to this option.
# The first format to have valid data present will be used.
#
# Baggage style is a special case, as it will always be extracted in addition if present.
# @default `DD_TRACE_PROPAGATION_STYLE_EXTRACT` environment variable (comma-separated list),
# otherwise `['datadog','b3multi','b3']`.
# @return [Array<String>]
Expand All @@ -53,6 +53,7 @@ def self.extended(base)
[
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_BAGGAGE,
]
)
o.after_set do |styles|
Expand All @@ -74,6 +75,7 @@ def self.extended(base)
o.default [
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_BAGGAGE,
]
o.after_set do |styles|
# Make values case-insensitive
Expand Down
109 changes: 109 additions & 0 deletions lib/datadog/tracing/distributed/baggage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# frozen_string_literal: true

require_relative '../metadata/ext'
require_relative '../trace_digest'
require_relative 'datadog_tags_codec'
require_relative '../utils'
require_relative 'helpers'
require 'uri'

module Datadog
module Tracing
module Distributed
# W3C Baggage propagator implementation.
# The baggage header is propagated through `baggage`.
# @see https://www.w3.org/TR/baggage/
class Baggage
BAGGAGE_KEY = 'baggage'
DD_TRACE_BAGGAGE_MAX_ITEMS = 64
DD_TRACE_BAGGAGE_MAX_BYTES = 8192
SAFE_CHARACTERS_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-.^_`|~"
SAFE_CHARACTERS_VALUE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()*+-./:<>?@[]^_`{|}~"
private_constant :BAGGAGE_KEY,
:DD_TRACE_BAGGAGE_MAX_ITEMS,
:DD_TRACE_BAGGAGE_MAX_BYTES,
:SAFE_CHARACTERS_KEY,
:SAFE_CHARACTERS_VALUE

def initialize(
fetcher:,
baggage_key: BAGGAGE_KEY
)
@baggage_key = baggage_key
@fetcher = fetcher
end

def inject!(digest, data)
baggage_items = digest.baggage.to_a
return if baggage_items.empty?

begin
if baggage_items.size > DD_TRACE_BAGGAGE_MAX_ITEMS
Datadog.logger.warn('Baggage item limit exceeded, dropping excess items')
baggage_items = baggage_items.first(DD_TRACE_BAGGAGE_MAX_ITEMS)
end

encoded_items = []
total_size = 0

baggage_items.each do |key, value|
item = "#{encode_key(key)}=#{encode_value(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 exceeded, dropping excess items')
break # stop adding items when size limit is reached
end
encoded_items << item
total_size += item_size
end

header_value = encoded_items.join(',')
data[baggage_key] = header_value
rescue => e
Datadog.logger.warn("Failed to encode and inject baggage header: #{e.message}")
end
end

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

TraceDigest.new(
baggage: baggage,
)
end

private

def encode_key(key)
URI.encode_www_form_component(key.strip).gsub(/[^#{Regexp.escape(SAFE_CHARACTERS_KEY)}]/o) do |char|
"%#{char.ord.to_s(16).upcase}"
end
end

def encode_value(value)
URI.encode_www_form_component(value.strip).gsub(/[^#{Regexp.escape(SAFE_CHARACTERS_VALUE)}]/o) do |char|
"%#{char.ord.to_s(16).upcase}"
end
end

def parse_baggage_header(baggage_header)
baggage = {}
baggages = baggage_header.split(',')
baggages.each do |key_value|
next unless key_value.include?('=')

key, value = key_value.split('=', 2)
key = URI.decode_www_form_component(key.strip)
value = URI.decode_www_form_component(value.strip)
next if key.empty? || value.empty?

baggage[key] = value
end
baggage
end
end
end
end
end
17 changes: 17 additions & 0 deletions lib/datadog/tracing/distributed/propagation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def initialize(

@propagation_style_inject = propagation_style_inject.map { |style| propagation_styles[style] }
@propagation_style_extract = propagation_style_extract.map { |style| propagation_styles[style] }

# The baggage propagator is unique in that baggage should always be extracted, if present.
# Therefore we remove it from the `propagation_style_extract` list.
@baggage_propagator = @propagation_style_extract.find { |propagator| propagator.is_a?(Baggage) }
@propagation_style_extract.delete(@baggage_propagator) if @baggage_propagator
end

# inject! populates the env with span ID, trace ID and sampling priority
Expand Down Expand Up @@ -138,12 +143,24 @@ def extract(data)
"Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
)
end
# Handle baggage after all other styles if present
extracted_trace_digest = propagate_baggage(data, extracted_trace_digest) if @baggage_propagator

extracted_trace_digest
end

private

def propagate_baggage(data, extracted_trace_digest)
if extracted_trace_digest
# Merge with baggage if present
TraceDigest.merge(extracted_trace_digest, @baggage_propagator.extract(data))
else
# Baggage is the only style
@baggage_propagator.extract(data)
end
end

def last_datadog_parent_id(headers, tracecontext_tags)
dd_propagator = @propagation_style_extract.find { |propagator| propagator.is_a?(Datadog) }
if tracecontext_tags&.fetch(
Expand Down
8 changes: 6 additions & 2 deletions lib/datadog/tracing/trace_operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class TraceOperation
:rule_sample_rate,
:sample_rate,
:sampling_priority,
:remote_parent
:remote_parent,
:baggage

attr_reader \
:active_span_count,
Expand Down Expand Up @@ -76,7 +77,8 @@ def initialize(
trace_state: nil,
trace_state_unknown_fields: nil,
remote_parent: false,
tracer: nil
tracer: nil,
baggage: nil

)
# Attributes
Expand All @@ -101,6 +103,7 @@ def initialize(
@trace_state = trace_state
@trace_state_unknown_fields = trace_state_unknown_fields
@tracer = tracer
@baggage = baggage || {}

# Generic tags
set_tags(tags) if tags
Expand Down Expand Up @@ -332,6 +335,7 @@ def to_digest
trace_state: @trace_state,
trace_state_unknown_fields: @trace_state_unknown_fields,
span_remote: (@remote_parent && @active_span.nil?),
baggage: @baggage,
).freeze
end

Expand Down

0 comments on commit a4d5eb2

Please sign in to comment.