ddtrace 0.43.0 → 0.44.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 (41) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +100 -129
  3. data/.circleci/images/primary/Dockerfile-3.0.0 +73 -0
  4. data/.github/workflows/add-milestone-to-pull-requests.yml +1 -1
  5. data/.simplecov +4 -1
  6. data/Appraisals +200 -8
  7. data/CHANGELOG.md +1005 -376
  8. data/Gemfile +4 -2
  9. data/Rakefile +121 -4
  10. data/ddtrace.gemspec +5 -8
  11. data/docker-compose.yml +30 -0
  12. data/docs/GettingStarted.md +47 -8
  13. data/lib/ddtrace.rb +1 -0
  14. data/lib/ddtrace/contrib/action_view/event.rb +0 -4
  15. data/lib/ddtrace/contrib/action_view/events/render_partial.rb +1 -0
  16. data/lib/ddtrace/contrib/action_view/events/render_template.rb +1 -0
  17. data/lib/ddtrace/contrib/active_record/utils.rb +11 -1
  18. data/lib/ddtrace/contrib/aws/services.rb +1 -0
  19. data/lib/ddtrace/contrib/configuration/resolvers/pattern_resolver.rb +2 -0
  20. data/lib/ddtrace/contrib/ethon/easy_patch.rb +1 -1
  21. data/lib/ddtrace/contrib/ethon/ext.rb +1 -0
  22. data/lib/ddtrace/contrib/grape/endpoint.rb +29 -11
  23. data/lib/ddtrace/contrib/grape/ext.rb +1 -0
  24. data/lib/ddtrace/contrib/httprb/instrumentation.rb +1 -1
  25. data/lib/ddtrace/contrib/qless/configuration/settings.rb +35 -0
  26. data/lib/ddtrace/contrib/qless/ext.rb +20 -0
  27. data/lib/ddtrace/contrib/qless/integration.rb +38 -0
  28. data/lib/ddtrace/contrib/qless/patcher.rb +35 -0
  29. data/lib/ddtrace/contrib/qless/qless_job.rb +72 -0
  30. data/lib/ddtrace/contrib/qless/tracer_cleaner.rb +32 -0
  31. data/lib/ddtrace/contrib/redis/configuration/resolver.rb +3 -1
  32. data/lib/ddtrace/contrib/redis/configuration/settings.rb +5 -0
  33. data/lib/ddtrace/contrib/redis/ext.rb +1 -0
  34. data/lib/ddtrace/contrib/redis/patcher.rb +20 -3
  35. data/lib/ddtrace/contrib/redis/quantize.rb +27 -0
  36. data/lib/ddtrace/contrib/redis/tags.rb +5 -1
  37. data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +2 -2
  38. data/lib/ddtrace/ext/ci.rb +0 -1
  39. data/lib/ddtrace/version.rb +1 -1
  40. data/lib/ddtrace/workers/runtime_metrics.rb +7 -3
  41. metadata +77 -29
@@ -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]))
@@ -57,6 +57,7 @@ module Datadog
57
57
  end
58
58
  end
59
59
 
60
+ # @return [Hash]
60
61
  def self.default_connection_config
61
62
  return @default_connection_config if instance_variable_defined?(:@default_connection_config)
62
63
  current_connection_name = if ::ActiveRecord::Base.respond_to?(:connection_specification_name)
@@ -66,10 +67,19 @@ module Datadog
66
67
  end
67
68
 
68
69
  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)
70
+ connection_pool.nil? ? EMPTY_CONFIG : (@default_connection_config = db_config(connection_pool))
70
71
  rescue StandardError
71
72
  EMPTY_CONFIG
72
73
  end
74
+
75
+ # @return [Hash]
76
+ def self.db_config(connection_pool)
77
+ if ::Rails::VERSION::MAJOR >= 6 && ::Rails::VERSION::MINOR >= 1
78
+ connection_pool.db_config.configuration_hash
79
+ else
80
+ connection_pool.spec.config
81
+ end
82
+ end
73
83
  end
74
84
  end
75
85
  end
@@ -104,6 +104,7 @@ module Datadog
104
104
  States
105
105
  StorageGateway
106
106
  Support
107
+ Textract
107
108
  WAF
108
109
  WAFRegional
109
110
  WorkDocs
@@ -8,6 +8,8 @@ module Datadog
8
8
  # Matches strings against Regexps.
