ddtrace 1.7.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -1
  3. data/README.md +2 -2
  4. data/ext/ddtrace_profiling_loader/extconf.rb +4 -1
  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 +24 -50
  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 +284 -74
  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 +32 -32
  15. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +2 -2
  16. data/ext/ddtrace_profiling_native_extension/extconf.rb +21 -7
  17. data/ext/ddtrace_profiling_native_extension/helpers.h +5 -0
  18. data/ext/ddtrace_profiling_native_extension/http_transport.c +50 -49
  19. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +5 -1
  20. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +42 -12
  21. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +116 -22
  22. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +9 -0
  23. data/ext/ddtrace_profiling_native_extension/profiling.c +205 -0
  24. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +86 -0
  25. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +28 -6
  26. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +23 -4
  27. data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +4 -0
  28. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +47 -50
  29. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +4 -4
  30. data/ext/ddtrace_profiling_native_extension/time_helpers.c +17 -0
  31. data/ext/ddtrace_profiling_native_extension/time_helpers.h +10 -0
  32. data/lib/datadog/appsec/assets/waf_rules/recommended.json +75 -8
  33. data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
  34. data/lib/datadog/appsec/assets/waf_rules/strict.json +1 -1
  35. data/lib/datadog/appsec/assets.rb +1 -1
  36. data/lib/datadog/appsec/configuration/settings.rb +35 -22
  37. data/lib/datadog/appsec/configuration.rb +4 -2
  38. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  39. data/lib/datadog/appsec/contrib/configuration/settings.rb +1 -1
  40. data/lib/datadog/appsec/contrib/integration.rb +1 -1
  41. data/lib/datadog/appsec/contrib/patcher.rb +1 -1
  42. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +1 -1
  43. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  44. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
  46. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
  48. data/lib/datadog/appsec/contrib/rack/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/rack/response.rb +1 -1
  50. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +1 -1
  51. data/lib/datadog/appsec/contrib/rails/ext.rb +1 -1
  52. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -1
  53. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +1 -1
  54. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
  55. data/lib/datadog/appsec/contrib/rails/request.rb +1 -1
  56. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +1 -1
  57. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +1 -1
  58. data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -1
  59. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -1
  60. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
  61. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +1 -1
  62. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +1 -1
  63. data/lib/datadog/appsec/event.rb +1 -1
  64. data/lib/datadog/appsec/extensions.rb +36 -26
  65. data/lib/datadog/appsec/instrumentation/gateway.rb +3 -3
  66. data/lib/datadog/appsec/processor.rb +15 -19
  67. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  68. data/lib/datadog/appsec/reactive/address_hash.rb +1 -1
  69. data/lib/datadog/appsec/reactive/engine.rb +1 -1
  70. data/lib/datadog/appsec/reactive/operation.rb +2 -2
  71. data/lib/datadog/appsec/reactive/subscriber.rb +1 -1
  72. data/lib/datadog/appsec/response.rb +18 -9
  73. data/lib/datadog/appsec/utils/http/media_range.rb +201 -0
  74. data/lib/datadog/appsec/utils/http/media_type.rb +87 -0
  75. data/lib/datadog/appsec/utils/http.rb +9 -0
  76. data/lib/datadog/appsec/utils.rb +7 -0
  77. data/lib/datadog/appsec.rb +1 -1
  78. data/lib/datadog/ci/ext/environment.rb +57 -13
  79. data/lib/datadog/core/configuration/agent_settings_resolver.rb +2 -2
  80. data/lib/datadog/core/configuration/base.rb +3 -0
  81. data/lib/datadog/core/configuration/components.rb +27 -6
  82. data/lib/datadog/core/configuration/ext.rb +26 -0
  83. data/lib/datadog/core/configuration/option_definition.rb +11 -2
  84. data/lib/datadog/core/configuration/settings.rb +16 -341
  85. data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
  86. data/lib/datadog/core/diagnostics/health.rb +4 -22
  87. data/lib/datadog/core/environment/variable_helpers.rb +58 -10
  88. data/lib/datadog/core/metrics/client.rb +3 -2
  89. data/lib/datadog/core/metrics/ext.rb +0 -2
  90. data/lib/datadog/core/telemetry/collector.rb +1 -0
  91. data/lib/datadog/core/utils.rb +0 -21
  92. data/lib/datadog/core.rb +21 -1
  93. data/lib/datadog/kit/appsec/events.rb +75 -0
  94. data/lib/datadog/kit/enable_core_dumps.rb +1 -0
  95. data/lib/datadog/kit/identity.rb +8 -7
  96. data/lib/datadog/opentelemetry/api/context.rb +187 -0
  97. data/lib/datadog/opentelemetry/api/trace/span.rb +15 -0
  98. data/lib/datadog/opentelemetry/sdk/configurator.rb +38 -0
  99. data/lib/datadog/opentelemetry/sdk/id_generator.rb +27 -0
  100. data/lib/datadog/opentelemetry/sdk/propagator.rb +91 -0
  101. data/lib/datadog/opentelemetry/sdk/span_processor.rb +92 -0
  102. data/lib/datadog/opentelemetry.rb +48 -0
  103. data/lib/datadog/opentracer/distributed_headers.rb +2 -2
  104. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +16 -5
  105. data/lib/datadog/profiling/collectors/dynamic_sampling_rate.rb +14 -0
  106. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +68 -0
  107. data/lib/datadog/profiling/stack_recorder.rb +14 -0
  108. data/lib/datadog/profiling.rb +2 -0
  109. data/lib/datadog/tracing/configuration/ext.rb +33 -4
  110. data/lib/datadog/tracing/configuration/settings.rb +433 -0
  111. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +4 -1
  112. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -0
  113. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +4 -1
  114. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  115. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +5 -1
  116. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +1 -0
  117. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +6 -1
  118. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  119. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -1
  120. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  121. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -1
  122. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  123. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +6 -1
  124. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +9 -4
  125. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  126. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -1
  127. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +10 -3
  128. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +9 -4
  129. data/lib/datadog/tracing/contrib/http/ext.rb +2 -0
  130. data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -6
  131. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -1
  132. data/lib/datadog/tracing/contrib/httpclient/ext.rb +2 -0
  133. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +3 -4
  134. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -1
  135. data/lib/datadog/tracing/contrib/httprb/ext.rb +2 -0
  136. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +3 -4
  137. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +5 -1
  138. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  139. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +4 -1
  140. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  141. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +2 -2
  142. data/lib/datadog/tracing/contrib/patcher.rb +3 -2
  143. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +4 -1
  144. data/lib/datadog/tracing/contrib/pg/ext.rb +1 -0
  145. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +56 -33
  146. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +4 -1
  147. data/lib/datadog/tracing/contrib/presto/ext.rb +1 -0
  148. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +1 -0
  149. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -12
  150. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +4 -1
  151. data/lib/datadog/tracing/contrib/redis/ext.rb +1 -0
  152. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +30 -23
  153. data/lib/datadog/tracing/contrib/redis/integration.rb +34 -2
  154. data/lib/datadog/tracing/contrib/redis/patcher.rb +18 -14
  155. data/lib/datadog/tracing/contrib/redis/quantize.rb +12 -9
  156. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -6
  157. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +72 -0
  158. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  159. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  160. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +33 -0
  161. data/lib/datadog/tracing/contrib/stripe/ext.rb +26 -0
  162. data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
  163. data/lib/datadog/tracing/contrib/stripe/patcher.rb +29 -0
  164. data/lib/datadog/tracing/contrib/stripe/request.rb +67 -0
  165. data/lib/datadog/tracing/contrib.rb +1 -0
  166. data/lib/datadog/{core → tracing}/diagnostics/ext.rb +1 -6
  167. data/lib/datadog/tracing/diagnostics/health.rb +40 -0
  168. data/lib/datadog/tracing/distributed/{b3.rb → b3_multi.rb} +2 -2
  169. data/lib/datadog/tracing/distributed/helpers.rb +2 -1
  170. data/lib/datadog/tracing/distributed/none.rb +19 -0
  171. data/lib/datadog/tracing/distributed/trace_context.rb +378 -0
  172. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  173. data/lib/datadog/tracing/metadata/tagging.rb +6 -0
  174. data/lib/datadog/tracing/sampling/priority_sampler.rb +11 -0
  175. data/lib/datadog/tracing/sampling/rate_sampler.rb +3 -3
  176. data/lib/datadog/tracing/span.rb +3 -19
  177. data/lib/datadog/tracing/span_operation.rb +5 -4
  178. data/lib/datadog/tracing/trace_digest.rb +85 -2
  179. data/lib/datadog/tracing/trace_operation.rb +13 -4
  180. data/lib/datadog/tracing/utils.rb +50 -0
  181. data/lib/ddtrace/version.rb +1 -1
  182. metadata +41 -9
