ddtrace 1.8.0 → 1.9.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -1
  3. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +10 -10
  4. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +32 -32
  5. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +2 -2
  6. data/ext/ddtrace_profiling_native_extension/http_transport.c +50 -49
  7. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +5 -1
  8. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +34 -12
  9. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +10 -0
  10. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +32 -32
  11. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +4 -4
  12. data/lib/datadog/appsec/assets/waf_rules/recommended.json +75 -8
  13. data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
  14. data/lib/datadog/appsec/assets/waf_rules/strict.json +1 -1
  15. data/lib/datadog/appsec/assets.rb +1 -1
  16. data/lib/datadog/appsec/configuration/settings.rb +35 -22
  17. data/lib/datadog/appsec/configuration.rb +4 -2
  18. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  19. data/lib/datadog/appsec/contrib/configuration/settings.rb +1 -1
  20. data/lib/datadog/appsec/contrib/integration.rb +1 -1
  21. data/lib/datadog/appsec/contrib/patcher.rb +1 -1
  22. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +1 -1
  23. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  24. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +1 -1
  25. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
  26. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
  27. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
  28. data/lib/datadog/appsec/contrib/rack/request.rb +1 -1
  29. data/lib/datadog/appsec/contrib/rack/response.rb +1 -1
  30. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +1 -1
  31. data/lib/datadog/appsec/contrib/rails/ext.rb +1 -1
  32. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -1
  33. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +1 -1
  34. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
  35. data/lib/datadog/appsec/contrib/rails/request.rb +1 -1
  36. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +1 -1
  37. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +1 -1
  38. data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -1
  39. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -1
  40. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
  41. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +1 -1
  42. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +1 -1
  43. data/lib/datadog/appsec/event.rb +1 -1
  44. data/lib/datadog/appsec/extensions.rb +36 -26
  45. data/lib/datadog/appsec/instrumentation/gateway.rb +3 -3
  46. data/lib/datadog/appsec/processor.rb +15 -19
  47. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  48. data/lib/datadog/appsec/reactive/address_hash.rb +1 -1
  49. data/lib/datadog/appsec/reactive/engine.rb +1 -1
  50. data/lib/datadog/appsec/reactive/operation.rb +2 -2
  51. data/lib/datadog/appsec/reactive/subscriber.rb +1 -1
  52. data/lib/datadog/appsec/response.rb +18 -9
  53. data/lib/datadog/appsec/utils/http/media_range.rb +201 -0
  54. data/lib/datadog/appsec/utils/http/media_type.rb +87 -0
  55. data/lib/datadog/appsec/utils/http.rb +9 -0
  56. data/lib/datadog/appsec/utils.rb +7 -0
  57. data/lib/datadog/appsec.rb +1 -1
  58. data/lib/datadog/ci/ext/environment.rb +57 -13
  59. data/lib/datadog/core/configuration/agent_settings_resolver.rb +2 -2
  60. data/lib/datadog/core/configuration/base.rb +3 -0
  61. data/lib/datadog/core/configuration/ext.rb +8 -0
  62. data/lib/datadog/core/configuration/option_definition.rb +11 -2
  63. data/lib/datadog/core/configuration/settings.rb +6 -4
  64. data/lib/datadog/core/diagnostics/environment_logger.rb +4 -3
  65. data/lib/datadog/core/metrics/client.rb +3 -2
  66. data/lib/datadog/core/metrics/ext.rb +0 -2
  67. data/lib/datadog/core/telemetry/collector.rb +1 -0
  68. data/lib/datadog/kit/appsec/events.rb +75 -0
  69. data/lib/datadog/kit/enable_core_dumps.rb +1 -0
  70. data/lib/datadog/kit/identity.rb +8 -7
  71. data/lib/datadog/opentelemetry/api/context.rb +187 -0
  72. data/lib/datadog/opentelemetry/api/trace/span.rb +15 -0
  73. data/lib/datadog/opentelemetry/sdk/configurator.rb +38 -0
  74. data/lib/datadog/opentelemetry/sdk/id_generator.rb +27 -0
  75. data/lib/datadog/opentelemetry/sdk/propagator.rb +91 -0
  76. data/lib/datadog/opentelemetry/sdk/span_processor.rb +92 -0
  77. data/lib/datadog/opentelemetry.rb +48 -0
  78. data/lib/datadog/tracing/configuration/ext.rb +1 -2
  79. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -0
  80. data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +10 -3
  81. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  82. data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -6
  83. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -0
  84. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  85. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +3 -4
  86. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -0
  87. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  88. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +3 -4
  89. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +44 -31
  90. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +33 -0
  91. data/lib/datadog/tracing/contrib/stripe/ext.rb +26 -0
  92. data/lib/datadog/tracing/contrib/stripe/integration.rb +43 -0
  93. data/lib/datadog/tracing/contrib/stripe/patcher.rb +29 -0
  94. data/lib/datadog/tracing/contrib/stripe/request.rb +67 -0
  95. data/lib/datadog/tracing/contrib.rb +1 -0
  96. data/lib/datadog/tracing/distributed/trace_context.rb +16 -7
  97. data/lib/datadog/tracing/metadata/tagging.rb +6 -0
  98. data/lib/datadog/tracing/trace_digest.rb +17 -7
  99. data/lib/datadog/tracing/trace_operation.rb +8 -0
  100. data/lib/ddtrace/version.rb +1 -1
  101. metadata +23 -6
