ddtrace 0.40.0 → 0.45.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +148 -130
  3. data/.circleci/images/primary/Dockerfile-3.0.0 +73 -0
  4. data/.github/workflows/add-milestone-to-pull-requests.yml +42 -0
  5. data/.github/workflows/create-next-milestone.yml +20 -0
  6. data/.simplecov +3 -0
  7. data/Appraisals +414 -135
  8. data/CHANGELOG.md +1112 -342
  9. data/CONTRIBUTING.md +2 -2
  10. data/Gemfile +4 -2
  11. data/README.md +1 -0
  12. data/Rakefile +231 -29
  13. data/ddtrace.gemspec +8 -8
  14. data/docker-compose.yml +30 -0
  15. data/docs/DevelopmentGuide.md +12 -2
  16. data/docs/GettingStarted.md +187 -16
  17. data/lib/ddtrace.rb +10 -0
  18. data/lib/ddtrace/auto_instrument.rb +3 -0
  19. data/lib/ddtrace/auto_instrument_base.rb +6 -0
  20. data/lib/ddtrace/buffer.rb +259 -52
  21. data/lib/ddtrace/configuration.rb +19 -0
  22. data/lib/ddtrace/configuration/options.rb +3 -1
  23. data/lib/ddtrace/configuration/settings.rb +9 -3
  24. data/lib/ddtrace/context.rb +18 -0
  25. data/lib/ddtrace/context_provider.rb +17 -5
  26. data/lib/ddtrace/contrib/action_cable/integration.rb +7 -0
  27. data/lib/ddtrace/contrib/action_pack/integration.rb +7 -0
  28. data/lib/ddtrace/contrib/action_view/event.rb +0 -4
  29. data/lib/ddtrace/contrib/action_view/events/render_partial.rb +1 -0
  30. data/lib/ddtrace/contrib/action_view/events/render_template.rb +1 -0
  31. data/lib/ddtrace/contrib/action_view/integration.rb +7 -0
  32. data/lib/ddtrace/contrib/active_record/events/sql.rb +4 -0
  33. data/lib/ddtrace/contrib/active_record/integration.rb +7 -0
  34. data/lib/ddtrace/contrib/active_record/utils.rb +67 -21
  35. data/lib/ddtrace/contrib/active_support/cache/instrumentation.rb +104 -3
  36. data/lib/ddtrace/contrib/active_support/cache/patcher.rb +21 -0
  37. data/lib/ddtrace/contrib/active_support/ext.rb +3 -0
  38. data/lib/ddtrace/contrib/active_support/integration.rb +7 -1
  39. data/lib/ddtrace/contrib/active_support/notifications/event.rb +10 -0
  40. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -2
  41. data/lib/ddtrace/contrib/auto_instrument.rb +48 -0
  42. data/lib/ddtrace/contrib/aws/instrumentation.rb +6 -1
  43. data/lib/ddtrace/contrib/aws/patcher.rb +0 -1
  44. data/lib/ddtrace/contrib/aws/services.rb +1 -0
  45. data/lib/ddtrace/contrib/configurable.rb +2 -0
  46. data/lib/ddtrace/contrib/configuration/resolvers/pattern_resolver.rb +6 -5
  47. data/lib/ddtrace/contrib/cucumber/configuration/settings.rb +38 -0
  48. data/lib/ddtrace/contrib/cucumber/ext.rb +19 -0
  49. data/lib/ddtrace/contrib/cucumber/formatter.rb +104 -0
  50. data/lib/ddtrace/contrib/cucumber/instrumentation.rb +24 -0
  51. data/lib/ddtrace/contrib/cucumber/integration.rb +45 -0
  52. data/lib/ddtrace/contrib/cucumber/patcher.rb +23 -0
  53. data/lib/ddtrace/contrib/dalli/instrumentation.rb +4 -0
  54. data/lib/ddtrace/contrib/delayed_job/configuration/settings.rb +2 -0
  55. data/lib/ddtrace/contrib/delayed_job/ext.rb +2 -0
  56. data/lib/ddtrace/contrib/delayed_job/plugin.rb +39 -15
  57. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +4 -0
  58. data/lib/ddtrace/contrib/ethon/easy_patch.rb +10 -7
  59. data/lib/ddtrace/contrib/ethon/ext.rb +1 -0
  60. data/lib/ddtrace/contrib/ethon/multi_patch.rb +4 -0
  61. data/lib/ddtrace/contrib/excon/middleware.rb +11 -1
  62. data/lib/ddtrace/contrib/extensions.rb +27 -1
  63. data/lib/ddtrace/contrib/faraday/middleware.rb +4 -0
  64. data/lib/ddtrace/contrib/faraday/patcher.rb +1 -1
  65. data/lib/ddtrace/contrib/grape/configuration/settings.rb +7 -0
  66. data/lib/ddtrace/contrib/grape/endpoint.rb +53 -18
  67. data/lib/ddtrace/contrib/grape/ext.rb +1 -0
  68. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +5 -1
  69. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +4 -0
  70. data/lib/ddtrace/contrib/http/instrumentation.rb +6 -2
  71. data/lib/ddtrace/contrib/httpclient/configuration/settings.rb +32 -0
  72. data/lib/ddtrace/contrib/httpclient/ext.rb +17 -0
  73. data/lib/ddtrace/contrib/httpclient/instrumentation.rb +152 -0
  74. data/lib/ddtrace/contrib/httpclient/integration.rb +43 -0
  75. data/lib/ddtrace/contrib/httpclient/patcher.rb +35 -0
  76. data/lib/ddtrace/contrib/httprb/instrumentation.rb +6 -3
  77. data/lib/ddtrace/contrib/kafka/event.rb +1 -1
  78. data/lib/ddtrace/contrib/mongodb/subscribers.rb +4 -0
  79. data/lib/ddtrace/contrib/mysql2/instrumentation.rb +4 -0
  80. data/lib/ddtrace/contrib/patchable.rb +18 -7
  81. data/lib/ddtrace/contrib/presto/instrumentation.rb +3 -0
  82. data/lib/ddtrace/contrib/qless/configuration/settings.rb +35 -0
  83. data/lib/ddtrace/contrib/qless/ext.rb +20 -0
  84. data/lib/ddtrace/contrib/qless/integration.rb +38 -0
  85. data/lib/ddtrace/contrib/qless/patcher.rb +35 -0
  86. data/lib/ddtrace/contrib/qless/qless_job.rb +72 -0
  87. data/lib/ddtrace/contrib/qless/tracer_cleaner.rb +32 -0
  88. data/lib/ddtrace/contrib/que/configuration/settings.rb +1 -0
  89. data/lib/ddtrace/contrib/que/tracer.rb +2 -1
  90. data/lib/ddtrace/contrib/racecar/event.rb +4 -0
  91. data/lib/ddtrace/contrib/rack/integration.rb +7 -0
  92. data/lib/ddtrace/contrib/rack/middlewares.rb +1 -1
  93. data/lib/ddtrace/contrib/rack/request_queue.rb +6 -1
  94. data/lib/ddtrace/contrib/rails/auto_instrument_railtie.rb +10 -0
  95. data/lib/ddtrace/contrib/rails/patcher.rb +19 -5
  96. data/lib/ddtrace/contrib/rails/utils.rb +4 -0
  97. data/lib/ddtrace/contrib/rake/integration.rb +1 -1
  98. data/lib/ddtrace/contrib/redis/configuration/resolver.rb +3 -1
  99. data/lib/ddtrace/contrib/redis/configuration/settings.rb +5 -0
  100. data/lib/ddtrace/contrib/redis/ext.rb +1 -0
  101. data/lib/ddtrace/contrib/redis/patcher.rb +20 -3
  102. data/lib/ddtrace/contrib/redis/quantize.rb +27 -0
  103. data/lib/ddtrace/contrib/redis/tags.rb +9 -1
  104. data/lib/ddtrace/contrib/resque/configuration/settings.rb +1 -0
  105. data/lib/ddtrace/contrib/resque/integration.rb +1 -1
  106. data/lib/ddtrace/contrib/resque/resque_job.rb +1 -1
  107. data/lib/ddtrace/contrib/rest_client/request_patch.rb +4 -0
  108. data/lib/ddtrace/contrib/rspec/configuration/settings.rb +38 -0
  109. data/lib/ddtrace/contrib/rspec/example.rb +61 -0
  110. data/lib/ddtrace/contrib/rspec/example_group.rb +61 -0
  111. data/lib/ddtrace/contrib/rspec/ext.rb +19 -0
  112. data/lib/ddtrace/contrib/rspec/integration.rb +46 -0
  113. data/lib/ddtrace/contrib/rspec/patcher.rb +25 -0
  114. data/lib/ddtrace/contrib/sequel/database.rb +3 -1
  115. data/lib/ddtrace/contrib/sequel/dataset.rb +3 -2
  116. data/lib/ddtrace/contrib/sequel/ext.rb +1 -0
  117. data/lib/ddtrace/contrib/sequel/utils.rb +16 -5
  118. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +1 -0
  119. data/lib/ddtrace/contrib/shoryuken/tracer.rb +4 -1
  120. data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +1 -0
  121. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +4 -1
  122. data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +2 -2
  123. data/lib/ddtrace/contrib/sneakers/configuration/settings.rb +1 -0
  124. data/lib/ddtrace/contrib/sneakers/tracer.rb +17 -20
  125. data/lib/ddtrace/contrib/status_code_matcher.rb +67 -0
  126. data/lib/ddtrace/ext/app_types.rb +1 -0
  127. data/lib/ddtrace/ext/ci.rb +297 -0
  128. data/lib/ddtrace/ext/distributed.rb +8 -2
  129. data/lib/ddtrace/ext/git.rb +11 -0
  130. data/lib/ddtrace/ext/integration.rb +8 -0
  131. data/lib/ddtrace/ext/runtime.rb +2 -0
  132. data/lib/ddtrace/ext/test.rb +24 -0
  133. data/lib/ddtrace/opentracer/distributed_headers.rb +1 -1
  134. data/lib/ddtrace/propagation/grpc_propagator.rb +18 -6
  135. data/lib/ddtrace/propagation/http_propagator.rb +17 -2
  136. data/lib/ddtrace/runtime/identity.rb +4 -5
  137. data/lib/ddtrace/runtime/metrics.rb +6 -2
  138. data/lib/ddtrace/sampler.rb +2 -2
  139. data/lib/ddtrace/sampling/rate_limiter.rb +65 -16
  140. data/lib/ddtrace/span.rb +152 -27
  141. data/lib/ddtrace/tracer.rb +25 -13
  142. data/lib/ddtrace/transport/http/adapters/net.rb +8 -2
  143. data/lib/ddtrace/transport/http/statistics.rb +14 -1
  144. data/lib/ddtrace/transport/traces.rb +7 -2
  145. data/lib/ddtrace/utils.rb +16 -13
  146. data/lib/ddtrace/utils/forking.rb +52 -0
  147. data/lib/ddtrace/version.rb +1 -1
  148. data/lib/ddtrace/workers/runtime_metrics.rb +7 -3
  149. data/lib/ddtrace/writer.rb +19 -1
  150. metadata +111 -19
