ddtrace 0.52.0 → 0.54.2

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -11
  3. data/ddtrace.gemspec +6 -3
  4. data/docs/DevelopmentGuide.md +1 -6
  5. data/docs/GettingStarted.md +109 -18
  6. data/docs/ProfilingDevelopment.md +2 -2
  7. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +86 -0
  8. data/ext/ddtrace_profiling_native_extension/clock_id.h +4 -0
  9. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +52 -0
  10. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +14 -0
  11. data/ext/ddtrace_profiling_native_extension/extconf.rb +177 -8
  12. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +35 -0
  13. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
  14. data/ext/ddtrace_profiling_native_extension/profiling.c +6 -1
  15. data/lib/datadog/ci/contrib/cucumber/formatter.rb +1 -0
  16. data/lib/datadog/ci/contrib/rspec/example.rb +1 -0
  17. data/lib/datadog/ci/contrib/rspec/integration.rb +2 -2
  18. data/lib/datadog/ci/ext/environment.rb +64 -22
  19. data/lib/datadog/ci/ext/test.rb +1 -0
  20. data/lib/datadog/ci/test.rb +5 -1
  21. data/lib/datadog/contrib.rb +2 -0
  22. data/lib/datadog/core/environment/vm_cache.rb +46 -0
  23. data/lib/ddtrace/buffer.rb +28 -16
  24. data/lib/ddtrace/configuration/agent_settings_resolver.rb +131 -53
  25. data/lib/ddtrace/configuration/components.rb +1 -1
  26. data/lib/ddtrace/configuration/settings.rb +13 -3
  27. data/lib/ddtrace/context.rb +10 -2
  28. data/lib/ddtrace/contrib/action_cable/instrumentation.rb +46 -0
  29. data/lib/ddtrace/contrib/action_cable/patcher.rb +1 -0
  30. data/lib/ddtrace/contrib/action_mailer/configuration/settings.rb +32 -0
  31. data/lib/ddtrace/contrib/action_mailer/event.rb +50 -0
  32. data/lib/ddtrace/contrib/action_mailer/events/deliver.rb +54 -0
  33. data/lib/ddtrace/contrib/action_mailer/events/process.rb +41 -0
  34. data/lib/ddtrace/contrib/action_mailer/events.rb +31 -0
  35. data/lib/ddtrace/contrib/action_mailer/ext.rb +32 -0
  36. data/lib/ddtrace/contrib/action_mailer/integration.rb +45 -0
  37. data/lib/ddtrace/contrib/action_mailer/patcher.rb +27 -0
  38. data/lib/ddtrace/contrib/active_job/configuration/settings.rb +33 -0
  39. data/lib/ddtrace/contrib/active_job/event.rb +54 -0
  40. data/lib/ddtrace/contrib/active_job/events/discard.rb +46 -0
  41. data/lib/ddtrace/contrib/active_job/events/enqueue.rb +45 -0
  42. data/lib/ddtrace/contrib/active_job/events/enqueue_at.rb +45 -0
  43. data/lib/ddtrace/contrib/active_job/events/enqueue_retry.rb +47 -0
  44. data/lib/ddtrace/contrib/active_job/events/perform.rb +45 -0
  45. data/lib/ddtrace/contrib/active_job/events/retry_stopped.rb +46 -0
  46. data/lib/ddtrace/contrib/active_job/events.rb +39 -0
  47. data/lib/ddtrace/contrib/active_job/ext.rb +32 -0
  48. data/lib/ddtrace/contrib/active_job/integration.rb +46 -0
  49. data/lib/ddtrace/contrib/active_job/log_injection.rb +21 -0
  50. data/lib/ddtrace/contrib/active_job/patcher.rb +33 -0
  51. data/lib/ddtrace/contrib/auto_instrument.rb +0 -1
  52. data/lib/ddtrace/contrib/delayed_job/plugin.rb +2 -2
  53. data/lib/ddtrace/contrib/mongodb/instrumentation.rb +1 -1
  54. data/lib/ddtrace/contrib/mongodb/integration.rb +5 -0
  55. data/lib/ddtrace/contrib/rails/auto_instrument_railtie.rb +0 -1
  56. data/lib/ddtrace/contrib/rails/configuration/settings.rb +7 -0
  57. data/lib/ddtrace/contrib/rails/framework.rb +24 -1
  58. data/lib/ddtrace/contrib/rails/patcher.rb +19 -10
  59. data/lib/ddtrace/contrib/redis/instrumentation.rb +90 -0
  60. data/lib/ddtrace/contrib/redis/patcher.rb +2 -84
  61. data/lib/ddtrace/contrib/registerable.rb +0 -1
  62. data/lib/ddtrace/contrib/resque/integration.rb +1 -5
  63. data/lib/ddtrace/contrib/sidekiq/ext.rb +3 -0
  64. data/lib/ddtrace/contrib/sidekiq/integration.rb +10 -0
  65. data/lib/ddtrace/contrib/sidekiq/patcher.rb +26 -0
  66. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/heartbeat.rb +30 -0
  67. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/job_fetch.rb +30 -0
  68. data/lib/ddtrace/contrib/sidekiq/server_internal_tracer/scheduled_push.rb +29 -0
  69. data/lib/ddtrace/contrib/sinatra/env.rb +2 -1
  70. data/lib/ddtrace/contrib/sinatra/tracer.rb +15 -2
  71. data/lib/ddtrace/ext/git.rb +12 -0
  72. data/lib/ddtrace/ext/priority.rb +6 -4
  73. data/lib/ddtrace/ext/profiling.rb +8 -11
  74. data/lib/ddtrace/ext/runtime.rb +3 -0
  75. data/lib/ddtrace/ext/transport.rb +11 -0
  76. data/lib/ddtrace/metrics.rb +2 -2
  77. data/lib/ddtrace/profiling/collectors/stack.rb +112 -72
  78. data/lib/ddtrace/profiling/encoding/profile.rb +10 -2
  79. data/lib/ddtrace/profiling/events/stack.rb +13 -13
  80. data/lib/ddtrace/profiling/native_extension.rb +23 -1
  81. data/lib/ddtrace/profiling/pprof/builder.rb +8 -2
  82. data/lib/ddtrace/profiling/pprof/converter.rb +22 -9
  83. data/lib/ddtrace/profiling/pprof/stack_sample.rb +32 -9
  84. data/lib/ddtrace/profiling/pprof/template.rb +2 -2
  85. data/lib/ddtrace/profiling/scheduler.rb +20 -4
  86. data/lib/ddtrace/profiling/tasks/setup.rb +21 -13
  87. data/lib/ddtrace/profiling/trace_identifiers/ddtrace.rb +10 -9
  88. data/lib/ddtrace/profiling/trace_identifiers/helper.rb +5 -5
  89. data/lib/ddtrace/profiling/transport/http/api/endpoint.rb +8 -15
  90. data/lib/ddtrace/profiling/transport/http.rb +8 -17
  91. data/lib/ddtrace/profiling.rb +0 -2
  92. data/lib/ddtrace/runtime/metrics.rb +14 -0
  93. data/lib/ddtrace/sampler.rb +18 -8
  94. data/lib/ddtrace/sampling/rule_sampler.rb +13 -1
  95. data/lib/ddtrace/span.rb +7 -19
  96. data/lib/ddtrace/tracer.rb +1 -1
  97. data/lib/ddtrace/transport/http/adapters/net.rb +13 -3
  98. data/lib/ddtrace/transport/http/adapters/test.rb +4 -2
  99. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +23 -12
  100. data/lib/ddtrace/transport/http/builder.rb +13 -6
  101. data/lib/ddtrace/transport/http.rb +5 -11
  102. data/lib/ddtrace/utils/time.rb +11 -6
  103. data/lib/ddtrace/version.rb +2 -2
  104. data/lib/ddtrace/workers/{loop.rb → interval_loop.rb} +0 -16
  105. data/lib/ddtrace/workers/polling.rb +1 -1
  106. metadata +40 -10
  107. data/lib/ddtrace/profiling/ext/cpu.rb +0 -67
  108. data/lib/ddtrace/profiling/ext/cthread.rb +0 -156
