ddtrace 1.6.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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