@@ -26,7 +26,9 @@ module Datadog
26
26
  builder = OptionDefinition::Builder.new(name, meta, &block)
27
27
  options[name] = builder.to_definition.tap do
28
28
  # Resolve and define helper functions
29
- helpers = default_helpers(name).merge(builder.helpers)
29
+ helpers = default_helpers(name)
30
+ # Prevent unnecessary creation of an identical copy of helpers if there's nothing to merge
31
+ helpers = helpers.merge(builder.helpers) unless builder.helpers.empty?
30
32
  define_helpers(helpers)
31
33
  end
32
34
  end
@@ -40,6 +40,11 @@ module Datadog
40
40
  option :debug do |o|
41
41
  o.default { env_to_bool(Datadog::Ext::Diagnostics::DD_TRACE_DEBUG, false) }
42
42
  o.lazy
43
+ o.on_set do |enabled|
44
+ # Enable rich debug print statements.
45
+ # We do not need to unnecessarily load 'pp' unless in debugging mode.
46
+ require 'pp' if enabled
47
+ end
43
48
  end
44
49
 
45
50
  settings :health_metrics do
@@ -64,7 +69,8 @@ module Datadog
64
69
  option :propagation_extract_style do |o|
65
70
  o.default do
66
71
  # Look for all headers by default