@@ -26,9 +26,11 @@ module Datadog
26
26
  end
27
27
 
28
28
  require 'ddtrace/contrib/action_cable/integration'
29
+ require 'ddtrace/contrib/action_mailer/integration'
29
30
  require 'ddtrace/contrib/action_pack/integration'
30
31
  require 'ddtrace/contrib/action_view/integration'
31
32
  require 'ddtrace/contrib/active_model_serializers/integration'
33
+ require 'ddtrace/contrib/active_job/integration'
32
34
  require 'ddtrace/contrib/active_record/integration'
33
35
  require 'ddtrace/contrib/active_support/integration'
34
36
  require 'ddtrace/contrib/aws/integration'
@@ -0,0 +1,46 @@
1
+ # typed: true
2
+ module Datadog
3
+ module Core
4
+ module Environment
5
+ # Reports Ruby VM cache performance statistics.
6
+ # This currently encompasses cache invalidation counters and is CRuby-specific.
7
+ #
8
+ # JRuby emulates some CRuby global cache statistics, but they are synthetic and don't
9
+ # provide actionable performance information in the same way CRuby does.
10
+ # @see https://github.com/jruby/jruby/issues/4384#issuecomment-267069314
11
+ #
12
+ # TruffleRuby does not have a global runtime cache invalidation cache.
13
+ # @see http://archive.today/2021.09.10-205702/https://medium.com/graalvm/precise-method-and-constant-invalidation-in-truffleruby-4dd56c6bac1a
14
+ module VMCache
15
+ module_function
16
+
17
+ # Global constant cache "generation" counter.
18
+ #
19
+ # Whenever a constant creation busts the global constant cache
20
+ # this value is incremented. This has a measurable performance impact
21
+ # and thus show be avoided after application warm up.
22
+ def global_constant_state
23
+ ::RubyVM.stat[:global_constant_state]
24
+ end
25
+
26
+ # Global method cache "generation" counter.
27
+ #
28
+ # Whenever a method creation busts the global method cache
29
+ # this value is incremented. This has a measurable performance impact
30
+ # and thus show be avoided after application warm up.
31
+ #
32
+ # Since Ruby 3.0, the method class is kept on a per-class basis,
33
+ # largely mitigating global method cache busting. `global_method_state`
34
+ # is thus not available since Ruby 3.0.
35
+ # @see https://bugs.ruby-lang.org/issues/16614
36
+ def global_method_state
37
+ ::RubyVM.stat[:global_method_state]
38
+ end
39
+
40
+ def available?
41
+ defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -14,7 +14,11 @@ module Datadog
14
14
  end