@@ -0,0 +1,75 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../identity'
5
+
6
+ module Datadog
7
+ module Kit
8
+ module AppSec
9
+ # Tracking events
10
+ module Events
11
+ LOGIN_SUCCESS_EVENT = 'users.login.success'
12
+ LOGIN_FAILURE_EVENT = 'users.login.failure'
13
+
14
+ # Attach login success event information to the trace
15
+ #
16
+ # This method is experimental and may change in the future.
17
+ #
18
+ # @param trace [TraceOperation] Trace to attach data to.
19
+ # @param user [Hash<Symbol, String>] User information to pass to
20
+ # Datadog::Kit::Identity.set_user. Must contain at least :id as key.
21
+ # @param others [Hash<String || Symbol, String>] Additional free-form
22
+ # event information to attach to the trace.
23
+ def self.track_login_success(trace, user:, **others)
24
+ track(LOGIN_SUCCESS_EVENT, trace, **others)
25
+
26
+ user_options = user.dup
27
+ user_id = user.delete(:id)
28
+
29
+ raise ArgumentError, 'missing required key: :user => { :id }' if user_id.nil?
30
+
31
+ Kit::Identity.set_user(trace, id: user_id, **user_options)
32
+ end
33
+
34
+ # Attach login failure event information to the trace
35
+ #
36
+ # This method is experimental and may change in the future.
37
+ #
38
+ # @param trace [TraceOperation] Trace to attach data to.
39
+ # @param user_id [String] User id that attempted login
40
+ # @param user_exists [bool] Whether the user id that did a login attempt exists.
41
+ # @param others [Hash<String || Symbol, String>] Additional free-form
42
+ # event information to attach to the trace.
43
+ def self.track_login_failure(trace, user_id:, user_exists:, **others)
44
+ track(LOGIN_FAILURE_EVENT, trace, **others)
45
+
46
+ raise ArgumentError, 'user_id cannot be nil' if user_id.nil?
47
+
48
+ trace.set_tag('appsec.events.users.login.failure.usr.id', user_id)
49
+ trace.set_tag('appsec.events.users.login.failure.usr.exists', user_exists)
50
+ end
51
+
52
+ # Attach custom event information to the trace
53
+ #
54
+ # This method is experimental and may change in the future.
55
+ #
56
+ # @param event [String] Mandatory. Event code.
57
+ # @param trace [TraceOperation] Trace to attach data to.
58
+ # @param others [Hash<Symbol, String>] Additional free-form
59
+ # event information to attach to the trace. Key must not
60
+ # be :track.
61
+ def self.track(event, trace, **others)
62
+ trace.set_tag("appsec.events.#{event}.track", 'true')
63
+
64
+ others.each do |k, v|
65
+ raise ArgumentError, 'key cannot be :track' if k.to_sym == :track
66
+
67
+ trace.set_tag("appsec.events.#{event}.#{k}", v) unless v.nil?
68
+ end
69
+
70
+ trace.keep!
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,4 +1,5 @@
1
1
  # typed: ignore
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Datadog
4
5
  module Kit