67
- env_to_list(Ext::DistributedTracing::PROPAGATION_EXTRACT_STYLE_ENV,
72
+ env_to_list([Ext::DistributedTracing::PROPAGATION_STYLE_EXTRACT_ENV,
73
+ Ext::DistributedTracing::PROPAGATION_EXTRACT_STYLE_ENV_OLD],
68
74
  [Ext::DistributedTracing::PROPAGATION_STYLE_DATADOG,
69
75
  Ext::DistributedTracing::PROPAGATION_STYLE_B3,
70
76
  Ext::DistributedTracing::PROPAGATION_STYLE_B3_SINGLE_HEADER])
@@ -76,7 +82,8 @@ module Datadog
76
82
  option :propagation_inject_style do |o|
77
83
  o.default do
78
84
  # Only inject Datadog headers by default
79
- env_to_list(Ext::DistributedTracing::PROPAGATION_INJECT_STYLE_ENV,
85
+ env_to_list([Ext::DistributedTracing::PROPAGATION_STYLE_INJECT_ENV,
86
+ Ext::DistributedTracing::PROPAGATION_INJECT_STYLE_ENV_OLD],
80
87
  [Ext::DistributedTracing::PROPAGATION_STYLE_DATADOG])
81
88
  end
82
89
 
@@ -91,7 +98,6 @@ module Datadog
91
98
 
92
99
  settings :logger do
93
100
  option :instance do |o|
94
- o.setter { |value, old_value| value.is_a?(::Logger) ? value : old_value }
95
101
  o.on_set { |value| set_option(:level, value.level) unless value.nil? }
96
102
  end
97
103
 
@@ -3,6 +3,7 @@ require 'ddtrace/diagnostics/health'
3
3
 
4
4
  require 'ddtrace/context_flush'
5
5
  require 'ddtrace/context_provider'
6
+ require 'ddtrace/utils/forking'
6
7
 
7
8
  module Datadog
8
9
  # \Context is used to keep track of a hierarchy of spans for the current
@@ -19,6 +20,8 @@ module Datadog
19
20
  # This data structure is thread-safe.
20
21
  # rubocop:disable Metrics/ClassLength
21
22
  class Context
23
+ include Datadog::Utils::Forking
24
+
22
25
  # 100k spans is about a 100Mb footprint
23
26
  DEFAULT_MAX_LENGTH = 100_000
24
27
 
@@ -232,6 +235,21 @@ module Datadog
232
235
  end
233
236
  end
234
237
 
238
+ # Generates equivalent context for forked processes.
239
+ #
240
+ # When Context from parent process is forked, child process
241
+ # should have a Context belonging to the same trace but not
242
+ # have the parent process spans.
243
+ def fork_clone
244
+ self.class.new(
245
+ trace_id: trace_id,
246
+ span_id: span_id,
247
+ sampled: sampled?,
248
+ sampling_priority: sampling_priority,
249
+ origin: origin
250
+ )
251
+ end
252
+
235
253
  private
236
254
 
237
255
  def reset(options = {})
@@ -13,9 +13,20 @@ module Datadog
13
13
  @context.local = ctx
14
14
  end
15
15
 
16
- # Return the current context.
17
- def context
18
- @context.local
16
+ # Return the local context.
17
+ def context(key = nil)
18
+ current_context = key.nil? ? @context.local : @context.local(key)
19
+
20
+ # Rebuild/reset context after a fork
21
+ #
22
+ # We don't want forked processes to copy and retransmit spans
23
+ # that were generated from the parent process. Reset it such
24
+ # that it acts like a distributed trace.
25
+ current_context.after_fork! do
26
+ current_context = self.context = current_context.fork_clone
27
+ end
28
+
29
+ current_context
19
30
  end
20
31
  end
21
32
 
@@ -43,8 +54,9 @@ module Datadog
43
54
  end
44
55
 
45
56
  # Return the thread-local context.
46
- def local
47
- Thread.current[@key] ||= Datadog::Context.new
57
+ def local(thread = Thread.current)
58
+ raise ArgumentError, '\'thread\' must be a Thread.' unless thread.is_a?(Thread)
59
+ thread[@key] ||= Datadog::Context.new
48
60
  end
49
61
  end
50
62
  end
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/contrib/integration'
2
2
  require 'ddtrace/contrib/action_cable/configuration/settings'
3
3
  require 'ddtrace/contrib/action_cable/patcher'
4
+ require 'ddtrace/contrib/rails/utils'
4
5
 
5
6
  module Datadog
6
7
  module Contrib
@@ -25,6 +26,12 @@ module Datadog
25
26
  super && version >= MINIMUM_VERSION
26
27
  end
27
28
 
29
+ # enabled by rails integration so should only auto instrument
30
+ # if detected that it is being used without rails
31
+ def auto_instrument?
32
+ !Datadog::Contrib::Rails::Utils.railtie_supported?
33
+ end
34
+
28
35
  def default_configuration
29
36
  Configuration::Settings.new
30
37
  end
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/contrib/integration'
2
2
  require 'ddtrace/contrib/action_pack/configuration/settings'
3
3
  require 'ddtrace/contrib/action_pack/patcher'
4
+ require 'ddtrace/contrib/rails/utils'
4
5
 
5
6
  module Datadog
6
7
  module Contrib
@@ -25,6 +26,12 @@ module Datadog
25
26
  super && version >= MINIMUM_VERSION
26
27
  end
27
28
 
29
+ # enabled by rails integration so should only auto instrument
30
+ # if detected that it is being used without rails
31
+ def auto_instrument?
32
+ !Datadog::Contrib::Rails::Utils.railtie_supported?
33
+ end
34
+
28
35
  def default_configuration
29
36
  Configuration::Settings.new
30
37
  end
@@ -12,10 +12,6 @@ module Datadog
12
12
 
13
13
  # Class methods for ActionView events.
14
14
  module ClassMethods
15
- def span_options
16
- { service: configuration[:service_name] }
17
- end
18
-
19
15
  def tracer
20
16
  -> { configuration[:tracer] }
21
17
  end
@@ -24,6 +24,7 @@ module Datadog
24
24
  end
25
25
 
26
26
  def process(span, _event, _id, payload)
27
+ span.service = configuration[:service_name]
27
28
  span.span_type = Datadog::Ext::HTTP::TEMPLATE
28
29
 
29
30
  if (template_name = Utils.normalize_template_name(payload[:identifier]))
@@ -24,6 +24,7 @@ module Datadog
24
24
  end
25
25
 
26
26
  def process(span, _event, _id, payload)
27
+ span.service = configuration[:service_name]
27
28
  span.span_type = Datadog::Ext::HTTP::TEMPLATE
28
29
 
29
30
  if (template_name = Utils.normalize_template_name(payload[:identifier]))
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/contrib/integration'
2
2
  require 'ddtrace/contrib/action_view/configuration/settings'
3
3
  require 'ddtrace/contrib/action_view/patcher'
4
+ require 'ddtrace/contrib/rails/utils'
4
5
 
5
6
  module Datadog
6
7
  module Contrib
@@ -32,6 +33,12 @@ module Datadog
32
33
  super && version >= MINIMUM_VERSION
33
34
  end
34
35
 
36
+ # enabled by rails integration so should only auto instrument
37
+ # if detected that it is being used without rails
38
+ def auto_instrument?
39
+ !Datadog::Contrib::Rails::Utils.railtie_supported?
40
+ end
41
+
35
42
  def default_configuration
36
43
  Configuration::Settings.new
37
44
  end
@@ -1,3 +1,4 @@
1
+ require 'ddtrace/ext/integration'
1
2
  require 'ddtrace/ext/net'
2
3
  require 'ddtrace/contrib/analytics'
3
4
  require 'ddtrace/contrib/active_record/ext'
@@ -39,6 +40,9 @@ module Datadog
39
40
  span.resource = payload.fetch(:sql)
40
41
  span.span_type = Datadog::Ext::SQL::TYPE
41
42
 
43
+ # Tag as an external peer service
44
+ span.set_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE, span.service)
45
+
42
46
  # Set analytics sample rate
43
47
  if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
44
48
  Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
@@ -5,6 +5,7 @@ require 'ddtrace/contrib/active_record/events'
5
5
  require 'ddtrace/contrib/active_record/configuration/resolver'
6
6
  require 'ddtrace/contrib/active_record/configuration/settings'
7
7
  require 'ddtrace/contrib/active_record/patcher'
8
+ require 'ddtrace/contrib/rails/utils'
8
9
 
9
10
  module Datadog
10
11
  module Contrib
@@ -29,6 +30,12 @@ module Datadog
29
30
  super && version >= MINIMUM_VERSION
30
31
  end
31
32
 
33
+ # enabled by rails integration so should only auto instrument
34
+ # if detected that it is being used without rails
35
+ def auto_instrument?
36
+ !Datadog::Contrib::Rails::Utils.railtie_supported?
37
+ end
38
+
32
39
  def default_configuration
33
40
  Configuration::Settings.new
34
41
  end
@@ -1,3 +1,5 @@
1
+ require 'ddtrace/ext/runtime'
2
+
1
3
  module Datadog
2
4
  module Contrib
3
5
  module ActiveRecord
@@ -21,42 +23,77 @@ module Datadog
21
23
  connection_config[:port]
22
24
  end
23
25
 
24
- # In newer Rails versions, the `payload` contains both the `connection` and its `object_id` named `connection_id`.
25
- #
26
- # So, if rails is recent we'll have a direct access to the connection.
27
- # Else, we'll find it thanks to the passed `connection_id`.
26
+ # Returns the connection configuration hash from the
27
+ # current connection
28
28
  #
29
- # See this PR for more details: https://github.com/rails/rails/pull/34602
29
+ # Since Rails 6.0, we have direct access to the object,
30
+ # while older versions of Rails only provide us the
31
+ # connection id.
30
32
  #
33
+ # @see https://github.com/rails/rails/pull/34602
31
34
  def self.connection_config(connection = nil, connection_id = nil)
32
35
  return default_connection_config if connection.nil? && connection_id.nil?
33
36
 
34
37
  conn = if !connection.nil?
38
+ # Since Rails 6.0, the connection object
39
+ # is directly available.
35
40
  connection
36
- # Rails 3.0 - 3.2
37
- elsif Gem.loaded_specs['activerecord'].version < Gem::Version.new('4.0')
38
- ::ActiveRecord::Base
39
- .connection_handler
40
- .connection_pools
41
- .values
42
- .flat_map(&:connections)
43
- .find { |c| c.object_id == connection_id }
44
- # Rails 4.2+
45
41
  else
46
- ::ActiveRecord::Base
47
- .connection_handler
48
- .connection_pool_list
49
- .flat_map(&:connections)
50
- .find { |c| c.object_id == connection_id }
42
+ # For Rails < 6.0, only the `connection_id`
43
+ # is available. We have to find the connection
44
+ # object from it.
45
+ connection_from_id(connection_id)
51
46
  end
52
47
 
53
- if conn.instance_variable_defined?(:@config)
48
+ if conn && conn.instance_variable_defined?(:@config)
54
49
  conn.instance_variable_get(:@config)
55
50
  else
56
51
  EMPTY_CONFIG
57
52
  end
58
53
  end
59
54
 
55
+ # DEV: JRuby responds to {ObjectSpace._id2ref}, despite raising an error
56
+ # DEV: when invoked. Thus, we have to explicitly check for Ruby runtime.
57
+ if Datadog::Ext::Runtime::RUBY_ENGINE != 'jruby'
58
+ # CRuby has access to {ObjectSpace._id2ref}, which allows for
59
+ # direct look up of the connection object.
60
+ def self.connection_from_id(connection_id)
61
+ # `connection_id` is the `#object_id` of the
62
+ # connection. We can perform an ObjectSpace
63
+ # lookup to find it.
64
+ #
65
+ # This works not only for ActiveRecord, but for
66
+ # extensions that might have their own connection
67
+ # pool (e.g. https://rubygems.org/gems/makara).
68
+ ObjectSpace._id2ref(connection_id)
69
+ rescue => e
70
+ # Because `connection_id` references a live connection
71
+ # present in the current stack, it is very unlikely that
72
+ # `_id2ref` will fail, but we add this safeguard just
73
+ # in case.
74
+ Datadog.logger.debug(
75
+ "connection_id #{connection_id} does not represent a valid object. " \
76
+ "Cause: #{e.message} Source: #{e.backtrace.first}"
77
+ )
78
+ end
79
+ else
80
+ # JRuby does not enable {ObjectSpace._id2ref} by default,
81
+ # as it has large performance impact:
82
+ # https://github.com/jruby/jruby/wiki/PerformanceTuning/cf155dd9#dont-enable-objectspace
83
+ #
84
+ # This fallback code does not support the makara gem,
85
+ # as its connections don't live in the ActiveRecord
86
+ # connection pool.
87
+ def self.connection_from_id(connection_id)
88
+ ::ActiveRecord::Base
89
+ .connection_handler
90
+ .connection_pool_list
91
+ .flat_map(&:connections)
92
+ .find { |c| c.object_id == connection_id }
93
+ end
94
+ end
95
+
96
+ # @return [Hash]
60
97
  def self.default_connection_config
61
98
  return @default_connection_config if instance_variable_defined?(:@default_connection_config)
62
99
  current_connection_name = if ::ActiveRecord::Base.respond_to?(:connection_specification_name)
@@ -66,10 +103,19 @@ module Datadog
66
103
  end
67
104
 
68
105
  connection_pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(current_connection_name)
69
- connection_pool.nil? ? EMPTY_CONFIG : (@default_connection_config = connection_pool.spec.config)
106
+ connection_pool.nil? ? EMPTY_CONFIG : (@default_connection_config = db_config(connection_pool))
70
107
  rescue StandardError
71
108
  EMPTY_CONFIG
72
109
  end
110
+
111
+ # @return [Hash]
112
+ def self.db_config(connection_pool)
113
+ if ::Rails::VERSION::MAJOR >= 6 && ::Rails::VERSION::MINOR >= 1
114
+ connection_pool.db_config.configuration_hash
115
+ else
116
+ connection_pool.spec.config
117
+ end
118
+ end
73
119
  end
74
120
  end
75
121
  end
@@ -18,9 +18,13 @@ module Datadog
18
18
  # NOTE: the ``finish_trace_cache()`` is fired but it already has a safe-guard
19
19
  # to avoid any kind of issue.
20
20
  current_span = tracer.active_span
21
- return if payload[:action] == Ext::RESOURCE_CACHE_GET &&
22
- current_span.try(:name) == Ext::SPAN_CACHE &&
23
- current_span.try(:resource) == Ext::RESOURCE_CACHE_GET
21
+ return if current_span.try(:name) == Ext::SPAN_CACHE &&
22
+ (
23
+ payload[:action] == Ext::RESOURCE_CACHE_GET &&
24
+ current_span.try(:resource) == Ext::RESOURCE_CACHE_GET ||
25
+ payload[:action] == Ext::RESOURCE_CACHE_MGET &&
26
+ current_span.try(:resource) == Ext::RESOURCE_CACHE_MGET
27
+ )
24
28
 
25
29
  tracing_context = payload.fetch(:tracing_context)
26
30
 
@@ -59,6 +63,32 @@ module Datadog
59
63
  Datadog.logger.debug(e.message)
60
64
  end
61
65
 
66
+ def finish_trace_cache_multi(payload)
67
+ # retrieve the tracing context and continue the trace
68
+ tracing_context = payload.fetch(:tracing_context)
69
+ span = tracing_context[:dd_cache_span]
70
+ return unless span && !span.finished?
71
+
72
+ begin
73
+ # discard parameters from the cache_store configuration
74
+ if defined?(::Rails)
75
+ store, = *Array.wrap(::Rails.configuration.cache_store).flatten
76
+ span.set_tag(Ext::TAG_CACHE_BACKEND, store)
77
+ end
78
+ normalized_keys = payload.fetch(:keys, []).map do |key|
79
+ ::ActiveSupport::Cache.expand_cache_key(key)
80
+ end
81
+ cache_keys = Datadog::Utils.truncate(normalized_keys, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
82
+ span.set_tag(Ext::TAG_CACHE_KEY_MULTI, cache_keys)
83
+
84
+ span.set_error(payload[:exception]) if payload[:exception]
85
+ ensure
86
+ span.finish
87
+ end
88
+ rescue StandardError => e
89
+ Datadog.logger.debug(e.message)
90
+ end
91
+
62
92
  # Defines instrumentation for ActiveSupport cache reading
63
93
  module Read
64
94
  def read(*args, &block)
@@ -82,6 +112,29 @@ module Datadog
82
112
  end
83
113
  end
84
114
 
115
+ # Defines instrumentation for ActiveSupport cache reading of multiple keys
116
+ module ReadMulti
117
+ def read_multi(*keys, &block)
118
+ payload = {
119
+ action: Ext::RESOURCE_CACHE_MGET,
120
+ keys: keys,
121
+ tracing_context: {}
122
+ }
123
+
124
+ begin
125
+ # process and catch cache exceptions
126
+ Instrumentation.start_trace_cache(payload)
127
+ super
128
+ rescue Exception => e
129
+ payload[:exception] = [e.class.name, e.message]
130
+ payload[:exception_object] = e
131
+ raise e
132
+ end
133
+ ensure
134
+ Instrumentation.finish_trace_cache_multi(payload)
135
+ end
136
+ end
137
+
85
138
  # Defines instrumentation for ActiveSupport cache fetching
86
139
  module Fetch
87
140
  def fetch(*args, &block)
@@ -105,6 +158,31 @@ module Datadog
105
158
  end
106
159
  end
107
160
 
161
+ # Defines instrumentation for ActiveSupport cache fetching of multiple keys
162
+ module FetchMulti
163
+ def fetch_multi(*args, &block)
164
+ # extract options hash
165
+ keys = args[-1].instance_of?(Hash) ? args[0..-2] : args
166
+ payload = {
167
+ action: Ext::RESOURCE_CACHE_MGET,
168
+ keys: keys,
169
+ tracing_context: {}
170
+ }
171
+
172
+ begin
173
+ # process and catch cache exceptions
174
+ Instrumentation.start_trace_cache(payload)
175
+ super
176
+ rescue Exception => e
177
+ payload[:exception] = [e.class.name, e.message]
178
+ payload[:exception_object] = e
179
+ raise e
180
+ end
181
+ ensure
182
+ Instrumentation.finish_trace_cache_multi(payload)
183
+ end
184
+ end
185
+
108
186
  # Defines instrumentation for ActiveSupport cache writing
109
187
  module Write
110
188
  def write(*args, &block)
@@ -128,6 +206,29 @@ module Datadog
128
206
  end
129
207
  end
130
208
 
209
+ # Defines instrumentation for ActiveSupport cache writing of multiple keys
210
+ module WriteMulti
211
+ def write_multi(hash, options = nil)
212
+ payload = {
213
+ action: Ext::RESOURCE_CACHE_MSET,
214
+ keys: hash.keys,
215
+ tracing_context: {}
216
+ }
217
+
218
+ begin
219
+ # process and catch cache exceptions
220
+ Instrumentation.start_trace_cache(payload)
221
+ super
222
+ rescue Exception => e
223
+ payload[:exception] = [e.class.name, e.message]
224
+ payload[:exception_object] = e
225
+ raise e
226
+ end
227
+ ensure
228
+ Instrumentation.finish_trace_cache_multi(payload)
229
+ end
230
+ end
231
+
131
232
  # Defines instrumentation for ActiveSupport cache deleting
132
233
  module Delete
133
234
  def delete(*args, &block)