15
15
 
16
16
  # Add a new ``item`` in the local queue. This method doesn't block the execution
17
- # even if the buffer is full. In that case, a random item is discarded.
17
+ # even if the buffer is full.
18
+ #
19
+ # When the buffer is full, we try to ensure that we are fairly sampling newly
20
+ # pushed traces by randomly inserting them into the buffer slots. This discards
21
+ # old traces randomly while trying to ensure that recent traces are still captured.
18
22
  def push(item)
19
23
  return if closed?
20
24
 
@@ -64,7 +68,7 @@ module Datadog
64
68
 
65
69
  protected
66
70
 
67
- # Segment items into two distinct segments: underflow and overflow.
71
+ # Segment items into two segments: underflow and overflow.
68
72
  # Underflow are items that will fit into buffer.
69
73
  # Overflow are items that will exceed capacity, after underflow is added.
70
74
  # Returns each array, and nil if there is no underflow/overflow.
@@ -176,9 +180,6 @@ module Datadog
176
180
  # Buffer that stores objects, has a maximum size, and
177
181
  # can be safely used concurrently with CRuby.
178
182
  #
179
- # Under extreme concurrency scenarios, this class can exceed
180
- # its +max_size+ by up to 4%.
181
- #
182
183
  # Because singular +Array+ operations are thread-safe in CRuby,
183
184
  # we can implement the trace buffer without an explicit lock,
184
185
  # while making the compromise of allowing the buffer to go
@@ -187,7 +188,6 @@ module Datadog
187
188
  # On the following scenario:
188
189
  # * 4.5 million spans/second.
189
190
  # * Pushed into a single CRubyTraceBuffer from 1000 threads.
190
- # The buffer can exceed its maximum size by no more than 4%.
191
191
  #
192
192
  # This implementation allocates less memory and is faster
193
193
  # than {Datadog::ThreadSafeBuffer}.
@@ -195,19 +195,31 @@ module Datadog
195
195
  # @see spec/ddtrace/benchmark/buffer_benchmark_spec.rb Buffer benchmarks