@@ -1,4 +1,5 @@
1
1
  # typed: false
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Datadog
4
5
  module Kit
@@ -22,7 +23,7 @@ module Datadog
22
23
  # currently possesses extracted from token or application security
23
24
  # context. The value would come from the scope associated with an OAuth
24
25
  # 2.0 Access Token or an attribute value in a SAML 2.0 Assertion.
25
- # @param others [Hash<String || Symbol, String>] Additional free-form
26
+ # @param others [Hash<Symbol, String>] Additional free-form
26
27
  # user information to attach to the trace.
27
28
  #
28
29
  # rubocop:disable Metrics/CyclomaticComplexity
@@ -32,12 +33,12 @@ module Datadog
32
33
 
33
34
  # enforce types
34
35
 
35
- raise TypeError, 'id must be a String' unless id.is_a?(String)
36
- raise TypeError, 'email must be a String' unless email.nil? || email.is_a?(String)
37
- raise TypeError, 'name must be a String' unless name.nil? || name.is_a?(String)
38
- raise TypeError, 'session_id must be a String' unless session_id.nil? || session_id.is_a?(String)
39
- raise TypeError, 'role must be a String' unless role.nil? || role.is_a?(String)
40
- raise TypeError, 'scope must be a String' unless scope.nil? || scope.is_a?(String)
36
+ raise TypeError, ':id must be a String' unless id.is_a?(String)
37
+ raise TypeError, ':email must be a String' unless email.nil? || email.is_a?(String)
38
+ raise TypeError, ':name must be a String' unless name.nil? || name.is_a?(String)
39
+ raise TypeError, ':session_id must be a String' unless session_id.nil? || session_id.is_a?(String)
40
+ raise TypeError, ':role must be a String' unless role.nil? || role.is_a?(String)
41
+ raise TypeError, ':scope must be a String' unless scope.nil? || scope.is_a?(String)
41
42
 
42
43
  others.each do |k, v|
