ddtrace 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -2
  3. data/README.md +2 -2
  4. data/ext/ddtrace_profiling_loader/extconf.rb +5 -2
  5. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +1 -1
  6. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +3 -2
  7. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +81 -47
  8. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +1 -1
  9. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +332 -125
  10. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.c +142 -0
  11. data/ext/ddtrace_profiling_native_extension/collectors_dynamic_sampling_rate.h +14 -0
  12. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.c +241 -0
  13. data/ext/ddtrace_profiling_native_extension/collectors_idle_sampling_helper.h +3 -0
  14. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +11 -13
  15. data/ext/ddtrace_profiling_native_extension/extconf.rb +22 -8
  16. data/ext/ddtrace_profiling_native_extension/helpers.h +5 -0
  17. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +8 -0
  18. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +111 -26
  19. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +9 -0
  20. data/ext/ddtrace_profiling_native_extension/profiling.c +205 -0
  21. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +86 -0
  22. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -6
  23. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +115 -0
  24. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +11 -0
  25. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +84 -35
  26. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +1 -0
  27. data/ext/ddtrace_profiling_native_extension/time_helpers.c +17 -0
  28. data/ext/ddtrace_profiling_native_extension/time_helpers.h +10 -0
  29. data/lib/datadog/appsec/assets/blocked.html +98 -3
  30. data/lib/datadog/appsec/assets/blocked.json +1 -0
  31. data/lib/datadog/appsec/assets/blocked.text +5 -0
  32. data/lib/datadog/appsec/assets/waf_rules/recommended.json +35 -46
  33. data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
  34. data/lib/datadog/appsec/assets/waf_rules/strict.json +46 -1
  35. data/lib/datadog/appsec/assets.rb +2 -2
  36. data/lib/datadog/appsec/configuration/settings.rb +6 -0
  37. data/lib/datadog/appsec/configuration.rb +4 -0
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +4 -8
  39. data/lib/datadog/appsec/contrib/rack/request.rb +17 -0
  40. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +2 -2
  41. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +2 -2
  42. data/lib/datadog/appsec/contrib/rails/patcher.rb +3 -6
  43. data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -0
  44. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -8
  46. data/lib/datadog/appsec/extensions.rb +10 -0
  47. data/lib/datadog/appsec/processor.rb +18 -0
  48. data/lib/datadog/appsec/response.rb +54 -0
  49. data/lib/datadog/core/configuration/components.rb +27 -6
  50. data/lib/datadog/core/configuration/ext.rb +18 -0
  51. data/lib/datadog/core/configuration/settings.rb +14 -341
  52. data/lib/datadog/core/diagnostics/health.rb +4 -22
  53. data/lib/datadog/core/environment/variable_helpers.rb +58 -10
  54. data/lib/datadog/core/runtime/ext.rb +1 -1
  55. data/lib/datadog/core/utils.rb +0 -21
  56. data/lib/datadog/core.rb +21 -1
  57. data/lib/datadog/opentracer/distributed_headers.rb +7 -9
  58. data/lib/datadog/opentracer/rack_propagator.rb +0 -3
  59. data/lib/datadog/opentracer/text_map_propagator.rb +5 -7
  60. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +10 -4
  61. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +20 -5
  62. data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
  63. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
  64. data/lib/datadog/profiling/collectors/old_stack.rb +7 -0
  65. data/lib/datadog/profiling/exporter.rb +5 -0
  66. data/lib/datadog/profiling/old_recorder.rb +8 -0
  67. data/lib/datadog/profiling/profiler.rb +7 -0
  68. data/lib/datadog/profiling/scheduler.rb +4 -7
  69. data/lib/datadog/profiling/stack_recorder.rb +36 -0
  70. data/lib/datadog/profiling/tasks/setup.rb +0 -7
  71. data/lib/datadog/profiling.rb +2 -0
  72. data/lib/datadog/tracing/configuration/ext.rb +33 -3
  73. data/lib/datadog/tracing/configuration/settings.rb +433 -0
  74. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +4 -1
  75. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
  76. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +4 -1
  77. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  78. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +4 -0
  79. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +5 -1
  80. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
  81. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +6 -1
  82. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  83. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -1
  84. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  85. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -1
  86. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  87. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +6 -1
  88. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -1
  89. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -12
  90. data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +27 -0
  91. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +43 -0
  92. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  93. data/lib/datadog/tracing/contrib/grpc/patcher.rb +0 -2
  94. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +6 -1
  95. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +32 -0
  96. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +38 -0
  97. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  98. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +6 -1
  99. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  100. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +6 -1
  101. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  102. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -0
  103. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -0
  104. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -0
  105. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +5 -1
  106. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  107. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  108. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -1
  109. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  110. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -2
  111. data/lib/datadog/tracing/contrib/patcher.rb +3 -2
  112. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +4 -1
  113. data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
  114. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +12 -2
  115. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +4 -1
  116. data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
  117. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +1 -0
  118. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -12
  119. data/lib/datadog/tracing/contrib/que/tracer.rb +2 -0
  120. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +4 -1
  121. data/lib/datadog/tracing/contrib/racecar/events/message.rb +4 -1
  122. data/lib/datadog/tracing/contrib/rack/middlewares.rb +2 -0
  123. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +4 -1
  124. data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
  125. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +30 -21
  126. data/lib/datadog/tracing/contrib/redis/integration.rb +34 -2
  127. data/lib/datadog/tracing/contrib/redis/patcher.rb +18 -14
  128. data/lib/datadog/tracing/contrib/redis/quantize.rb +12 -9
  129. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -6
  130. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +72 -0
  131. data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -0
  132. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  133. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  134. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +2 -0
  135. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +5 -0
  136. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -0
  137. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +2 -0
  138. data/lib/datadog/{core → tracing}/diagnostics/ext.rb +1 -6
  139. data/lib/datadog/tracing/diagnostics/health.rb +40 -0
  140. data/lib/datadog/tracing/distributed/b3_multi.rb +66 -0
  141. data/lib/datadog/tracing/distributed/b3_single.rb +66 -0
  142. data/lib/datadog/tracing/distributed/datadog.rb +153 -0
  143. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -0
  144. data/lib/datadog/tracing/distributed/fetcher.rb +30 -0
  145. data/lib/datadog/tracing/distributed/headers/ext.rb +18 -16
  146. data/lib/datadog/tracing/distributed/helpers.rb +9 -7
  147. data/lib/datadog/tracing/distributed/none.rb +19 -0
  148. data/lib/datadog/tracing/distributed/propagation.rb +127 -0
  149. data/lib/datadog/tracing/distributed/trace_context.rb +369 -0
  150. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  151. data/lib/datadog/tracing/propagation/http.rb +3 -106
  152. data/lib/datadog/tracing/sampling/priority_sampler.rb +11 -0
  153. data/lib/datadog/tracing/sampling/rate_sampler.rb +3 -3
  154. data/lib/datadog/tracing/span.rb +3 -19
  155. data/lib/datadog/tracing/span_operation.rb +5 -4
  156. data/lib/datadog/tracing/trace_digest.rb +75 -2
  157. data/lib/datadog/tracing/trace_operation.rb +5 -4
  158. data/lib/datadog/tracing/trace_segment.rb +1 -1
  159. data/lib/datadog/tracing/utils.rb +50 -0
  160. data/lib/ddtrace/transport/trace_formatter.rb +2 -5
  161. data/lib/ddtrace/version.rb +2 -2
  162. metadata +35 -15
  163. data/lib/datadog/tracing/distributed/headers/b3.rb +0 -55
  164. data/lib/datadog/tracing/distributed/headers/b3_single.rb +0 -67
  165. data/lib/datadog/tracing/distributed/headers/datadog.rb +0 -144
  166. data/lib/datadog/tracing/distributed/headers/parser.rb +0 -37
  167. data/lib/datadog/tracing/distributed/metadata/b3.rb +0 -55
  168. data/lib/datadog/tracing/distributed/metadata/b3_single.rb +0 -66
  169. data/lib/datadog/tracing/distributed/metadata/datadog.rb +0 -73
  170. data/lib/datadog/tracing/distributed/metadata/parser.rb +0 -34
  171. data/lib/datadog/tracing/propagation/grpc.rb +0 -98