196
196
  # @see https://github.com/ruby-concurrency/concurrent-ruby/blob/c1114a0c6891d9634f019f1f9fe58dcae8658964/lib/concurrent-ruby/concurrent/array.rb#L23-L27
197
197
  class CRubyBuffer < Buffer
198
+ # A very large number to allow us to effectively
199
+ # drop all items when invoking `slice!(i, FIXNUM_MAX)`.
200
+ FIXNUM_MAX = (1 << 62) - 1
201
+
198
202
  # Add a new ``trace`` in the local queue. This method doesn't block the execution
199
203
  # even if the buffer is full. In that case, a random trace is discarded.
200
204
  def replace!(item)
201
- # we should replace a random trace with the new one
202
- replace_index = rand(@items.size)
203
- replaced_trace = @items.delete_at(replace_index)
204
- @items << item
205
-
206
- # We might have deleted an element right when the buffer
207
- # was drained, thus +replaced_trace+ will be +nil+.
208
- # In that case, nothing was replaced, and this method
209
- # performed a simple insertion into the buffer.
210
- replaced_trace
205
+ # Ensure buffer stays within +max_size+ items.
206
+ # This can happen when there's concurrent modification
207
+ # between a call the check in `full?` and the `add!` call in
208
+ # `full? ? replace!(item) : add!(item)`.
209
+ #
210
+ # We can still have `@items.size > @max_size` for a short period of
211
+ # time, but we will always try to correct it here.
212
+ #
213
+ # `slice!` is performed before `delete_at` & `<<` to avoid always
214
+ # removing the item that was just inserted.
215
+ #
216
+ # DEV: `slice!` with two integer arguments is ~10% faster than
217
+ # `slice!` with a {Range} argument.
218
+ @items.slice!(@max_size, FIXNUM_MAX)
219
+
220
+ # We should replace a random trace with the new one
221
+ replace_index = rand(@max_size)
222
+ @items[replace_index] = item
211
223
  end
212
224
  end
213
225
 
@@ -20,25 +20,49 @@ module Datadog
20
20
  class AgentSettingsResolver
21
21
  AgentSettings = \
22
22
  Struct.new(
23
+ :adapter,
23
24
  :ssl,
24
25
  :hostname,
25
26
  :port,
27
+ :uds_path,
26
28
  :timeout_seconds,
27
29
  :deprecated_for_removal_transport_configuration_proc,
28
30
  :deprecated_for_removal_transport_configuration_options
29
31
  ) do
30
32
  def initialize(
33
+ adapter:,
31
34
  ssl:,
32
35
  hostname:,
33
36
  port:,
37
+ uds_path:,
34
38
  timeout_seconds:,
35
39
  deprecated_for_removal_transport_configuration_proc:,
36
40
  deprecated_for_removal_transport_configuration_options:
37
41
  )
38
- super(ssl, hostname, port, timeout_seconds, deprecated_for_removal_transport_configuration_proc, \
39
- deprecated_for_removal_transport_configuration_options)
42
+ super(
43
+ adapter,
44
+ ssl,
45
+ hostname,
46
+ port,
47
+ uds_path,
48
+ timeout_seconds,
49
+ deprecated_for_removal_transport_configuration_proc,
50
+ deprecated_for_removal_transport_configuration_options,
51
+ )
40
52
  freeze
41
53
  end
54
+
55
+ # Returns a frozen copy of this struct
56
+ # with the provided +member_values+ modified.
57
+ def merge(**member_values)
58
+ new_struct = dup
59
+
60
+ member_values.each do |member, value|
61
+ new_struct[member] = value
62
+ end
63
+
64
+ new_struct.freeze
65
+ end
42
66
  end
43
67
 
44
68
  def self.call(settings, logger: Datadog.logger)
@@ -58,9 +82,11 @@ module Datadog
58
82
 
59
83
  def call
60
84
  AgentSettings.new(
85
+ adapter: adapter,
61
86
  ssl: ssl?,
62
87
  hostname: hostname,
63
88
  port: port,
89
+ uds_path: uds_path,
64
90
  timeout_seconds: timeout_seconds,
65
91
  # NOTE: When provided, the deprecated_for_removal_transport_configuration_proc can override all
66
92
  # values above (ssl, hostname, port, timeout), or even make them irrelevant (by using an unix socket or
@@ -72,65 +98,99 @@ module Datadog
72
98
  )
