Module: Datadog::Trace

Defined in:
lib/datadog/lambda/trace/context.rb,
lib/datadog/lambda/trace/listener.rb,
lib/datadog/lambda/trace/constants.rb,
lib/datadog/lambda/trace/patch_http.rb,
lib/datadog/lambda/trace/xray_lambda.rb

Overview

Trace contains methods to help with patching Net/HTTP

Defined Under Namespace

Modules: NetExtensions Classes: FacadeSegment, LambdaContext, LambdaEmitter, LambdaStreamer, Listener

Constant Summary collapse

SAMPLE_MODE_USER_REJECT =
-1
SAMPLE_MODE_AUTO_REJECT =
0
SAMPLE_MODE_AUTO_KEEP =
1
SAMPLE_MODE_USER_KEEP =
2
DD_TRACE_ID_HEADER =
'x-datadog-trace-id'
DD_PARENT_ID_HEADER =
'x-datadog-parent-id'
DD_SAMPLING_PRIORITY_HEADER =
'x-datadog-sampling-priority'
DD_XRAY_SUBSEGMENT_NAME =
'datadog-metadata'
DD_XRAY_SUBSEGMENT_KEY =
'trace'
DD_XRAY_SUBSEGMENT_NAMESPACE =
'datadog'

Class Method Summary collapse

Class Method Details

.add_trace_context_to_xray(context) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/datadog/lambda/trace/context.rb', line 33

def add_trace_context_to_xray(context)
  seg = XRay.recorder.begin_subsegment(
    DD_XRAY_SUBSEGMENT_NAME,
    namespace: DD_XRAY_SUBSEGMENT_NAMESPACE
  )
  data = {
    "parent-id": context[:parent_id],
    "sampling-priority": context[:sample_mode].to_s,
    "trace-id": context[:trace_id]
  }
  seg.(namespace: DD_XRAY_SUBSEGMENT_NAMESPACE)
     .update("#{DD_XRAY_SUBSEGMENT_KEY}": data)
  XRay.recorder.end_subsegment
end

.convert_to_apm_parent_id(xray_parent_id) ⇒ Object



117
118
119
120
121
122
123
# File 'lib/datadog/lambda/trace/context.rb', line 117

def convert_to_apm_parent_id(xray_parent_id)
  return nil if xray_parent_id.length != 16
  return nil if xray_parent_id.upcase[/\H/]

  hex = xray_parent_id.to_i(16)
  hex.to_s(10)
end

.convert_to_apm_trace_id(xray_trace_id) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/datadog/lambda/trace/context.rb', line 103

def convert_to_apm_trace_id(xray_trace_id)
  parts = xray_trace_id.split('-')
  return nil if parts.length < 3

  last_part = parts[2]
  return nil if last_part.length != 24
  # Make sure every character is hex
  return nil if last_part.upcase[/\H/]

  hex = last_part.to_i(16)
  last_63_bits = hex & 0x7fffffffffffffff
  last_63_bits.to_s(10)
end

.convert_to_sample_mode(xray_sampled) ⇒ Object



125
126
127
# File 'lib/datadog/lambda/trace/context.rb', line 125

def convert_to_sample_mode(xray_sampled)
  xray_sampled == '1' ? SAMPLE_MODE_USER_KEEP : SAMPLE_MODE_USER_REJECT
end

.current_trace_context(trace_context) ⇒ Object



48
49
50
51
52
53
54
55
56
# File 'lib/datadog/lambda/trace/context.rb', line 48

def current_trace_context(trace_context)
  entity = XRay.recorder.current_entity
  parent_id = entity.id
  {
    trace_id: trace_context[:trace_id],
    parent_id: convert_to_apm_parent_id(parent_id),
    sample_mode: trace_context[:sample_mode]
  }
end

.extract_trace_context(event) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/datadog/lambda/trace/context.rb', line 20

def extract_trace_context(event)
  context = read_trace_context_from_event(event)
  unless context.nil?
    begin
      add_trace_context_to_xray(context)
    rescue StandardError => e
      Datadog::Utils.logger.error("couldn't add metadata to xray #{e}")
    end
    return context
  end
  read_trace_context_from_xray
end

.headers?(event) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
89
# File 'lib/datadog/lambda/trace/context.rb', line 86

def headers?(event)
  event.is_a?(Hash) && event.key?('headers') &&
    event['headers'].is_a?(Hash)
end

.patch_httpObject



25
26
27
28
# File 'lib/datadog/lambda/trace/patch_http.rb', line 25

def self.patch_http
  Net::HTTP.prepend NetExtensions unless @patched
  @patched = true
end

.read_trace_context_from_event(event) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/datadog/lambda/trace/context.rb', line 72

def read_trace_context_from_event(event)
  return nil unless headers?(event)

  headers = event['headers'].transform_keys(&:downcase)

  return nil unless trace_headers_present?(headers)

  {
    trace_id: headers[DD_TRACE_ID_HEADER],
    parent_id: headers[DD_PARENT_ID_HEADER],
    sample_mode: headers[DD_SAMPLING_PRIORITY_HEADER].to_i
  }
end

.read_trace_context_from_xrayObject



58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/datadog/lambda/trace/context.rb', line 58

def read_trace_context_from_xray
  segment = XRay.recorder.current_entity
  parent_id = segment.id
  mode = segment.sampled ? SAMPLE_MODE_USER_KEEP : SAMPLE_MODE_USER_REJECT
  {
    trace_id: convert_to_apm_trace_id(segment.trace_id),
    parent_id: convert_to_apm_parent_id(parent_id),
    sample_mode: mode
  }
rescue StandardError => e
  Datadog::Utils.logger.error("couldn't read xray trace header #{e}")
  nil
end

.trace_contextObject



16
17
18
# File 'lib/datadog/lambda/trace/patch_http.rb', line 16

def self.trace_context
  @trace_context
end

.trace_context=(val) ⇒ Object



20
21
22
# File 'lib/datadog/lambda/trace/patch_http.rb', line 20

def self.trace_context=(val)
  @trace_context = val
end

.trace_headers_present?(headers) ⇒ Boolean

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
100
101
# File 'lib/datadog/lambda/trace/context.rb', line 91

def trace_headers_present?(headers)
  expected = [
    DD_TRACE_ID_HEADER,
    DD_PARENT_ID_HEADER,
    DD_SAMPLING_PRIORITY_HEADER
  ]
  expected.each do |key|
    return false unless headers.key?(key) && headers[key].is_a?(String)
  end
  true
end