43
44
  raise TypeError, "#{k.inspect} must be a String" unless v.nil? || v.is_a?(String)
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ require_relative 'trace/span'
5
+ require_relative '../../tracing/trace_operation'
6
+
7
+ module Datadog
8
+ module OpenTelemetry
9
+ module API
10
+ # The OpenTelemetry Context contains a key-value store that can be attached
11
+ # to a trace.
12
+ #
13
+ # It loosely matches our `TraceOperations#tags`, except for the following:
14
+ # * Context can store arbitrary objects as values. One example is for the key
15
+ # `Context::Key.new('current-span')`, which is associated with a `Span` object.
16
+ # In contrast, `TraceOperations#tags` only stores string values.
17
+ # * Context is how spans know who their parent span is. The parenting operation happens on every
18
+ # span created. Parenting is not directly tied to the active Fiber or Thread.
19
+ # * Context is immutable: changing a value creates a copy of Context.
20
+ # * Context is not bound to a specific trace: it can be reused an arbitrary number of times.
21
+ module Context
22
+ CURRENT_SPAN_KEY = ::OpenTelemetry::Trace.const_get(:CURRENT_SPAN_KEY)
23
+ private_constant :CURRENT_SPAN_KEY
24
+
25
+ def initialize(entries, trace: nil)
26
+ @trace = trace || ::Datadog::Tracing.send(:tracer).send(:start_trace)
27
+ @trace.otel_values.merge!(entries) if entries
28
+ @trace.otel_context ||= self
29
+ end
30
+
31
+ # Because Context can be reused, we have to make sure we have
32
+ # a valid `TraceOperation` on every invocation.
33
+ def ensure_trace
34
+ return nil unless @trace
35
+
36
+ # The Context can be reused after the root span has finished.
37
+ @trace.send(:reset) if @trace.finished?
38
+ @trace
39
+ end
40
+
41
+ # Returns the corresponding value (or nil) for key
42
+ #
43
+ # @param [Key] key The lookup key
44
+ # @return [Object]
45
+ def value(key)
46
+ return nil unless @trace
47
+
48
+ @trace.otel_value(key)
49
+ end
50
+
51
+ alias [] value
52
+
53
+ # Returns a new Context where entries contains the newly added key and value
54
+ #
55
+ # @param [Key] key The key to store this value under
56
+ # @param [Object] value Object to be stored under key
57
+ # @return [Context]
58
+ def set_value(key, value)
59
+ set_values(key => value)
60
+ end
61
+
62
+ # Returns a new Context with the current context's entries merged with the
63
+ # new entries
64
+ #
65
+ # @param [Hash] values The values to be merged with the current context's
66
+ # entries.
67
+ # @param [Object] value Object to be stored under key
68
+ # @return [Context]
69
+ def set_values(values)
70
+ trace = nil
71
+ if (current_span = values[CURRENT_SPAN_KEY])
72
+ trace = current_span.datadog_trace
73
+ end
74
+
75
+ existing_values = @trace && @trace.otel_values || {}
76
+
77
+ ::OpenTelemetry::Context.new(existing_values.merge(values), trace: trace)
78
+ end
79
+
80
+ # The Datadog {TraceOperation} associated with this {Context}.
81
+ def trace
82
+ @trace
83
+ end
84
+
85
+ # Singleton class methods for {Context}
86
+ module SingletonClass
87
+ # Returns current context, which is never nil
88
+ #
89
+ # @return [Context]
90
+ def current
91
+ trace = Tracing.active_trace
92
+ return ::OpenTelemetry::Context::ROOT unless trace
93
+
94
+ trace.otel_context ||= ::OpenTelemetry::Context.from_trace(trace)
95
+ end
96
+
97
+ # Associates a Context with the caller's current Fiber. Every call to
98
+ # this operation should be paired with a corresponding call to detach.
99
+ #
100
+ # Returns a token to be used with the matching call to detach
101
+ #
102
+ # @param [Context] context The new context
103
+ # @return [Object] A token to be used when detaching
104
+ def attach(context)
105
+ previous_trace = Tracing.active_trace
106
+ continue_trace!(context)
107
+
108
+ stack.push(previous_trace && previous_trace.otel_context || ::OpenTelemetry::Context::ROOT)
109
+ stack.size
110
+ end
111
+
112
+ # Restores the previous Context associated with the current Fiber.
113
+ # The supplied token is used to check if the call to detach is balanced
114
+ # with a corresponding attach call. A warning is logged if the
115
+ # calls are unbalanced.
116
+ #
117
+ # @param [Object] token The token provided by the matching call to attach
118
+ # @return [Boolean] True if the calls matched, false otherwise
119
+ def detach(token)
120
+ s = stack
121
+ calls_matched = (token == s.size)
122
+ unless calls_matched
123
+ ::OpenTelemetry.handle_error(
124
+ exception: ::OpenTelemetry::Context::DetachError.new(
125
+ 'calls to detach should match corresponding calls to attach.'
126
+ )
127
+ )
128
+ end
129
+
130
+ previous_context = s.pop
131
+ continue_trace!(previous_context)
132
+ calls_matched
133
+ end
134
+
135
+ # Part of the OpenTelemetry public API for {Context}.
136
+ def clear
137
+ super
138
+ tracer = Tracing.send(:tracer)
139
+ tracer.send(:call_context).activate!(nil)
140
+ end
141
+
142
+ # Creates a new {Context} associated with a {TraceOperation}.
143
+ def from_trace(trace)
144
+ new({}, trace: trace)
145
+ end
146
+
147
+ private
148
+
149
+ def continue_trace!(context, &block)
150
+ call_context = Tracing.send(:tracer).send(:call_context)
151
+ if context && context.trace
152
+ call_context.activate!(context.ensure_trace, &block)
153
+ else
154
+ call_context.activate!(nil)
155
+ end
156
+ end
157
+ end
158
+
159
+ def self.prepended(base)
160
+ base.singleton_class.prepend(SingletonClass)
161
+ end
162
+
163
+ ::OpenTelemetry::Context.prepend(self)
164
+ end
165
+
166
+ # OpenTelemetry-specific {TraceOperation} features.
167
+ #
168
+ # These extensions providing matching between {TraceOperation}
169
+ # and OpenTelemetry {Context}.
170
+ module TraceOperation
171
+ attr_accessor :otel_context
172
+
173
+ # Stores values from Context#entries
174
+ def otel_value(key)
175
+ otel_values[key]
176
+ end
177
+
178
+ # Retrieves values for Context#entries
179
+ def otel_values
180
+ @otel_values ||= {}
181
+ end
182
+
183
+ Tracing::TraceOperation.include(self)
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ module Datadog
5
+ module OpenTelemetry
6
+ module Trace
7
+ # Stores associated Datadog entities to the OpenTelemetry Span.
8
+ module Span
9
+ attr_accessor :datadog_trace, :datadog_span
10
+
11
+ ::OpenTelemetry::Trace::Span.prepend(self)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ require_relative 'span_processor'
5
+ require_relative 'id_generator'
6
+ require_relative 'propagator'
7
+
8
+ module Datadog
9
+ module OpenTelemetry
10
+ module SDK
11
+ # The Configurator is responsible for setting wiring up
12
+ # different OpenTelemetry requirements together.
13
+ # Some of the requirements will be changed to Datadog versions.
14
+ module Configurator
15
+ def initialize
16
+ super
17
+ @id_generator = IdGenerator
18
+ end
19
+
20
+ # Ensure Datadog-configure propagation styles have are applied when configured.
21
+ #
22
+ # DEV: Support configuring propagation through the environment variable
23
+ # DEV: `OTEL_PROPAGATORS`, similar to `DD_TRACE_PROPAGATION*`?
24
+ def configure_propagation
25
+ @propagators = [Propagator.new(Tracing::Contrib::HTTP::Distributed::Propagation.new)]
26
+ super
27
+ end
28
+
29
+ # Ensure Datadog-configure trace writer is configured.
30
+ def wrapped_exporters_from_env
31
+ [SpanProcessor.new]
32
+ end
33
+
34
+ ::OpenTelemetry::SDK::Configurator.prepend(self)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ module Datadog
5
+ module OpenTelemetry
6
+ module SDK
7
+ # Generates Datadog-compatible IDs for OpenTelemetry traces.
8
+ # OpenTelemetry traces already produce Datadog-compatible IDs.
9
+ class IdGenerator
10
+ class << self
11
+ include ::OpenTelemetry::Trace
12
+
13
+ # Generates a valid trace identifier, a 16-byte string with at least one
14
+ # non-zero byte.
15
+ #
16
+ # @return [String] a valid trace ID.
17
+ def generate_trace_id
18
+ loop do
19
+ id = Random.bytes(8) # DEV: Change to 16 (16*8-byte) when 128-bit trace_id is supported.
20
+ return id unless id == ::OpenTelemetry::Trace::INVALID_SPAN_ID
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ module Datadog
5
+ module OpenTelemetry
6
+ module SDK
7
+ # Compatibility wrapper to allow Datadog propagators to fulfill the
8
+ # OpenTelemetry propagator API.
9
+ class Propagator
10
+ def initialize(datadog_propagator)
11
+ @datadog_propagator = datadog_propagator
12
+ end
13
+
14
+ def inject(
15
+ carrier, context: ::OpenTelemetry::Context.current,
16
+ setter: ::OpenTelemetry::Context::Propagation.text_map_setter
17
+ )
18
+ unless setter == ::OpenTelemetry::Context::Propagation.text_map_setter
19
+ Datadog.logger.error(
20
+ 'Custom setter is not supported. Please inform the `ddtrace` team at ' \
21
+ ' https://github.com/DataDog/dd-trace-rb of your use case so we can best support you. Using the default ' \
22
+ 'OpenTelemetry::Context::Propagation.text_map_setter as a fallback setter.'
23
+ )
24
+ end
25
+
26
+ @datadog_propagator.inject!(context.trace.to_digest, carrier)
27
+ end
28
+
29
+ def extract(
30
+ carrier, context: ::OpenTelemetry::Context.current,
31
+ getter: ::OpenTelemetry::Context::Propagation.text_map_getter
32
+ )
33
+ unless getter == ::OpenTelemetry::Context::Propagation.text_map_getter
34
+ Datadog.logger.error(
35
+ 'Custom getter is not supported. Please inform the `ddtrace` team at ' \
36
+ ' https://github.com/DataDog/dd-trace-rb of your use case so we can best support you. Using the default ' \
37
+ 'OpenTelemetry::Context::Propagation.text_map_getter as a fallback getter.'
38
+ )
39
+ end
40
+
41
+ digest = @datadog_propagator.extract(carrier)
42
+ return context unless digest
43
+
44
+ trace_id = to_otel_id(digest.trace_id)
45
+ span_id = to_otel_id(digest.span_id)
46
+
47
+ if digest.trace_state || digest.trace_flags
48
+ trace_flags = ::OpenTelemetry::Trace::TraceFlags.from_byte(digest.trace_flags)
49
+ tracestate = Tracing::Distributed::TraceContext.new(fetcher: nil).send(:build_tracestate, digest)
50
+ else
51
+ trace_flags = if Tracing::Sampling::PrioritySampler.sampled?(digest.trace_sampling_priority)
52
+ ::OpenTelemetry::Trace::TraceFlags::SAMPLED
53
+ else
54
+ ::OpenTelemetry::Trace::TraceFlags::DEFAULT
55
+ end
56
+ tracestate = ::OpenTelemetry::Trace::Tracestate::DEFAULT
57
+ end
58
+
59
+ span_context = ::OpenTelemetry::Trace::SpanContext.new(
60
+ trace_id: trace_id,
61
+ span_id: span_id,
62
+ trace_flags: trace_flags,
63
+ tracestate: tracestate,
64
+ remote: true
65
+ )
66
+
67
+ span = ::OpenTelemetry::Trace.non_recording_span(span_context)
68
+
69
+ trace = Tracing.continue_trace!(digest)
70
+
71
+ span.datadog_trace = trace
72
+
73
+ ::OpenTelemetry::Trace.context_with_span(span, parent_context: context)
74
+ end
75
+
76
+ # Returns fields set by this propagator.
77
+ # DEV: Doesn't seem like it's used in production Otel code paths.
78
+ def fields
79
+ []
80
+ end
81
+
82
+ private
83
+
84
+ # Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
85
+ def to_otel_id(dd_id)
86
+ Array(dd_id).pack('S')
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ module Datadog
5
+ module OpenTelemetry
6
+ module SDK
7
+ # Keeps OpenTelemetry spans in sync with the Datadog execution context.
8
+ # Also responsible for flushing spans when their are finished.
9
+ class SpanProcessor
10
+ # Called when a {Span} is started, if the {Span#recording?}
11
+ # returns true.
12
+ #
13
+ # This method is called synchronously on the execution thread, should
14
+ # not throw or block the execution thread.
15
+ #
16
+ # @param [Span] span the {Span} that just started.
17
+ # @param [Context] parent_context the parent {Context} of the newly
18
+ # started span.
19
+ def on_start(span, parent_context)
20
+ create_matching_datadog_span(span, parent_context)
21
+ end
22
+
23
+ # Called when a {Span} is ended, if the {Span#recording?}
24
+ # returns true.
25
+ #
26
+ # This method is called synchronously on the execution thread, should
27
+ # not throw or block the execution thread.
28
+ #
29
+ # @param [Span] span the {Span} that just ended.
30
+ def on_finish(span)
31
+ span.datadog_span.finish
32
+ end
33
+
34
+ # Export all ended spans to the configured `Exporter` that have not yet
35
+ # been exported.
36
+ #
37
+ # This method should only be called in cases where it is absolutely
38
+ # necessary, such as when using some FaaS providers that may suspend
39
+ # the process after an invocation, but before the `Processor` exports
40
+ # the completed spans.
41
+ #
42
+ # @param [optional Numeric] timeout An optional timeout in seconds.
43
+ # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
44
+ # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
45
+ def force_flush(timeout: nil)
46
+ writer.force_flush(timeout: timeout) if writer.respond_to? :force_flush
47
+ Export::SUCCESS
48
+ end
49
+
50
+ # Called when {TracerProvider#shutdown} is called.
51
+ #
52
+ # @param [optional Numeric] timeout An optional timeout in seconds.
53
+ # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
54
+ # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
55
+ def shutdown(timeout: nil)
56
+ writer.stop
57
+ Export::SUCCESS
58
+ end
59
+
60
+ private
61
+
62
+ def writer
63
+ Datadog.configuration.tracing.writer
64
+ end
65
+
66
+ def create_matching_datadog_span(span, parent_context)
67
+ if parent_context.trace
68
+ Tracing.send(:tracer).send(:call_context).activate!(parent_context.ensure_trace)
69
+ else
70
+ Tracing.continue_trace!(nil)
71
+ end
72
+
73
+ datadog_span = start_datadog_span(span)
74
+
75
+ span.datadog_span = datadog_span
76
+ span.datadog_trace = Tracing.active_trace
77
+ end
78
+
79
+ def start_datadog_span(span)
80
+ tags = span.resource.attribute_enumerator.to_h
81
+
82
+ kind = span.kind || 'internal'
83
+ tags[Tracing::Metadata::Ext::TAG_KIND] = kind
84
+
85
+ datadog_span = Tracing.trace(span.name, tags: tags)
86
+ datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
87
+ datadog_span
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # Entrypoint file for OpenTelemetry integration.
5
+ #
6
+ # This file's path is part of the @public_api.
7
+ #
8
+ # OpenTelemetry includes metrics, tracing, logs, and profiling.
9
+ # This file activates the integrations of all OpenTelemetry
10
+ # components supported by Datadog.
11
+
12
+ require_relative 'tracing'
13
+ require_relative 'opentelemetry/api/context'
14
+
15
+ # DEV: Should this be a Contrib integration, that depends on the `opentelemetry-sdk`
16
+ # DEV: and checks for compatibility?
17
+ # DEV: This is different from our existing OpenTracer API, but there are many safety
18
+ # DEV: features built into Contrib instrumentation today.
19
+ require_relative 'opentelemetry/sdk/configurator' if defined?(OpenTelemetry::SDK)
20
+
21
+ module Datadog
22
+ # Datadog OpenTelemetry integration.
23
+ module OpenTelemetry
24
+ # Used by Telemetry to decide if OpenTelemetry instrumentation is enabled
25
+ LOADED = true
26
+
27
+ # Use `Datadog.logger` as the default logger
28
+ def logger
29
+ @logger ||= ::Datadog.logger
30
+ end
31
+
32
+ ::OpenTelemetry.singleton_class.prepend(self)
33
+ end
34
+ end
35
+
36
+ # OpenTelemetry does not wait until the "root" span is finished to flush:
37
+ # the "root" span does not have special influence on flushing order.
38
+ #
39
+ # The "root" OpenTelemetry span might be a span that is never finished, but
40
+ # instead a placeholder for distributed tracing information, and ultimately gets discarded.
41
+ # Consumers of the OpenTelemetry SpanProcessor pipeline are free to flush spans whenever
42
+ # an individual span is finished.
43
+ # Currently, this closely translates to Datadog's partial flushing.
44
+ #
45
+ # @see OpenTelemetry::SDK::Trace::SpanProcessor#on_finish
46
+ Datadog.configure do |c|
47
+ c.tracing.partial_flush.enabled = true
48
+ end
@@ -48,7 +48,7 @@ module Datadog
48
48
  ENV_PROPAGATION_STYLE_EXTRACT_OLD = 'DD_PROPAGATION_STYLE_EXTRACT'.freeze