73
99
  end
74
100
 
75
- def hostname
76
- pick_from(
77
- configurations_in_priority_order: [
78
- DetectedConfiguration.new(
79
- friendly_name: "'c.tracer.hostname'",
80
- value: settings.tracer.hostname
81
- ),
82
- DetectedConfiguration.new(
83
- friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_URL} environment variable",
84
- value: parsed_url && parsed_url.hostname
85
- ),
86
- DetectedConfiguration.new(
87
- friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_HOST} environment variable",
88
- value: ENV[Datadog::Ext::Transport::HTTP::ENV_DEFAULT_HOST]
89
- )
90
- ],
91
- or_use_default: Datadog::Ext::Transport::HTTP::DEFAULT_HOST
101
+ def adapter
102
+ # If no agent settings have been provided, we try to connect using a local unix socket.
103
+ # We only do so if the socket is present when `ddtrace` runs.
104
+ if should_use_uds_fallback?
105
+ Ext::Transport::UnixSocket::ADAPTER
106
+ else
107
+ Ext::Transport::HTTP::ADAPTER
108
+ end
109
+ end
110
+
111
+ def configured_hostname
112
+ return @configured_hostname if defined?(@configured_hostname)
113
+
114
+ @configured_hostname = pick_from(
115
+ DetectedConfiguration.new(
116
+ friendly_name: "'c.tracer.hostname'",
117
+ value: settings.tracer.hostname
118
+ ),
119
+ DetectedConfiguration.new(
120
+ friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_URL} environment variable",
121
+ value: parsed_url && parsed_url.hostname
122
+ ),
123
+ DetectedConfiguration.new(
124
+ friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_HOST} environment variable",
125
+ value: ENV[Datadog::Ext::Transport::HTTP::ENV_DEFAULT_HOST]
126
+ )
92
127
  )
93
128
  end
94
129
 
95
- def port
96
- port_from_env = ENV[Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT]
130
+ def configured_port
131
+ return @configured_port if defined?(@configured_port)
132
+
97
133
  parsed_port_from_env =
98
- if port_from_env
99
- begin
100
- Integer(port_from_env)
101
- rescue ArgumentError
102
- log_warning(
103
- "Invalid value for #{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT} environment variable " \
104
- "('#{port_from_env}'). Ignoring this configuration."
105
- )
106
- end
107
- end
134
+ try_parsing_as_integer(
135
+ friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT} environment variable",
136
+ value: ENV[Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT],
137
+ )
108
138
 
109
- pick_from(
110
- configurations_in_priority_order: [
111
- DetectedConfiguration.new(
112
- friendly_name: '"c.tracer.port"',
113
- value: settings.tracer.port
114
- ),
115
- DetectedConfiguration.new(
116
- friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_URL} environment variable",
117
- value: parsed_url && parsed_url.port
118
- ),
119
- DetectedConfiguration.new(
120
- friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT} environment variable",
121
- value: parsed_port_from_env
122
- )
123
- ],
124
- or_use_default: Datadog::Ext::Transport::HTTP::DEFAULT_PORT
139
+ parsed_settings_tracer_port =
140
+ try_parsing_as_integer(
141
+ friendly_name: '"c.tracer.port"',
142
+ value: settings.tracer.port,
143
+ )
144
+
145
+ @configured_port = pick_from(
146
+ DetectedConfiguration.new(
147
+ friendly_name: '"c.tracer.port"',
148
+ value: parsed_settings_tracer_port,
149
+ ),
150
+ DetectedConfiguration.new(
151
+ friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_URL} environment variable",
152
+ value: parsed_url && parsed_url.port,
153
+ ),
154
+ DetectedConfiguration.new(
155
+ friendly_name: "#{Datadog::Ext::Transport::HTTP::ENV_DEFAULT_PORT} environment variable",
156
+ value: parsed_port_from_env,
157
+ )
125
158
  )
126
159
  end
127
160
 
161
+ def try_parsing_as_integer(value:, friendly_name:)
162
+ return unless value
163
+
164
+ begin
165
+ Integer(value)
166
+ rescue ArgumentError, TypeError
167
+ log_warning("Invalid value for #{friendly_name} (#{value.inspect}). Ignoring this configuration.")
168
+
169
+ nil
170
+ end
171
+ end
172
+
128
173
  def ssl?