@@ -0,0 +1,369 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ module Datadog
5
+ module Tracing
6
+ module Distributed
7
+ # W3C Trace Context propagator implementation, version 00.
8
+ # The trace is propagated through two fields: `traceparent` and `tracestate`.
9
+ # @see https://www.w3.org/TR/trace-context/
10
+ class TraceContext
11
+ TRACEPARENT_KEY = 'traceparent'
12
+ TRACESTATE_KEY = 'tracestate'
13
+ SPEC_VERSION = '00'
14
+
15
+ def initialize(
16
+ fetcher:,
17
+ traceparent_key: TRACEPARENT_KEY,
18
+ tracestate_key: TRACESTATE_KEY
19
+ )
20
+ @fetcher = fetcher
21
+ @traceparent_key = traceparent_key
22
+ @tracestate_key = tracestate_key
23
+ end
24
+
25
+ def inject!(digest, data)
26
+ return if digest.nil?
27
+
28
+ if (traceparent = build_traceparent(digest))
29
+ data[@traceparent_key] = traceparent
30
+
31
+ if (tracestate = build_tracestate(digest))
32
+ data[@tracestate_key] = tracestate
33
+ end
34
+ end
35
+
36
+ data
37
+ end
38
+
39
+ def extract(data)
40
+ fetcher = @fetcher.new(data)
41
+
42
+ trace_id, dd_trace_id, parent_id, sampled, trace_flags = extract_traceparent(fetcher[@traceparent_key])
43
+
44
+ return unless trace_id # Could not parse traceparent
45
+
46
+ tracestate, sampling_priority, origin, tags = extract_tracestate(fetcher[@tracestate_key])
47
+
48
+ sampling_priority = parse_priority_sampling(sampled, sampling_priority)
49
+
50
+ TraceDigest.new(
51
+ span_id: parent_id,
52
+ trace_id: dd_trace_id,
53
+ trace_origin: origin,
54
+ trace_sampling_priority: sampling_priority,
55
+ trace_distributed_tags: tags,
56
+ trace_distributed_id: trace_id,
57
+ trace_state: tracestate,
58
+ trace_flags: trace_flags
59
+ )
60
+ end
61
+
62
+ private
63
+
64
+ # Refinements to ensure newer rubies do not suffer performance impact
65
+ # by needing to use older APIs.
66
+ module Refine
67
+ # Backport `Regexp::match?` because it is measurably the most performant
68
+ # way to check if a string matches a regular expression.
69
+ unless Regexp.method_defined?(:match?)
70
+ refine ::Regexp do
71
+ def match?(*args)
72
+ !match(*args).nil?
73
+ end
74
+ end
75
+ end
76
+
77
+ unless String.method_defined?(:delete_prefix)
78
+ refine ::String do
79
+ def delete_prefix(prefix)
80
+ prefix = prefix.to_s
81
+ if rindex(prefix, 0)
82
+ self[prefix.length..-1]
83
+ else
84
+ dup
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ using Refine
91
+
92
+ # @see https://www.w3.org/TR/trace-context/#traceparent-header
93
+ def build_traceparent(digest)
94
+ build_traceparent_string(
95
+ digest.trace_distributed_id || digest.trace_id,
96
+ digest.span_id,
97
+ build_trace_flags(digest)
98
+ )
99
+ end
100
+
101
+ # For the current version (00), the traceparent has the following format:
102
+ #
103
+ # `"#{version}-#{trace_id}-#{parent_id}-#{trace_flags}"`
104
+ #
105
+ # Where:
106
+ # * `version`: 2 hex-encoded digits, zero padded.
107
+ # * `trace_id`: 32 hex-encoded digits, zero padded.
108
+ # * `parent_id`: 16 hex-encoded digits, zero padded.
109
+ # * `trace_flags`: 2 hex-encoded digits, zero padded.
110
+ #
111
+ # All hex values should be lowercase.
112
+ #
113
+ # @param trace_id [Integer] 128-bit
114
+ # @param parent_id [Integer] 64-bit
115
+ # @param trace_flags [Integer] 8-bit
116
+ def build_traceparent_string(trace_id, parent_id, trace_flags)
117
+ "00-#{format('%032x', trace_id)}-#{format('%016x', parent_id)}-#{format('%02x', trace_flags)}"
118
+ end
119
+
120
+ # Sets the trace flag to an existing `trace_flags`.
121
+ def build_trace_flags(digest)
122
+ trace_flags = digest.trace_flags || DEFAULT_TRACE_FLAGS
123
+
124
+ if digest.trace_sampling_priority
125
+ if Tracing::Sampling::PrioritySampler.sampled?(digest.trace_sampling_priority)
126
+ trace_flags |= TRACE_FLAGS_SAMPLED
127
+ else
128
+ trace_flags &= ~TRACE_FLAGS_SAMPLED
129
+ end
130
+ end
131
+
132
+ trace_flags
133
+ end
134
+
135
+ # @see https://www.w3.org/TR/trace-context/#tracestate-header
136
+ def build_tracestate(digest)
137
+ tracestate = String.new('dd=')
138
+ tracestate << "s:#{digest.trace_sampling_priority};" if digest.trace_sampling_priority
139
+ tracestate << "o:#{serialize_origin(digest.trace_origin)};" if digest.trace_origin
140
+
141
+ if digest.trace_distributed_tags
142
+ digest.trace_distributed_tags.each do |name, value|
143
+ tag = "t.#{serialize_tag_key(name)}:#{serialize_tag_value(value)};"
144
+
145
+ # If tracestate size limit is exceed, drop the remaining data.
146
+ # String#bytesize is used because only ASCII characters are allowed.
147
+ #
148
+ # We add 1 to the limit because of the trailing comma, which will be removed before returning.
149
+ break if tracestate.bytesize + tag.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
150
+
151
+ tracestate << tag
152
+ end
153
+ end
154
+
155
+ # Is there any Datadog-specific information to propagate.
156
+ # Check for > 3 size because the empty prefix `dd=` has 3 characters.
157
+ if tracestate.size > 3
158
+ # Propagate upstream tracestate with `dd=...` appended to the list
159
+ tracestate.chop! # Removes trailing `;` from Datadog trace state string.
160
+
161
+ if digest.trace_state
162
+ # Delete existing `dd=` tracestate fields, if present.
163
+ vendors = split_tracestate(digest.trace_state)
164
+ vendors.reject! { |v| v.start_with?('dd=') }
165
+ end
166
+
167
+ if vendors && !vendors.empty?
168
+ # Ensure the list has at most 31 elements, as we need to prepend Datadog's
169
+ # entry and the limit is 32 elements total.
170
+ vendors = vendors[0..30]
171
+ "#{tracestate},#{vendors.join(',')}"
172
+ else
173
+ tracestate.to_s
174
+ end
175
+ else
176
+ digest.trace_state # Propagate upstream tracestate with no Datadog changes
177
+ end
178
+ end
179
+
180
+ # If any characters in <origin_value> are invalid, replace each invalid character with 0x5F (underscore).
181
+ # Invalid characters are: characters outside the ASCII range 0x20 to 0x7E, 0x2C (comma), and 0x3D (equals).
182
+ def serialize_origin(value)
183
+ # DEV: It's unlikely that characters will be out of range, as they mostly
184
+ # DEV: come from Datadog-controlled sources.
185
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
186
+ if INVALID_ORIGIN_CHARS.match?(value)
187
+ value.gsub(INVALID_ORIGIN_CHARS, '_')
188
+ else
189
+ value
190
+ end
191
+ end
192
+
193
+ # Serialize `_dd.p.{key}` by first removing the `_dd.p.` prefix.
194
+ # Then replacing invalid characters with `_`.
195
+ def serialize_tag_key(name)
196
+ key = name.delete_prefix(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX)
197
+
198
+ # DEV: It's unlikely that characters will be out of range, as they mostly
199
+ # DEV: come from Datadog-controlled sources.
200
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
201
+ if INVALID_TAG_KEY_CHARS.match?(key)
202
+ key.gsub(INVALID_TAG_KEY_CHARS, '_')
203
+ else
204
+ key
205
+ end
206
+ end
207
+
208
+ # Replaces invalid characters with `_`, then replaces `=` with `:`.
209
+ def serialize_tag_value(value)
210
+ # DEV: It's unlikely that characters will be out of range, as they mostly
211
+ # DEV: come from Datadog-controlled sources.
212
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
213
+ ret = if INVALID_TAG_VALUE_CHARS.match?(value)
214
+ value.gsub(INVALID_TAG_VALUE_CHARS, '_')
215
+ else
216
+ value
217
+ end
218
+
219
+ # DEV: Checking for an unlikely '=' is faster than a no-op `tr!`.
220
+ ret.tr!('=', ':') if ret.include?('=')
221
+ ret
222
+ end
223
+
224
+ def extract_traceparent(traceparent)
225
+ trace_id, parent_id, trace_flags = parse_traceparent_string(traceparent)
226
+
227
+ # Return unless all traceparent fields are valid.
228
+ return unless trace_id && !trace_id.zero? && parent_id && !parent_id.zero? && trace_flags
229
+
230
+ dd_trace_id = parse_datadog_trace_id(trace_id)
231
+ sampled = parse_sampled_flag(trace_flags)
232
+
233
+ [trace_id, dd_trace_id, parent_id, sampled, trace_flags]
234
+ end
235
+
236
+ def parse_traceparent_string(traceparent)
237
+ return unless traceparent
238
+
239
+ version, trace_id, parent_id, trace_flags, extra = traceparent.strip.split('-')
240
+
241
+ return if version == INVALID_VERSION
242
+
243
+ # Extra fields are not allowed in version 00, but we have to be lenient for future versions.
244
+ return if version == SPEC_VERSION && extra
245
+
246
+ # Invalid field sizes
247
+ return if version.size != 2 || trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
248
+
249
+ [Integer(trace_id, 16), Integer(parent_id, 16), Integer(trace_flags, 16)]
250
+ rescue ArgumentError # Conversion to integer failed
251
+ nil
252
+ end
253
+
254
+ # Datadog only allows 64 bits for the trace id.
255
+ # We extract the lower 64 bits from the original 128-bit id.
256
+ def parse_datadog_trace_id(trace_id)
257
+ trace_id & 0xFFFFFFFFFFFFFFFF
258
+ end
259
+
260
+ def parse_sampled_flag(trace_flags)
261
+ trace_flags & TRACE_FLAGS_SAMPLED
262
+ end
263
+
264
+ # @return [Array<String,Integer,String,Hash>] returns 4 values: tracestate, sampling_priority, origin, tags.
265
+ def extract_tracestate(tracestate)
266
+ return unless tracestate
267
+
268
+ vendors = split_tracestate(tracestate)
269
+
270
+ # Find Datadog's `dd=` tracestate field.
271
+ dd_tracestate = vendors.find { |v| v.start_with?('dd=') }
272
+ return tracestate unless dd_tracestate
273
+
274
+ # Delete `dd=` prefix
275
+ dd_tracestate.slice!(0..2)
276
+
277
+ origin, sampling_priority, tags = extract_datadog_fields(dd_tracestate)
278
+
279
+ [tracestate, sampling_priority, origin, tags]
280
+ end
281
+
282
+ def extract_datadog_fields(dd_tracestate)
283
+ sampling_priority = nil
284
+ origin = nil
285
+ tags = nil
286
+
287
+ # DEV: Since Ruby 2.6 `split` can receive a block, so `each` can be removed then.
288
+ dd_tracestate.split(';').each do |pair|
289
+ key, value = pair.split(':', 2)
290
+ case key
291
+ when 's'
292
+ sampling_priority = Integer(value) rescue nil
293
+ when 'o'
294
+ origin = value
295
+ when /^t\./
296
+ key.slice!(0..1) # Delete `t.` prefix
297
+
298
+ value = deserialize_tag_value(value)
299
+
300
+ tags ||= {}
301
+ tags["#{Tracing::Metadata::Ext::Distributed::TAGS_PREFIX}#{key}"] = value
302
+ end
303
+ end
304
+
305
+ [origin, sampling_priority, tags]
306
+ end
307
+
308
+ # Restore `:` back to `=`.
309
+ def deserialize_tag_value(value)
310
+ value.tr!(':', '=')
311
+ value
312
+ end
313
+
314
+ # If `sampled` and `sampling_priority` disagree, `sampled` overrides the decision.
315
+ # @return [Integer] one of the {Datadog::Tracing::Sampling::Ext::Priority} values
316
+ def parse_priority_sampling(sampled, sampling_priority)
317
+ # If both fields agree
318
+ if sampling_priority &&
319
+ (!Tracing::Sampling::PrioritySampler.sampled?(sampling_priority) && sampled == 0 || # Both drop
320
+ Tracing::Sampling::PrioritySampler.sampled?(sampling_priority) && sampled == 1) # Both keep
321
+
322
+ return sampling_priority # Return the richer `sampling_priority`
323
+ end
324
+
325
+ sampled # Sampled flag trumps `sampling_priority` on conflict
326
+ end
327
+
328
+ def split_tracestate(tracestate)
329
+ tracestate.split(/[ \t]*,[ \t]*/)[0..31]
330
+ end
331
+
332
+ # Version 0xFF is invalid as per spec
333
+ # @see https://www.w3.org/TR/trace-context/#version
334
+ INVALID_VERSION = 'ff'
335
+ private_constant :INVALID_VERSION
336
+
337
+ # Empty 8-bit `trace-flags`.
338
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
339
+ DEFAULT_TRACE_FLAGS = 0b00000000
340
+ private_constant :DEFAULT_TRACE_FLAGS
341
+
342
+ # Bit-mask for `trace-flags` that represents a sampled span (sampled==true).
343
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
344
+ TRACE_FLAGS_SAMPLED = 0b00000001
345
+ private_constant :TRACE_FLAGS_SAMPLED
346
+
347
+ # The limit is inclusive: sizes *greater* than 256 are disallowed.
348
+ # @see https://www.w3.org/TR/trace-context/#value
349
+ TRACESTATE_VALUE_SIZE_LIMIT = 256
350
+ private_constant :TRACESTATE_VALUE_SIZE_LIMIT
351
+
352
+ # Replace all characters with `_`, except ASCII characters 0x20-0x7E.
353
+ # Additionally, `,`, ';', and `=` must also be replaced by `_`.
354
+ INVALID_ORIGIN_CHARS = /[\u0000-\u0019,;=\u007F-\u{10FFFF}]/.freeze
355
+ private_constant :INVALID_ORIGIN_CHARS
356
+
357
+ # Replace all characters with `_`, except ASCII characters 0x21-0x7E.
358
+ # Additionally, `,` and `=` must also be replaced by `_`.
359
+ INVALID_TAG_KEY_CHARS = /[\u0000-\u0020,=\u007F-\u{10FFFF}]/.freeze
360
+ private_constant :INVALID_TAG_KEY_CHARS
361
+
362
+ # Replace all characters with `_`, except ASCII characters 0x20-0x7E.
363
+ # Additionally, `,`, ':' and `;` must also be replaced by `_`.
364
+ INVALID_TAG_VALUE_CHARS = /[\u0000-\u001F,:;\u007F-\u{10FFFF}]/.freeze
365
+ private_constant :INVALID_TAG_VALUE_CHARS
366
+ end
367
+ end
368
+ end
369
+ end
@@ -60,7 +60,7 @@ module Datadog
60
60
  # @public_api