@@ -0,0 +1,378 @@
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, unknown_fields = 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_flags: trace_flags,
58
+ trace_state: tracestate,
59
+ trace_state_unknown_fields: unknown_fields,
60
+ )
61
+ end
62
+
63
+ private
64
+
65
+ # Refinements to ensure newer rubies do not suffer performance impact
66
+ # by needing to use older APIs.
67
+ module Refine
68
+ # Backport `Regexp::match?` because it is measurably the most performant
69
+ # way to check if a string matches a regular expression.
70
+ unless Regexp.method_defined?(:match?)
71
+ refine ::Regexp do
72
+ def match?(*args)
73
+ !match(*args).nil?
74
+ end
75
+ end
76
+ end
77
+
78
+ unless String.method_defined?(:delete_prefix)
79
+ refine ::String do
80
+ def delete_prefix(prefix)
81
+ prefix = prefix.to_s
82
+ if rindex(prefix, 0)
83
+ self[prefix.length..-1]
84
+ else
85
+ dup
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ using Refine
92
+
93
+ # @see https://www.w3.org/TR/trace-context/#traceparent-header
94
+ def build_traceparent(digest)
95
+ build_traceparent_string(
96
+ digest.trace_distributed_id || digest.trace_id,
97
+ digest.span_id,
98
+ build_trace_flags(digest)
99
+ )
100
+ end
101
+
102
+ # For the current version (00), the traceparent has the following format:
103
+ #
104
+ # `"#{version}-#{trace_id}-#{parent_id}-#{trace_flags}"`
105
+ #
106
+ # Where:
107
+ # * `version`: 2 hex-encoded digits, zero padded.
108
+ # * `trace_id`: 32 hex-encoded digits, zero padded.
109
+ # * `parent_id`: 16 hex-encoded digits, zero padded.
110
+ # * `trace_flags`: 2 hex-encoded digits, zero padded.
111
+ #
112
+ # All hex values should be lowercase.
113
+ #
114
+ # @param trace_id [Integer] 128-bit
115
+ # @param parent_id [Integer] 64-bit
116
+ # @param trace_flags [Integer] 8-bit
117
+ def build_traceparent_string(trace_id, parent_id, trace_flags)
118
+ "00-#{format('%032x', trace_id)}-#{format('%016x', parent_id)}-#{format('%02x', trace_flags)}"
119
+ end
120
+
121
+ # Sets the trace flag to an existing `trace_flags`.
122
+ def build_trace_flags(digest)
123
+ trace_flags = digest.trace_flags || DEFAULT_TRACE_FLAGS
124
+
125
+ if digest.trace_sampling_priority
126
+ if Tracing::Sampling::PrioritySampler.sampled?(digest.trace_sampling_priority)
127
+ trace_flags |= TRACE_FLAGS_SAMPLED
128
+ else
129
+ trace_flags &= ~TRACE_FLAGS_SAMPLED
130
+ end
131
+ end
132
+
133
+ trace_flags
134
+ end
135
+
136
+ # @see https://www.w3.org/TR/trace-context/#tracestate-header
137
+ def build_tracestate(digest)
138
+ tracestate = String.new('dd=')
139
+ tracestate << "s:#{digest.trace_sampling_priority};" if digest.trace_sampling_priority
140
+ tracestate << "o:#{serialize_origin(digest.trace_origin)};" if digest.trace_origin
141
+
142
+ if digest.trace_distributed_tags
143
+ digest.trace_distributed_tags.each do |name, value|
144
+ tag = "t.#{serialize_tag_key(name)}:#{serialize_tag_value(value)};"
145
+
146
+ # If tracestate size limit is exceed, drop the remaining data.
147
+ # String#bytesize is used because only ASCII characters are allowed.
148
+ #
149
+ # We add 1 to the limit because of the trailing comma, which will be removed before returning.
150
+ break if tracestate.bytesize + tag.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
151
+
152
+ tracestate << tag
153
+ end
154
+ end
155
+
156
+ tracestate << digest.trace_state_unknown_fields if digest.trace_state_unknown_fields
157
+
158
+ # Is there any Datadog-specific information to propagate.
159
+ # Check for > 3 size because the empty prefix `dd=` has 3 characters.
160
+ if tracestate.size > 3
161
+ # Propagate upstream tracestate with `dd=...` appended to the list
162
+ tracestate.chop! # Removes trailing `;` from Datadog trace state string.
163
+
164
+ if digest.trace_state
165
+ # Delete existing `dd=` tracestate fields, if present.
166
+ vendors = split_tracestate(digest.trace_state)
167
+ vendors.reject! { |v| v.start_with?('dd=') }
168
+ end
169
+
170
+ if vendors && !vendors.empty?
171
+ # Ensure the list has at most 31 elements, as we need to prepend Datadog's
172
+ # entry and the limit is 32 elements total.
173
+ vendors = vendors[0..30]
174
+ "#{tracestate},#{vendors.join(',')}"
175
+ else
176
+ tracestate.to_s
177
+ end
178
+ else
179
+ digest.trace_state # Propagate upstream tracestate with no Datadog changes
180
+ end
181
+ end
182
+
183
+ # If any characters in <origin_value> are invalid, replace each invalid character with 0x5F (underscore).
184
+ # Invalid characters are: characters outside the ASCII range 0x20 to 0x7E, 0x2C (comma), and 0x3D (equals).
185
+ def serialize_origin(value)
186
+ # DEV: It's unlikely that characters will be out of range, as they mostly
187
+ # DEV: come from Datadog-controlled sources.
188
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
189
+ if INVALID_ORIGIN_CHARS.match?(value)
190
+ value.gsub(INVALID_ORIGIN_CHARS, '_')
191
+ else
192
+ value
193
+ end
194
+ end
195
+
196
+ # Serialize `_dd.p.{key}` by first removing the `_dd.p.` prefix.
197
+ # Then replacing invalid characters with `_`.
198
+ def serialize_tag_key(name)
199
+ key = name.delete_prefix(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX)
200
+
201
+ # DEV: It's unlikely that characters will be out of range, as they mostly
202
+ # DEV: come from Datadog-controlled sources.
203
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
204
+ if INVALID_TAG_KEY_CHARS.match?(key)
205
+ key.gsub(INVALID_TAG_KEY_CHARS, '_')
206
+ else
207
+ key
208
+ end
209
+ end
210
+
211
+ # Replaces invalid characters with `_`, then replaces `=` with `:`.
212
+ def serialize_tag_value(value)
213
+ # DEV: It's unlikely that characters will be out of range, as they mostly
214
+ # DEV: come from Datadog-controlled sources.
215
+ # DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
216
+ ret = if INVALID_TAG_VALUE_CHARS.match?(value)
217
+ value.gsub(INVALID_TAG_VALUE_CHARS, '_')
218
+ else
219
+ value
220
+ end
221
+
222
+ # DEV: Checking for an unlikely '=' is faster than a no-op `tr!`.
223
+ ret.tr!('=', ':') if ret.include?('=')
224
+ ret
225
+ end
226
+
227
+ def extract_traceparent(traceparent)
228
+ trace_id, parent_id, trace_flags = parse_traceparent_string(traceparent)
229
+
230
+ # Return unless all traceparent fields are valid.
231
+ return unless trace_id && !trace_id.zero? && parent_id && !parent_id.zero? && trace_flags
232
+
233
+ dd_trace_id = parse_datadog_trace_id(trace_id)
234
+ sampled = parse_sampled_flag(trace_flags)
235
+
236
+ [trace_id, dd_trace_id, parent_id, sampled, trace_flags]
237
+ end
238
+
239
+ def parse_traceparent_string(traceparent)
240
+ return unless traceparent
241
+
242
+ version, trace_id, parent_id, trace_flags, extra = traceparent.strip.split('-')
243
+
244
+ return if version == INVALID_VERSION
245
+
246
+ # Extra fields are not allowed in version 00, but we have to be lenient for future versions.
247
+ return if version == SPEC_VERSION && extra
248
+
249
+ # Invalid field sizes
250
+ return if version.size != 2 || trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
251
+
252
+ [Integer(trace_id, 16), Integer(parent_id, 16), Integer(trace_flags, 16)]
253
+ rescue ArgumentError # Conversion to integer failed
254
+ nil
255
+ end
256
+
257
+ # Datadog only allows 64 bits for the trace id.
258
+ # We extract the lower 64 bits from the original 128-bit id.
259
+ def parse_datadog_trace_id(trace_id)
260
+ trace_id & 0xFFFFFFFFFFFFFFFF
261
+ end
262
+
263
+ def parse_sampled_flag(trace_flags)
264
+ trace_flags & TRACE_FLAGS_SAMPLED
265
+ end
266
+
267
+ # @return [Array<String,Integer,String,Hash>] returns 4 values: tracestate, sampling_priority, origin, tags.
268
+ def extract_tracestate(tracestate)
269
+ return unless tracestate
270
+
271
+ vendors = split_tracestate(tracestate)
272
+
273
+ # Find Datadog's `dd=` tracestate field.
274
+ idx = vendors.index { |v| v.start_with?('dd=') }
275
+ return tracestate unless idx
276
+
277
+ # Delete `dd=` prefix
278
+ dd_tracestate = vendors.delete_at(idx)
279
+ dd_tracestate.slice!(0..2)
280
+
281
+ origin, sampling_priority, tags, unknown_fields = extract_datadog_fields(dd_tracestate)
282
+
283
+ [vendors.join(','), sampling_priority, origin, tags, unknown_fields]
284
+ end
285
+
286
+ def extract_datadog_fields(dd_tracestate)
287
+ sampling_priority = nil
288
+ origin = nil
289
+ tags = nil
290
+ unknown_fields = nil
291
+
292
+ # DEV: Since Ruby 2.6 `split` can receive a block, so `each` can be removed then.
293
+ dd_tracestate.split(';').each do |pair|
294
+ key, value = pair.split(':', 2)
295
+ case key
296
+ when 's'
297
+ sampling_priority = Integer(value) rescue nil
298
+ when 'o'
299
+ origin = value
300
+ when /^t\./
301
+ key.slice!(0..1) # Delete `t.` prefix
302
+
303
+ value = deserialize_tag_value(value)
304
+
305
+ tags ||= {}
306
+ tags["#{Tracing::Metadata::Ext::Distributed::TAGS_PREFIX}#{key}"] = value
307
+ else
308
+ unknown_fields ||= String.new
309
+ unknown_fields << pair
310
+ unknown_fields << ';'
311
+ end
312
+ end
313
+
314
+ [origin, sampling_priority, tags, unknown_fields]
315
+ end
316
+
317
+ # Restore `:` back to `=`.
318
+ def deserialize_tag_value(value)
319
+ value.tr!(':', '=')
320
+ value
321
+ end
322
+
323
+ # If `sampled` and `sampling_priority` disagree, `sampled` overrides the decision.
324
+ # @return [Integer] one of the {Datadog::Tracing::Sampling::Ext::Priority} values
325
+ def parse_priority_sampling(sampled, sampling_priority)
326
+ # If both fields agree
327
+ if sampling_priority &&
328
+ (!Tracing::Sampling::PrioritySampler.sampled?(sampling_priority) && sampled == 0 || # Both drop
329
+ Tracing::Sampling::PrioritySampler.sampled?(sampling_priority) && sampled == 1) # Both keep
330
+
331
+ return sampling_priority # Return the richer `sampling_priority`
332
+ end
333
+
334
+ sampled # Sampled flag trumps `sampling_priority` on conflict
335
+ end
336
+
337
+ def split_tracestate(tracestate)
338
+ tracestate.split(/[ \t]*,[ \t]*/)[0..31]
339
+ end
340
+
341
+ # Version 0xFF is invalid as per spec
342
+ # @see https://www.w3.org/TR/trace-context/#version
343
+ INVALID_VERSION = 'ff'
344
+ private_constant :INVALID_VERSION
345
+
346
+ # Empty 8-bit `trace-flags`.
347
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
348
+ DEFAULT_TRACE_FLAGS = 0b00000000
349
+ private_constant :DEFAULT_TRACE_FLAGS
350
+
351
+ # Bit-mask for `trace-flags` that represents a sampled span (sampled==true).
352
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
353
+ TRACE_FLAGS_SAMPLED = 0b00000001
354
+ private_constant :TRACE_FLAGS_SAMPLED
355
+
356
+ # The limit is inclusive: sizes *greater* than 256 are disallowed.
357
+ # @see https://www.w3.org/TR/trace-context/#value
358
+ TRACESTATE_VALUE_SIZE_LIMIT = 256
359
+ private_constant :TRACESTATE_VALUE_SIZE_LIMIT
360
+
361
+ # Replace all characters with `_`, except ASCII characters 0x20-0x7E.
362
+ # Additionally, `,`, ';', and `=` must also be replaced by `_`.
363
+ INVALID_ORIGIN_CHARS = /[\u0000-\u0019,;=\u007F-\u{10FFFF}]/.freeze
364
+ private_constant :INVALID_ORIGIN_CHARS
365
+
366
+ # Replace all characters with `_`, except ASCII characters 0x21-0x7E.
367
+ # Additionally, `,` and `=` must also be replaced by `_`.
368
+ INVALID_TAG_KEY_CHARS = /[\u0000-\u0020,=\u007F-\u{10FFFF}]/.freeze
369
+ private_constant :INVALID_TAG_KEY_CHARS
370
+
371
+ # Replace all characters with `_`, except ASCII characters 0x20-0x7E.
372
+ # Additionally, `,`, ':' and `;` must also be replaced by `_`.
373
+ INVALID_TAG_VALUE_CHARS = /[\u0000-\u001F,:;\u007F-\u{10FFFF}]/.freeze
374
+ private_constant :INVALID_TAG_VALUE_CHARS
375
+ end
376
+ end
377
+ end
378
+ 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
@@ -110,6 +110,12 @@ module Datadog
110
110
  metrics.delete(key)