129
174
  !parsed_url.nil? && parsed_url.scheme == 'https'
130
175
  end
131
176
 
177
+ def hostname
178
+ configured_hostname || (should_use_uds_fallback? ? nil : Datadog::Ext::Transport::HTTP::DEFAULT_HOST)
179
+ end
180
+
181
+ def port
182
+ configured_port || (should_use_uds_fallback? ? nil : Datadog::Ext::Transport::HTTP::DEFAULT_PORT)
183
+ end
184
+
185
+ # Unix socket path in the file system
186
+ def uds_path
187
+ uds_fallback
188
+ end
189
+
190
+ # Defaults to +nil+, letting the adapter choose what default
191
+ # works best in their case.
132
192
  def timeout_seconds
133
- Datadog::Ext::Transport::HTTP::DEFAULT_TIMEOUT_SECONDS
193
+ nil
134
194
  end
135
195
 
136
196
  def deprecated_for_removal_transport_configuration_proc
@@ -150,6 +210,26 @@ module Datadog
150
210
  end
151
211
  end
152
212
 
213
+ # We only use the default unix socket if it is already present.
214
+ # This is by design, as we still want to use the default host:port if no unix socket is present.
215
+ def uds_fallback
216
+ return @uds_fallback if defined?(@uds_fallback)
217
+
218
+ @uds_fallback =
219
+ if configured_hostname.nil? &&
220
+ configured_port.nil? &&
221
+ deprecated_for_removal_transport_configuration_proc.nil? &&
222
+ deprecated_for_removal_transport_configuration_options.nil? &&
223
+ File.exist?(Ext::Transport::UnixSocket::DEFAULT_PATH)
224
+
225
+ Ext::Transport::UnixSocket::DEFAULT_PATH
226
+ end
227
+ end
228
+
229
+ def should_use_uds_fallback?
230
+ uds_fallback != nil
231
+ end
232
+
153
233
  def parsed_url
154
234
  return @parsed_url if defined?(@parsed_url)
155
235
 
@@ -177,7 +257,7 @@ module Datadog
177
257
  @unparsed_url_from_env ||= ENV[Datadog::Ext::Transport::HTTP::ENV_DEFAULT_URL]
178
258
  end
179
259
 
180
- def pick_from(configurations_in_priority_order:, or_use_default:)
260
+ def pick_from(*configurations_in_priority_order)
181
261
  detected_configurations_in_priority_order = configurations_in_priority_order.select(&:value?)
182
262
 
183
263
  if detected_configurations_in_priority_order.any?
@@ -186,8 +266,6 @@ module Datadog
186
266
  # The configurations are listed in priority, so we only need to look at the first; if there's more than
187
267
  # one, we emit a warning above
188
268
  detected_configurations_in_priority_order.first.value
189
- else
190
- or_use_default
191
269
  end
192
270
  end
193
271
 
@@ -197,8 +275,8 @@ module Datadog
197
275
  log_warning(
198
276
  'Configuration mismatch: values differ between ' \
199
277
  "#{detected_configurations_in_priority_order
200
- .map { |config| "#{config.friendly_name} ('#{config.value}')" }.join(' and ')}" \
201
- ". Using '#{detected_configurations_in_priority_order.first.value}'."
278
+ .map { |config| "#{config.friendly_name} (#{config.value.inspect})" }.join(' and ')}" \
279
+ ". Using #{detected_configurations_in_priority_order.first.value.inspect}."
202
280
  )
203
281
  end
204
282
 
@@ -117,7 +117,7 @@ module Datadog
117
117
 
118
118
  trace_identifiers_helper = Datadog::Profiling::TraceIdentifiers::Helper.new(
119
119
  tracer: tracer,
120
- extract_trace_resource: settings.profiling.advanced.extract_trace_resource
120
+ endpoint_collection_enabled: settings.profiling.advanced.endpoint.collection.enabled
121
121
  )
122
122
 
123
123
  recorder = build_profiler_recorder(settings)
@@ -161,6 +161,9 @@ module Datadog
161
161
  end
162
162
 
163
163
  settings :advanced do