9
9
  class PatternResolver < Datadog::Contrib::Configuration::Resolver
10
10
  def resolve(name)
11
+ return if patterns.empty?
12
+
11
13
  # Try to find a matching pattern
12
14
  matching_pattern = patterns.find do |pattern|
13
15
  if pattern.is_a?(Proc)
@@ -99,7 +99,7 @@ module Datadog
99
99
 
100
100
  def datadog_tag_request
101
101
  span = @datadog_span
102
- method = 'N/A'
102
+ method = Ext::NOT_APPLICABLE_METHOD
103
103
  if instance_variable_defined?(:@datadog_method) && !@datadog_method.nil?
104
104
  method = @datadog_method.to_s
105
105
  end
@@ -12,6 +12,7 @@ module Datadog
12
12
  SERVICE_NAME = 'ethon'.freeze
13
13
  SPAN_REQUEST = 'ethon.request'.freeze
14
14
  SPAN_MULTI_REQUEST = 'ethon.multi.request'.freeze
15
+ NOT_APPLICABLE_METHOD = 'N/A'.freeze
15
16
  end
16
17
  end
17
18
  end
@@ -61,17 +61,12 @@ module Datadog
61
61
  begin
62
62
  # collect endpoint details
63
63
  api = payload[:endpoint].options[:for]
64
- # If the API inherits from Grape::API in version >= 1.2.0
65
- # then the API will be an instance and the name must be derived from the base.
66
- # See https://github.com/ruby-grape/grape/issues/1825
67
- api_view = if defined?(::Grape::API::Instance) && api <= ::Grape::API::Instance
68
- api.base.to_s
69
- else
70
- api.to_s
71
- end
72
-
73
- path = payload[:endpoint].options[:path].join('/')
74
- resource = "#{api_view}##{path}"
64
+
65
+ api_view = api_view(api)
66
+
67
+ request_method = payload[:endpoint].options[:method].first
68
+ path = endpoint_expand_path(payload[:endpoint])
69
+ resource = "#{api_view} #{request_method} #{path}"
75
70
  span.resource = resource
76
71
 
77
72
  # set the request span resource if it's a `rack.request` span
@@ -97,6 +92,10 @@ module Datadog
97
92
  # override the current span with this notification values
98
93
  span.set_tag(Ext::TAG_ROUTE_ENDPOINT, api_view) unless api_view.nil?
99
94
  span.set_tag(Ext::TAG_ROUTE_PATH, path)
95
+ span.set_tag(Ext::TAG_ROUTE_METHOD, request_method)
96
+
97
+ span.set_tag(Datadog::Ext::HTTP::METHOD, request_method)
98
+ span.set_tag(Datadog::Ext::HTTP::URL, path)
100
99
  ensure
101
100
  span.start(start)
102
101
  span.finish(finish)
@@ -187,6 +186,25 @@ module Datadog
187
186
 
188
187
  private
189
188
 
189
+ def api_view(api)
190
+ # If the API inherits from Grape::API in version >= 1.2.0
191
+ # then the API will be an instance and the name must be derived from the base.
192
+ # See https://github.com/ruby-grape/grape/issues/1825
193
+ if defined?(::Grape::API::Instance) && api <= ::Grape::API::Instance
194
+ api.base.to_s
195
+ else
196
+ api.to_s
197
+ end
198
+ end
199
+
200
+ def endpoint_expand_path(endpoint)
201
+ route_path = endpoint.options[:path]
202
+ namespace = endpoint.routes.first && endpoint.routes.first.namespace || ''
203
+
204
+ parts = (namespace.split('/') + route_path).reject { |p| p.blank? || p.eql?('/') }
205
+ parts.join('/').prepend('/')
206
+ end
207
+
190
208
  def tracer
191
209
  datadog_configuration[:tracer]
192
210
  end
@@ -16,6 +16,7 @@ module Datadog
16
16
  TAG_FILTER_TYPE = 'grape.filter.type'.freeze
17
17
  TAG_ROUTE_ENDPOINT = 'grape.route.endpoint'.freeze
18
18
  TAG_ROUTE_PATH = 'grape.route.path'.freeze
19
+ TAG_ROUTE_METHOD = 'grape.route.method'.freeze
19
20
  end
20
21
  end
21
22
  end
@@ -40,7 +40,7 @@ module Datadog
40
40
  # Add additional request specific tags to the span.
41
41
  annotate_span_with_request!(span, req, request_options)