49
49
 
50
50
  # A no-op propagator. Compatible with OpenTelemetry's `none` propagator.
51
- # @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_propagators
51
+ # @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#get_otel__propagators
52
52
  PROPAGATION_STYLE_NONE = 'none'.freeze
53
53
 
54
54
  ENV_X_DATADOG_TAGS_MAX_LENGTH = 'DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH'.freeze
@@ -78,7 +78,6 @@ module Datadog
78
78
 
79
79
  # @public_api
80
80
  module Transport
81
- ENV_DEFAULT_HOST = 'DD_AGENT_HOST'.freeze
82
81
  ENV_DEFAULT_PORT = 'DD_TRACE_AGENT_PORT'.freeze
83
82
  ENV_DEFAULT_URL = 'DD_TRACE_AGENT_URL'.freeze
84
83
  end
@@ -33,6 +33,11 @@ module Datadog
33
33
  o.lazy
34
34
  end
35
35
 
36
+ option :error_status_codes do |o|
37
+ o.default { env_to_list(Ext::ENV_ERROR_STATUS_CODES, 400...600, comma_separated_only: false) }
38
+ o.lazy
39
+ end
40
+
36
41
  option :split_by_domain, default: false
37
42
  end
38
43
  end
@@ -8,13 +8,20 @@ module Datadog
8
8
  module Contrib