111
111
  end
112
112
 
113
+ # Returns a copy of all metadata.
114
+ # Keys for `@meta` and `@metrics` don't collide, by construction.
115
+ def tags
116
+ @meta.merge(@metrics)
117
+ end
118
+
113
119
  protected
114
120
 
115
121
  def meta
@@ -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
 
@@ -6,6 +6,77 @@ module Datadog
6
6
  # Used to propagate context and continue traces across execution boundaries.
7
7
  # @public_api
8
8
  class TraceDigest
9
+ # @!attribute [r] span_id
10
+ # Datadog id for the currently active span.
11
+ # @return [Integer]
12
+ # @!attribute [r] span_name
13
+ # The operation name of the currently active span.
14
+ # @return [String]
15
+ # @!attribute [r] span_resource
16
+ # The resource name of the currently active span.
17
+ # @return [String]
18
+ # @!attribute [r] span_service
19
+ # The service of the currently active span.
20
+ # @return [String]
21
+ # @!attribute [r] span_type
22
+ # The type of the currently active span.
23
+ # @return [String]
24
+ # @!attribute [r] trace_distributed_tags
25
+ # Datadog-specific tags that support richer distributed tracing association.
26
+ # @return [Hash<String,String>]
27
+ # @!attribute [r] trace_hostname
28
+ # The hostname of the currently active trace. Use to attribute traces to hosts.
29
+ # @return [String]
30
+ # @!attribute [r] trace_id
31
+ # Datadog id for the currently active trace.
32
+ # @return [Integer]
33
+ # @!attribute [r] trace_name
34
+ # Operation name for the currently active trace.
35
+ # @return [Integer]
36
+ # @!attribute [r] trace_origin
37
+ # Datadog-specific attribution of this trace's creation.
38
+ # @return [String]
39
+ # @!attribute [r] trace_process_id
40
+ # The OS-specific process id.
41
+ # @return [Integer]
42
+ # @!attribute [r] trace_resource
43
+ # The resource name of the currently active trace.
44
+ # @return [String]
45
+ # @!attribute [r] trace_runtime_id
46
+ # Unique id to this Ruby process. Used to differentiate traces coming from
47
+ # child processes forked from same parent process.
48
+ # @return [String]
49
+ # @!attribute [r] trace_sampling_priority
50
+ # Datadog-specific sampling decision for the currently active trace.
51
+ # @return [Integer]
52
+ # @!attribute [r] trace_service
53
+ # The service of the currently active trace.
54
+ # @return [String]
55
+ # @!attribute [r] trace_distributed_id
56
+ # The trace id extracted from a distributed context, if different from `trace_id`.
57
+ #
58
+ # The current use case is when the distributed context has a trace id integer larger than 64-bit:
59
+ # This attribute will preserve the original id, while `trace_id` will only contain the lower 64 bits.
60
+ # @return [Integer]
61
+ # @see https://www.w3.org/TR/trace-context/#trace-id
62
+ # @!attribute [r] trace_flags
63
+ # The W3C "trace-flags" extracted from a distributed context. This field is an 8-bit unsigned integer.
64
+ # @return [Integer]
65
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
66
+ # @!attribute [r] trace_state
67
+ # The W3C "tracestate" extracted from a distributed context.
68
+ # This field is a string representing vendor-specific distribution data.
69
+ # The `dd=` entry is removed from `trace_state` as its value is dynamically calculated
70
+ # on every propagation injection.
71
+ # @return [String]
72
+ # @see https://www.w3.org/TR/trace-context/#tracestate-header
73
+ # @!attribute [r] trace_state_unknown_fields
74
+ # From W3C "tracestate"'s `dd=` entry, when keys are not recognized they are stored here long with their values.
75
+ # This allows later propagation to include those unknown fields, as they can represent future versions of the spec
76
+ # sending data through this service. This value ends in a trailing `;` to facilitate serialization.
77
+ # @return [String]
78
+ # TODO: The documentation for the last attribute above won't be rendered.
79
+ # TODO: This might be a YARD bug as adding an attribute, making it now second-last attribute, renders correctly.
9
80
  attr_reader \