61
61
  module Errors
62
62
  STATUS = 1
63
- TAG_MSG = 'error.msg'
63
+ TAG_MSG = 'error.message'
64
64
  TAG_STACK = 'error.stack'
65
65
  TAG_TYPE = 'error.type'
66
66
  end
@@ -1,117 +1,14 @@
1
1
  # typed: false
2
2
 
3
- require_relative '../../core'
4
-
5
- require_relative '../configuration/ext'
6
- require_relative '../sampling/ext'
7
- require_relative '../distributed/headers/b3'
8
- require_relative '../distributed/headers/b3_single'
9
- require_relative '../distributed/headers/datadog'
10
-
11
- require_relative '../trace_digest'
12
- require_relative '../trace_operation'
3
+ require_relative '../contrib/http/distributed/propagation'
13
4
 
14
5
  module Datadog
15
6
  module Tracing
16
7
  module Propagation
17
8
  # Propagation::HTTP helps extracting and injecting HTTP headers.
9
+ # DEV-2.0: This file has been moved to Contrib. Should be deleted in the next release.
18
10
  # @public_api
19
- module HTTP
20
- PROPAGATION_STYLES = {
21
- Configuration::Ext::Distributed::PROPAGATION_STYLE_B3 => Distributed::Headers::B3,
22
- Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER => Distributed::Headers::B3Single,
23
- Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG => Distributed::Headers::Datadog
24
- }.freeze
25
-
26
- # inject! populates the env with span ID, trace ID and sampling priority
27
- #
28
- # DEV-2.0: inject! should work without arguments, injecting the active_trace's digest
29
- # DEV-2.0: and returning a new Hash with the injected headers.
30
- # DEV-2.0: inject! should also accept either a `trace` or a `digest`, as a `trace`
31
- # DEV-2.0: argument is the common use case, but also allows us to set error tags in the `trace`
32
- # DEV-2.0: if needed.
33
- # DEV-2.0: Ideally, we'd have a separate stream to report tracer errors and never
34
- # DEV-2.0: touch the active span.
35
- def self.inject!(digest, env)
36
- # Prevent propagation from being attempted if trace headers provided are nil.
37
- if digest.nil?
38
- Datadog.logger.debug(
39
- 'Cannot inject trace headers into env to propagate over HTTP: trace headers are nil.'.freeze
40
- )
41
- return
42
- end
43
-
44
- digest = digest.to_digest if digest.is_a?(TraceOperation)
45
-
46
- # Inject all configured propagation styles
47
- Datadog.configuration.tracing.distributed_tracing.propagation_inject_style.each do |style|
48
- propagator = PROPAGATION_STYLES[style]
49
- begin
50
- propagator.inject!(digest, env) unless propagator.nil?
51
- rescue => e
52
- Datadog.logger.error(
53
- 'Error injecting propagated trace headers into the environment. ' \
54
- "Cause: #{e} Location: #{Array(e.backtrace).first}"
55
- )
56
- end
57
- end
58
- end
59
-
60
- # extract returns trace headers containing the span ID, trace ID and
61
- # sampling priority defined in env.
62
- def self.extract(env)
63
- trace_digest = nil
64
- dd_trace_digest = nil
65
-
66
- Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style|
67
- propagator = PROPAGATION_STYLES[style]
68
- next if propagator.nil?
69
-
70
- # Extract trace headers
71
- # DEV: `propagator.extract` will return `nil`, where `Propagation::HTTP#extract` will not
72
- begin
73
- extracted_trace_digest = propagator.extract(env)
74
- rescue => e
75
- Datadog.logger.error(
76
- 'Error extracting propagated trace headers from the environment. ' \
77
- "Cause: #{e} Location: #{Array(e.backtrace).first}"
78
- )
79
- end
80
-
81
- # Skip this style if no valid headers were found
82
- next if extracted_trace_digest.nil?
83
-
84
- # Keep track of the Datadog extract trace headers, we want to return
85
- # this one if we have one
86
- if extracted_trace_digest && style == Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG
87
- dd_trace_digest = extracted_trace_digest
88
- end
89
-
90
- # No previously extracted trace headers, use the one we just extracted
91
- if trace_digest.nil?
92
- trace_digest = extracted_trace_digest
93
- else
94
- unless trace_digest.trace_id == extracted_trace_digest.trace_id \
95
- && trace_digest.span_id == extracted_trace_digest.span_id
96
- # Return an empty/new trace headers if we have a mismatch in values extracted
97
- msg = "#{trace_digest.trace_id} != #{extracted_trace_digest.trace_id} && " \
98
- "#{trace_digest.span_id} != #{extracted_trace_digest.span_id}"
99
- Datadog.logger.debug(
100
- "Cannot extract trace headers from HTTP: extracted trace headers differ, #{msg}"
101
- )
102
- # DEV: This will return from `self.extract` not this `each` block
103
- return TraceDigest.new
104
- end
105
- end
106
- end
107
-
108
- # Return the extracted trace headers if we found one or else a new empty trace headers
109
- # Always return the Datadog trace headers if one exists since it has more
110
- # information than the B3 headers e.g. origin, expanded priority
111
- # sampling values, etc
112
- dd_trace_digest || trace_digest || nil
113
- end
114
- end
11
+ HTTP = Tracing::Contrib::HTTP::Distributed::Propagation.new
115
12
  end
