Class: Datadog::Tracing::Distributed::Propagation

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

Overview

Provides extraction and injection of distributed trace data.

Instance Method Summary collapse

Constructor Details

#initialize(propagation_styles:, propagation_style_inject:, propagation_style_extract:, propagation_extract_first:) ⇒ Propagation

Returns a new instance of Propagation.

Parameters:

  • propagation_styles (Hash<String,Object>)

    a map of propagation styles to their corresponding implementations

  • propagation_style_inject (Array<String>)

    a list of styles to use when injecting distributed trace data

  • propagation_style_extract (Array<String>)

    a list of styles to use when extracting distributed trace data

  • propagation_extract_first (Boolean)

    if true, only the first successfully extracted trace will be used



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

def initialize(
  propagation_styles:,
  propagation_style_inject:,
  propagation_style_extract:,
  propagation_extract_first:
)
  @propagation_styles = propagation_styles
  @propagation_extract_first = propagation_extract_first

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

Instance Method Details

#extract(data) ⇒ Object

extract returns TraceDigest containing the distributed trace information. sampling priority defined in data.

This method will never raise errors, but instead log them to ‘Datadog.logger`.

Parameters:

  • data (Hash)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/datadog/tracing/distributed/propagation.rb', line 92

def extract(data)
  return unless data
  return if data.empty?

  extracted_trace_digest = nil

  @propagation_style_extract.each do |propagator|
    # First extraction?
    unless extracted_trace_digest
      extracted_trace_digest = propagator.extract(data)
      next
    end

    # Return if we are only inspecting the first valid style.
    next if @propagation_extract_first

    # Continue parsing styles to find the W3C `tracestate` header, if present.
    # `tracestate` must always be propagated, as it might contain pass-through data that we don't control.
    # @see https://www.w3.org/TR/2021/REC-trace-context-1-20211123/#mutating-the-tracestate-field
    next unless propagator.is_a?(TraceContext)

    if (tracecontext_digest = propagator.extract(data))
      # Only parse if it represent the same trace as the successfully extracted one
      next unless tracecontext_digest.trace_id == extracted_trace_digest.trace_id

      parent_id = extracted_trace_digest.span_id
      distributed_tags = extracted_trace_digest.trace_distributed_tags
      unless extracted_trace_digest.span_id == tracecontext_digest.span_id
        # span_id in the tracecontext header takes precedence over the value in all conflicting headers
        parent_id = tracecontext_digest.span_id
        if (lp_id = last_datadog_parent_id(data, tracecontext_digest.trace_distributed_tags))
          distributed_tags = extracted_trace_digest.trace_distributed_tags&.dup || {}
          distributed_tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] = lp_id
        end
      end
      # Preserve the trace state and last datadog span id
      extracted_trace_digest = extracted_trace_digest.merge(
        span_id: parent_id,
        trace_state: tracecontext_digest.trace_state,
        trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields,
        trace_distributed_tags: distributed_tags
      )
    end
  rescue => e
    # TODO: Not to report Telemetry logs for now
    ::Datadog.logger.error(
      "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
    )
  end

  extracted_trace_digest
end

#inject!(digest, data) ⇒ Boolean?

inject! populates the env with span ID, trace ID and sampling priority

This method will never raise errors. It can propagate partial data, if deemed useful, instead of failing. In case of unrecoverable errors, it will log them to ‘Datadog.logger`.

DEV-2.0: inject! should work without arguments, injecting the active_trace’s digest DEV-2.0: and returning a new Hash with the injected data. DEV-2.0: inject! should also accept either a ‘trace` or a `digest`, as a `trace` DEV-2.0: argument is the common use case, but also allows us to set error tags in the `trace` DEV-2.0: if needed. DEV-2.0: Ideally, we’d have a separate stream to report tracer errors and never DEV-2.0: touch the active span. DEV-3.0: Sample trace here instead of when generating digest.

Parameters:

Returns:

  • (Boolean)

    ‘true` if injected successfully, `false` if no propagation style is configured

  • (nil)

    in case of unrecoverable errors, see ‘Datadog.logger` output for details.



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
83
84
# File 'lib/datadog/tracing/distributed/propagation.rb', line 53

def inject!(digest, data)
  if digest.nil?
    ::Datadog.logger.debug('Cannot inject distributed trace data: digest is nil.')
    return nil
  end

  digest = digest.to_digest if digest.respond_to?(:to_digest)

  if digest.trace_id.nil?
    ::Datadog.logger.debug('Cannot inject distributed trace data: digest.trace_id is nil.')
    return nil
  end

  result = false

  # Inject all configured propagation styles
  @propagation_style_inject.each do |propagator|
    propagator.inject!(digest, data)
    result = true
  rescue => e
    result = nil
    ::Datadog.logger.error(
      "Error injecting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
    )
    ::Datadog::Core::Telemetry::Logger.report(
      e,
      description: "Error injecting distributed trace data with #{propagator.class.name}"
    )
  end

  result
end