10
81
  :span_id,
11
82
  :span_name,
@@ -21,7 +92,11 @@ module Datadog
21
92
  :trace_resource,
22
93
  :trace_runtime_id,
23
94
  :trace_sampling_priority,
24
- :trace_service
95
+ :trace_service,
96
+ :trace_distributed_id,
97
+ :trace_flags,
98
+ :trace_state,
99
+ :trace_state_unknown_fields
25
100
 
26
101
  def initialize(
27
102
  span_id: nil,
@@ -38,7 +113,11 @@ module Datadog
38
113
  trace_resource: nil,
39
114
  trace_runtime_id: nil,
40
115
  trace_sampling_priority: nil,
41
- trace_service: nil
116
+ trace_service: nil,
117
+ trace_distributed_id: nil,
118
+ trace_flags: nil,
119
+ trace_state: nil,
120
+ trace_state_unknown_fields: nil
42
121
  )
43
122
  @span_id = span_id
44
123
  @span_name = span_name && span_name.dup.freeze
@@ -55,6 +134,10 @@ module Datadog
55
134
  @trace_runtime_id = trace_runtime_id && trace_runtime_id.dup.freeze
56
135
  @trace_sampling_priority = trace_sampling_priority
57
136
  @trace_service = trace_service && trace_service.dup.freeze