116
13
  end
117
14
  end
@@ -85,6 +85,17 @@ module Datadog
85
85
  @priority_sampler.update(rate_by_service, decision: decision)
86
86
  end
87
87
 
88
+ # Check if the Priority Sampling decision is to keep or drop the trace.
89
+ # Other factors can influence the sampling decision; this method is only
90
+ # responsible for interpreting the Sampling Priority decision.
91
+ #
92
+ # @param priority_sampling [Integer] priority sampling number
93
+ # @return [Boolean] true if trace is "kept" by priority sampling
94
+ # @return [Boolean] false if trace is "dropped" by priority sampling
95
+ def self.sampled?(priority_sampling)
96
+ priority_sampling >= Ext::Priority::AUTO_KEEP
97
+ end
98
+
88
99
  private
89
100
 
90
101
  def pre_sample?(trace)
@@ -3,7 +3,7 @@
3
3
  require_relative '../../core'
4
4
 
5
5
  require_relative 'sampler'
6
- require_relative '../span'
6
+ require_relative '../utils'
7
7
 
8
8
  module Datadog
9
9
  module Tracing
@@ -49,11 +49,11 @@ module Datadog
49
49
 
50
50
  def sample_rate=(sample_rate)
51
51
  @sample_rate = sample_rate
52
- @sampling_id_threshold = sample_rate * Tracing::Span::EXTERNAL_MAX_ID
52
+ @sampling_id_threshold = sample_rate * Tracing::Utils::EXTERNAL_MAX_ID
53
53
  end