42
42
  rescue StandardError => e
43
- logger.error("error preparing span for http.rb request: #{e}, Soure: #{e.backtrace}")
43
+ logger.error("error preparing span for http.rb request: #{e}, Source: #{e.backtrace}")
44
44
  ensure
45
45
  res = super(req, options)
46
46
  end
@@ -0,0 +1,35 @@
1
+ require 'ddtrace/contrib/configuration/settings'
2
+ require 'ddtrace/contrib/qless/ext'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Qless
7
+ module Configuration
8
+ # Custom settings for the Qless integration
9
+ class Settings < Contrib::Configuration::Settings
10
+ option :analytics_enabled do |o|
11
+ o.default { env_to_bool(Ext::ENV_ANALYTICS_ENABLED, false) }
12
+ o.lazy
13
+ end
14
+
15
+ option :analytics_sample_rate do |o|
16
+ o.default { env_to_float(Ext::ENV_ANALYTICS_SAMPLE_RATE, 1.0) }
17
+ o.lazy
18
+ end
19
+
20
+ option :tag_job_data do |o|
21
+ o.default { env_to_bool(Ext::ENV_TAG_JOB_DATA, false) }
22
+ o.lazy
23
+ end
24
+
25
+ option :tag_job_tags do |o|
26
+ o.default { env_to_bool(Ext::ENV_TAG_JOB_TAGS, false) }
27
+ o.lazy
28
+ end
29
+
30
+ option :service_name, default: Ext::SERVICE_NAME
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Qless
4
+ # Qless integration constants
5
+ module Ext
6
+ APP = 'qless'.freeze
7
+ ENV_ANALYTICS_ENABLED = 'DD_QLESS_ANALYTICS_ENABLED'.freeze
8
+ ENV_ANALYTICS_SAMPLE_RATE = 'DD_QLESS_ANALYTICS_SAMPLE_RATE'.freeze
9
+ ENV_TAG_JOB_DATA = 'DD_QLESS_TAG_JOB_DATA'.freeze
10
+ ENV_TAG_JOB_TAGS = 'DD_QLESS_TAG_JOB_TAGS'.freeze
11
+ SERVICE_NAME = 'qless'.freeze
12
+ SPAN_JOB = 'qless.job'.freeze
13
+ TAG_JOB_ID = 'qless.job.id'.freeze
14
+ TAG_JOB_DATA = 'qless.job.data'.freeze
15
+ TAG_JOB_QUEUE = 'qless.job.queue'.freeze
16
+ TAG_JOB_TAGS = 'qless.job.tags'.freeze
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ require 'ddtrace/contrib/integration'
2
+ require 'ddtrace/contrib/qless/configuration/settings'
3
+ require 'ddtrace/contrib/qless/patcher'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module Qless
8
+ # Description of Qless integration
9
+ class Integration
10
+ include Contrib::Integration
11
+
12
+ MINIMUM_VERSION = Gem::Version.new('0.10.0')
13
+
14
+ register_as :qless, auto_patch: true
15
+
16
+ def self.version
17
+ Gem.loaded_specs['qless'] && Gem.loaded_specs['qless'].version
18
+ end
19
+
20
+ def self.loaded?
21
+ !defined?(::Qless).nil?
22
+ end
23
+
24
+ def self.compatible?
25
+ super && version >= MINIMUM_VERSION
26
+ end
27
+
28
+ def default_configuration
29
+ Configuration::Settings.new
30
+ end
31
+
32
+ def patcher
33
+ Patcher
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'ddtrace/contrib/patcher'
2
+ require 'ddtrace/ext/app_types'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Qless
7
+ # Patcher enables patching of 'qless' module.
8
+ module Patcher
9
+ include Contrib::Patcher
10
+
11
+ module_function
12
+
13
+ def target_version
14
+ Integration.version
15
+ end
16
+
17
+ def patch
18
+ require_relative 'qless_job'
19
+ require_relative 'tracer_cleaner'
20
+
21
+ # Instrument all Qless Workers
22
+ ::Qless::Workers::BaseWorker.class_eval do
23
+ # These are executed in inverse order of listing here
24
+ include QlessJob
25
+ include TracerCleaner
26
+ end
27
+ end
28
+
29
+ def get_option(option)
30
+ Datadog.configuration[:qless].get_option(option)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,72 @@
1
+ require 'ddtrace/ext/app_types'
2
+ require 'ddtrace/contrib/analytics'
3
+ require 'qless'
4
+
5
+ module Datadog
6
+ module Contrib
7
+ module Qless
8
+ # Uses Qless job hooks to create traces
9
+ module QlessJob
10
+ def around_perform(job)
11
+ return super unless datadog_configuration && tracer
12
+ tracer.trace(Ext::SPAN_JOB, span_options) do |span|
13
+ span.resource = job.klass_name
14
+ span.span_type = Datadog::Ext::AppTypes::WORKER
15
+ span.set_tag(Ext::TAG_JOB_ID, job.jid)
16
+ span.set_tag(Ext::TAG_JOB_QUEUE, job.queue_name)
17
+
18
+ tag_job_tags = datadog_configuration[:tag_job_tags]
19
+ span.set_tag(Ext::TAG_JOB_TAGS, job.tags) if tag_job_tags
20
+
21
+ tag_job_data = datadog_configuration[:tag_job_data]
22
+ if tag_job_data && !job.data.empty?
23
+ job_data = job.data.with_indifferent_access
24
+ formatted_data = job_data.except(:tags).map do |key, value|
25
+ "#{key}:#{value}".underscore
26
+ end
27
+
28
+ span.set_tag(Ext::TAG_JOB_DATA, formatted_data)
29
+ end
30
+
31
+ # Set analytics sample rate
32
+ if Contrib::Analytics.enabled?(datadog_configuration[:analytics_enabled])
33
+ Contrib::Analytics.set_sample_rate(span, datadog_configuration[:analytics_sample_rate])
34
+ end
35
+
36
+ # Measure service stats
37
+ Contrib::Analytics.set_measured(span)
38
+
39
+ super
40
+ end
41
+ end
42
+
43
+ def after_fork
44
+ configuration = Datadog.configuration[:qless]
45
+ return if configuration.nil?
46
+
47
+ # Add a pin, marking the job as forked.
48
+ # Used to trigger shutdown in forks for performance reasons.
49
+ # Cleanup happens in the TracerCleaner class
50
+ Datadog::Pin.new(
51
+ configuration[:service_name],
52
+ config: { forked: true }
53
+ ).onto(::Qless)
54
+ end
55
+
56
+ private
57
+
58
+ def span_options
59
+ { service: datadog_configuration[:service_name] }
60
+ end
61
+
62
+ def tracer
63
+ datadog_configuration.tracer
64
+ end
65
+
66
+ def datadog_configuration
67
+ Datadog.configuration[:qless]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,32 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Qless
4
+ # Shutdown Tracer in forks for performance reasons
5
+ module TracerCleaner
6
+ def around_perform(job)
7
+ return super unless datadog_configuration && tracer
8
+
9
+ super.tap do
10
+ tracer.shutdown! if forked?
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def forked?
17
+ pin = Datadog::Pin.get_from(::Qless)
18
+ return false unless pin
19
+ pin.config[:forked] == true
20
+ end
21
+
22
+ def tracer
23
+ datadog_configuration.tracer
24
+ end
25
+
26
+ def datadog_configuration
27
+ Datadog.configuration[:qless]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -4,6 +4,8 @@ module Datadog
4
4
  module Contrib
5
5
  module Redis
6
6
  module Configuration
7
+ UNIX_SCHEME = 'unix'.freeze
8
+
7
9
  # Converts Symbols, Strings, and Hashes to a normalized connection settings Hash.
8
10
  class Resolver < Contrib::Configuration::Resolver
9
11
  def resolve(key_or_hash)
@@ -13,7 +15,7 @@ module Datadog
13
15
  end
14
16
 
15
17
  def normalize(hash)
16
- return { url: hash[:url] } if hash[:scheme] == 'unix'
18
+ return { url: hash[:url] } if hash[:scheme] == UNIX_SCHEME
17
19
 
18
20
  # Connexion strings are always converted to host, port, db and scheme
19
21
  # but the host, port, db and scheme will generate the :url only after
@@ -22,6 +22,11 @@ module Datadog
22
22
  o.lazy
23
23
  end
24
24
 
25
+ option :command_args do |o|
26
+ o.default { env_to_bool(Ext::ENV_COMMAND_ARGS, true) }
27
+ o.lazy
28
+ end
29
+
25
30
  option :service_name, default: Ext::SERVICE_NAME
26
31
  end
27
32
  end