ddtrace 0.43.0 → 0.44.0

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