9
9
  module HTTP
10
10
  module Distributed
11
- # Retrieves Rack formatted headers from HTTP headers.
11
+ # Retrieves HTTP headers from carrier.
12
+ # Headers will also match if Rack-formatted:
13
+ # 'my-header' will match 'my-header' and 'HTTP_MY_HEADER'.
14
+ #
15
+ # In case both variants are present, the verbatim match will be used.
12
16
  class Fetcher < Tracing::Distributed::Fetcher
13
- # TODO: Don't assume Rack format.
14
- # Make distributed tracing headers apathetic.
15
17
  # DEV: Should we try to parse both verbatim an Rack-formatted headers,
16
18
  # DEV: given Rack-formatted is the most common format in Ruby?
17
19
  def [](name)
20
+ # Try to fetch with the plain key
21
+ value = super(name)
22
+ return value if value && !value.empty?
23
+
24
+ # If not found, try the Rack-formatted key
18
25
  rack_header = "HTTP-#{name}"
19
26
  rack_header.upcase!
20
27
  rack_header.tr!('-'.freeze, '_'.freeze)
@@ -11,6 +11,7 @@ module Datadog
11
11
  ENV_SERVICE_NAME = 'DD_TRACE_NET_HTTP_SERVICE_NAME'.freeze
12
12
  ENV_ANALYTICS_ENABLED = 'DD_TRACE_HTTP_ANALYTICS_ENABLED'.freeze
13
13
  ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_HTTP_ANALYTICS_SAMPLE_RATE'.freeze
14
+ ENV_ERROR_STATUS_CODES = 'DD_TRACE_HTTP_ERROR_STATUS_CODES'.freeze
14
15
  DEFAULT_PEER_SERVICE_NAME = 'net/http'.freeze
15
16
  SPAN_REQUEST = 'http.request'.freeze
16
17
  TAG_COMPONENT = 'net/http'.freeze