137
+ @trace_distributed_id = trace_distributed_id
138
+ @trace_flags = trace_flags
139
+ @trace_state = trace_state && trace_state.dup.freeze
140
+ @trace_state_unknown_fields = trace_state_unknown_fields && trace_state_unknown_fields.dup.freeze
58
141
 
59
142
  freeze
60
143
  end
@@ -4,12 +4,13 @@ require_relative '../core'
4
4
  require_relative '../core/environment/identity'
5
5
  require_relative '../core/utils'
6
6
 
7
- require_relative 'sampling/ext'
8
7
  require_relative 'event'
8
+ require_relative 'metadata/tagging'
9
+ require_relative 'sampling/ext'
9
10
  require_relative 'span_operation'
10
- require_relative 'trace_segment'
11
11
  require_relative 'trace_digest'
12
- require_relative 'metadata/tagging'
12
+ require_relative 'trace_segment'
13
+ require_relative 'utils'
13
14
 
14
15
  module Datadog
15
16
  module Tracing
@@ -70,7 +71,7 @@ module Datadog
70
71
  metrics: nil
71
72
  )
72
73
  # Attributes
73
- @id = id || Core::Utils.next_id
74
+ @id = id || Tracing::Utils.next_id
74
75
  @max_length = max_length || DEFAULT_MAX_LENGTH
75
76
  @parent_span_id = parent_span_id
76
77
  @sampled = sampled.nil? ? true : sampled
@@ -462,6 +463,14 @@ module Datadog
462
463
  def distributed_tags
463
464
  meta.select { |name, _| name.start_with?(Metadata::Ext::Distributed::TAGS_PREFIX) }
464
465
  end
466
+
467
+ def reset
468
+ @root_span = nil
469
+ @active_span = nil
470
+ @active_span_count = 0
471
+ @finished = false
472
+ @spans = []
473
+ end
465
474
  end
466
475
  end
467
476
  end