ddtrace 0.40.0 → 0.45.0

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