54
54
 
55
55
  def sample?(trace)
56
- ((trace.id * KNUTH_FACTOR) % Tracing::Span::EXTERNAL_MAX_ID) <= @sampling_id_threshold
56
+ ((trace.id * KNUTH_FACTOR) % Tracing::Utils::EXTERNAL_MAX_ID) <= @sampling_id_threshold
57
57
  end
58
58
 
59
59
  def sample!(trace)
@@ -2,8 +2,8 @@
2
2
 
3
3
  # typed: true
4
4
 
5
- require_relative '../core/utils'
6
5
  require_relative '../core/utils/safe_dup'
6
+ require_relative 'utils'
7
7
 
8
8
  require_relative 'metadata/ext'
9
9
  require_relative 'metadata'
@@ -19,22 +19,6 @@ module Datadog
19
19
  class Span
20
20
  include Metadata
21
21
 
22
- # The max value for a {Datadog::Tracing::Span} identifier.
23
- # Span and trace identifiers should be strictly positive and strictly inferior to this limit.
24
- #
25
- # Limited to +2<<62-1+ positive integers, as Ruby is able to represent such numbers "inline",
26
- # inside a +VALUE+ scalar, thus not requiring memory allocation.
27
- #
28
- # The range of IDs also has to consider portability across different languages and platforms.
29
- RUBY_MAX_ID = (1 << 62) - 1
30
-
31
- # Excludes zero from possible values
32
- RUBY_ID_RANGE = (1..RUBY_MAX_ID).freeze
33
-
34
- # While we only generate 63-bit integers due to limitations in other languages, we support
35
- # parsing 64-bit integers for distributed tracing since an upstream system may generate one
36
- EXTERNAL_MAX_ID = 1 << 64
37
-
38
22
  attr_accessor \