164
+ # This should never be reduced, as it can cause the resulting profiles to become biased.
165
+ # The current default should be enough for most services, allowing 16 threads to be sampled around 30 times
166
+ # per second for a 60 second period.
164
167
  option :max_events, default: 32768
165
168
 
166
169
  # Controls the maximum number of frames for each thread sampled. Can be tuned to avoid omitted frames in the
@@ -170,9 +173,16 @@ module Datadog
170
173
  o.lazy
171
174
  end
172
175
 
173
- # When using profiling together with tracing, this controls if trace resources (usually the endpoint names)
174
- # are gathered and reported together with profiles.
175
- option :extract_trace_resource, default: true
176
+ settings :endpoint do
177
+ settings :collection do
178
+ # When using profiling together with tracing, this controls if endpoint names
179
+ # are gathered and reported together with profiles.
180
+ option :enabled do |o|
181
+ o.default { env_to_bool(Ext::Profiling::ENV_ENDPOINT_COLLECTION_ENABLED, true) }
182
+ o.lazy
183
+ end
184
+ end
185
+ end
176
186
  end
177
187
 
178
188
  settings :upload do
@@ -78,13 +78,21 @@ module Datadog
78
78
  # earlier while child spans still need to finish their traced execution.
79
79
  def current_span
80
80
  @mutex.synchronize do
81
- return @current_span
81
+ @current_span
82
82
  end
83
83
  end
84
84
 
85
85
  def current_root_span
86
86
  @mutex.synchronize do
87
- return @current_root_span
87
+ @current_root_span
88
+ end
89
+ end
90
+
91
+ # Same as calling #current_span and #current_root_span, but works atomically thus preventing races when we need to
92
+ # retrieve both
93
+ def current_span_and_root_span
94
+ @mutex.synchronize do
95
+ [@current_span, @current_root_span]
88
96
  end
89
97
  end
90
98
 
@@ -26,6 +26,52 @@ module Datadog
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ # Instrumentation for when a Channel is subscribed to/unsubscribed from.
31
+ module ActionCableChannel
32
+ def self.included(base)
33
+ base.class_eval do
34
+ set_callback(
35
+ :subscribe,
36
+ :around,
37
+ ->(channel, block) { Tracer.trace(channel, :subscribe, &block) },
38
+ prepend: true
39
+ )
40
+
41
+ set_callback(
42
+ :unsubscribe,
43
+ :around,
44
+ ->(channel, block) { Tracer.trace(channel, :unsubscribe, &block) },
45
+ prepend: true
46
+ )
47
+ end
48
+ end
49
+
50
+ # Instrumentation for Channel hooks.
51
+ class Tracer
52
+ def self.trace(channel, hook)
53
+ configuration = Datadog.configuration[:action_cable]
54
+
55
+ Datadog.tracer.trace("action_cable.#{hook}") do |span|
56
+ span.service = configuration[:service_name]
57
+ span.resource = "#{channel.class}##{hook}"
58
+ span.span_type = Datadog::Ext::AppTypes::WEB
59
+
60
+ # Set analytics sample rate
61
+ if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
62
+ Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
63
+ end
64
+
65
+ # Measure service stats
66
+ Contrib::Analytics.set_measured(span)
67
+
68
+ span.set_tag(Ext::TAG_CHANNEL_CLASS, channel.class.to_s)
69
+
70
+ yield
71
+ end
72
+ end
73
+ end
74
+ end
29
75
  end
30
76
  end
31
77
  end
@@ -21,6 +21,7 @@ module Datadog
21
21
  def patch
22
22
  Events.subscribe!
23
23
  ::ActionCable::Connection::Base.prepend(Instrumentation::ActionCableConnection)
24
+ ::ActionCable::Channel::Base.include(Instrumentation::ActionCableChannel)
24
25
  end
25
26
  end
26
27
  end
@@ -0,0 +1,32 @@
1
+ # typed: false
2
+ require 'ddtrace/contrib/configuration/settings'
3
+ require 'ddtrace/contrib/action_mailer/ext'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module ActionMailer
8
+ module Configuration
9
+ # Custom settings for the ActionMailer integration
10
+ class Settings < Contrib::Configuration::Settings
11
+ option :enabled do |o|
12
+ o.default { env_to_bool(Ext::ENV_ENABLED, true) }
13
+ o.lazy
14
+ end
15
+
16
+ option :analytics_enabled do |o|
17
+ o.default { env_to_bool([Ext::ENV_ANALYTICS_ENABLED, Ext::ENV_ANALYTICS_ENABLED_OLD], false) }
18
+ o.lazy
19
+ end
20
+
21
+ option :analytics_sample_rate do |o|
22
+ o.default { env_to_float([Ext::ENV_ANALYTICS_SAMPLE_RATE, Ext::ENV_ANALYTICS_SAMPLE_RATE_OLD], 1.0) }
23
+ o.lazy
24
+ end
25
+
26
+ option :service_name, default: Ext::SERVICE_NAME
27
+ option :email_data, default: false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # typed: false
2
+ require 'ddtrace/contrib/analytics'
3
+ require 'ddtrace/contrib/active_support/notifications/event'
4
+ require 'ddtrace/contrib/action_mailer/ext'
5
+
6
+ module Datadog
7
+ module Contrib
8
+ module ActionMailer
9
+ # Defines basic behaviors for an ActionMailer event.
10
+ module Event
11
+ def self.included(base)
12
+ base.send(:include, ActiveSupport::Notifications::Event)
13
+ base.send(:extend, ClassMethods)
14
+ end
15
+
16
+ # Class methods for ActionMailer events.
17
+ module ClassMethods
18
+ def span_options
19
+ { service: configuration[:service_name] }
20
+ end
21
+
22
+ def tracer
23
+ -> { configuration[:tracer] }
24
+ end
25
+
26
+ def configuration
27
+ Datadog.configuration[:action_mailer]
28
+ end
29
+
30
+ def process(span, event, _id, payload)
31
+ span.service = configuration[:service_name]
32
+ span.resource = payload[:mailer]
33
+
34
+ # Set analytics sample rate
35
+ if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
36
+ Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
37
+ end
38
+
39
+ # Measure service stats
40
+ Contrib::Analytics.set_measured(span)
41
+
42
+ report_if_exception(span, payload)
43
+ rescue StandardError => e
44
+ Datadog.logger.debug(e.message)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ # typed: false
2
+ require 'ddtrace/contrib/action_mailer/ext'
3
+ require 'ddtrace/contrib/action_mailer/event'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module ActionMailer
8
+ module Events
9
+ # Defines instrumentation for process.action_mailer event
10
+ module Deliver
11
+ include ActionMailer::Event
12
+
13
+ EVENT_NAME = 'deliver.action_mailer'.freeze
14
+
15
+ module_function
16
+
17
+ def event_name
18
+ self::EVENT_NAME
19
+ end
20
+
21
+ def span_name
22
+ Ext::SPAN_DELIVER
23
+ end
24
+
25
+ def span_type
26
+ # deliver.action_mailer sends emails
27
+ Datadog::Ext::AppTypes::WORKER
28
+ end
29
+
30
+ def process(span, event, _id, payload)
31
+ super
32
+
33
+ span.span_type = span_type
34
+ span.set_tag(Ext::TAG_MAILER, payload[:mailer])
35
+ span.set_tag(Ext::TAG_MSG_ID, payload[:message_id])
36
+
37
+ # Since email date can contain PII we disable by default
38
+ # Some of these fields can be either strings or arrays, so we try to normalize
39
+ # https://github.com/rails/rails/blob/18707ab17fa492eb25ad2e8f9818a320dc20b823/actionmailer/lib/action_mailer/base.rb#L742-L754
40
+ if configuration[:email_data] == true
41
+ span.set_tag(Ext::TAG_SUBJECT, payload[:subject].to_s) if payload[:subject]
42
+ span.set_tag(Ext::TAG_TO, payload[:to].join(',')) if payload[:to]
43
+ span.set_tag(Ext::TAG_FROM, payload[:from].join(',')) if payload[:from]
44
+ span.set_tag(Ext::TAG_BCC, payload[:bcc].join(',')) if payload[:bcc]
45
+ span.set_tag(Ext::TAG_CC, payload[:cc].join(',')) if payload[:cc]
46
+ span.set_tag(Ext::TAG_DATE, payload[:date].to_s) if payload[:date]
47
+ span.set_tag(Ext::TAG_PERFORM_DELIVERIES, payload[:perform_deliveries]) if payload[:perform_deliveries]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end