39
23
  :end_time,
40
24
  :id,
@@ -90,9 +74,9 @@ module Datadog
90
74
  @resource = Core::Utils::SafeDup.frozen_or_dup(resource)
91
75
  @type = Core::Utils::SafeDup.frozen_or_dup(type)
92
76
 
93
- @id = id || Core::Utils.next_id
77
+ @id = id || Tracing::Utils.next_id
94
78
  @parent_id = parent_id || 0
95
- @trace_id = trace_id || Core::Utils.next_id
79
+ @trace_id = trace_id || Tracing::Utils.next_id
96
80
 
97
81
  @meta = meta || {}
98
82
  @metrics = metrics || {}
@@ -7,10 +7,11 @@ require_relative '../core/environment/identity'
7
7
  require_relative '../core/utils'
8
8
  require_relative '../core/utils/time'
9
9
 
10
- require_relative 'metadata/ext'
11
10
  require_relative 'event'
12
- require_relative 'span'
13
11
  require_relative 'metadata'
12
+ require_relative 'metadata/ext'
13
+ require_relative 'span'
14
+ require_relative 'utils'
14
15
 
15
16
  module Datadog
16
17
  module Tracing
@@ -61,9 +62,9 @@ module Datadog
61
62
  self.type = type
62
63
  self.resource = resource
63
64
 
64
- @id = Core::Utils.next_id
65
+ @id = Tracing::Utils.next_id
65
66
  @parent_id = parent_id || 0
66
- @trace_id = trace_id || Core::Utils.next_id
67
+ @trace_id = trace_id || Tracing::Utils.next_id
67
68
 
68
69
  @status = 0
69
70