ddtrace 0.44.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +48 -1
  3. data/Appraisals +16 -7
  4. data/CHANGELOG.md +43 -1
  5. data/README.md +1 -0
  6. data/Rakefile +51 -2
  7. data/ddtrace.gemspec +1 -0
  8. data/docs/GettingStarted.md +65 -6
  9. data/lib/ddtrace.rb +7 -0
  10. data/lib/ddtrace/auto_instrument.rb +3 -0
  11. data/lib/ddtrace/auto_instrument_base.rb +6 -0
  12. data/lib/ddtrace/contrib/action_cable/integration.rb +7 -0
  13. data/lib/ddtrace/contrib/action_pack/integration.rb +7 -0
  14. data/lib/ddtrace/contrib/action_view/integration.rb +7 -0
  15. data/lib/ddtrace/contrib/active_record/integration.rb +7 -0
  16. data/lib/ddtrace/contrib/active_record/utils.rb +56 -20
  17. data/lib/ddtrace/contrib/active_support/integration.rb +7 -1
  18. data/lib/ddtrace/contrib/auto_instrument.rb +48 -0
  19. data/lib/ddtrace/contrib/cucumber/integration.rb +5 -0
  20. data/lib/ddtrace/contrib/ethon/easy_patch.rb +5 -4
  21. data/lib/ddtrace/contrib/extensions.rb +27 -1
  22. data/lib/ddtrace/contrib/httpclient/configuration/settings.rb +32 -0
  23. data/lib/ddtrace/contrib/httpclient/ext.rb +17 -0
  24. data/lib/ddtrace/contrib/httpclient/instrumentation.rb +152 -0
  25. data/lib/ddtrace/contrib/httpclient/integration.rb +43 -0
  26. data/lib/ddtrace/contrib/httpclient/patcher.rb +35 -0
  27. data/lib/ddtrace/contrib/patchable.rb +18 -7
  28. data/lib/ddtrace/contrib/rack/integration.rb +7 -0
  29. data/lib/ddtrace/contrib/rack/middlewares.rb +1 -1
  30. data/lib/ddtrace/contrib/rack/request_queue.rb +6 -1
  31. data/lib/ddtrace/contrib/rails/auto_instrument_railtie.rb +10 -0
  32. data/lib/ddtrace/contrib/rails/utils.rb +4 -0
  33. data/lib/ddtrace/contrib/rake/integration.rb +1 -1
  34. data/lib/ddtrace/contrib/rspec/integration.rb +5 -0
  35. data/lib/ddtrace/ext/ci.rb +42 -9
  36. data/lib/ddtrace/ext/git.rb +0 -1
  37. data/lib/ddtrace/propagation/http_propagator.rb +17 -2
  38. data/lib/ddtrace/version.rb +1 -1
  39. metadata +29 -6
@@ -109,11 +109,12 @@ module Datadog
109
109
  # Set analytics sample rate
110
110
  Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?
111
111
 
112
- return unless uri
113
- span.set_tag(Datadog::Ext::HTTP::URL, uri.path)
112
+ this_uri = uri
113
+ return unless this_uri
114
+ span.set_tag(Datadog::Ext::HTTP::URL, this_uri.path)
114
115
  span.set_tag(Datadog::Ext::HTTP::METHOD, method)
115
- span.set_tag(Datadog::Ext::NET::TARGET_HOST, uri.host)
116
- span.set_tag(Datadog::Ext::NET::TARGET_PORT, uri.port)
116
+ span.set_tag(Datadog::Ext::NET::TARGET_HOST, this_uri.host)
117
+ span.set_tag(Datadog::Ext::NET::TARGET_PORT, this_uri.port)
117
118
  end
118
119
 
119
120
  def set_span_error_message(message)
@@ -27,8 +27,26 @@ module Datadog
27
27
 
28
28
  # Activate integrations
29
29
  if target.respond_to?(:integrations_pending_activation)
30
+ reduce_verbosity = target.respond_to?(:reduce_verbosity?) ? target.reduce_verbosity? : false
30
31
  target.integrations_pending_activation.each do |integration|
31
- integration.patch if integration.respond_to?(:patch)
32
+ next unless integration.respond_to?(:patch)
33
+ # integration.patch returns either true or a hash of details on why patching failed
34
+ patch_results = integration.patch
35
+
36
+ next if patch_results == true
37
+
38
+ # if patching failed, only log output if verbosity is unset
39
+ # or if patching failure is due to compatibility or integration specific reasons
40
+ next unless !reduce_verbosity ||
41
+ ((patch_results[:available] && patch_results[:loaded]) &&
42
+ (!patch_results[:compatible] || !patch_results[:patchable]))
43
+
44
+ desc = "Available?: #{patch_results[:available]}"
45
+ desc += ", Loaded? #{patch_results[:loaded]}"
46
+ desc += ", Compatible? #{patch_results[:compatible]}"
47
+ desc += ", Patchable? #{patch_results[:patchable]}"
48
+
49
+ Datadog.logger.warn("Unable to patch #{patch_results['name']} (#{desc})")
32
50
  end
33
51
 
34
52
  target.integrations_pending_activation.clear
@@ -86,6 +104,14 @@ module Datadog
86
104
  registry[name] ||
87
105
  raise(InvalidIntegrationError, "'#{name}' is not a valid integration.")
88
106
  end
107
+
108
+ def reduce_verbosity?
109
+ defined?(@reduce_verbosity) ? @reduce_verbosity : false
110
+ end
111
+
112
+ def reduce_log_verbosity
113
+ @reduce_verbosity ||= true
114
+ end
89
115
  end
90
116
  end
91
117
  end
@@ -0,0 +1,32 @@
1
+ require 'ddtrace/contrib/configuration/settings'
2
+ require 'ddtrace/contrib/httpclient/ext'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Httpclient
7
+ module Configuration
8
+ # Custom settings for the Httpclient integration
9
+ class Settings < Contrib::Configuration::Settings
10
+ option :enabled do |o|
11
+ o.default { env_to_bool(Ext::ENV_ENABLED, true) }
12
+ o.lazy
13
+ end
14
+
15
+ option :analytics_enabled do |o|
16
+ o.default { env_to_bool([Ext::ENV_ANALYTICS_ENABLED, Ext::ENV_ANALYTICS_ENABLED_OLD], false) }
17
+ o.lazy
18
+ end
19
+
20
+ option :analytics_sample_rate do |o|
21
+ o.default { env_to_float([Ext::ENV_ANALYTICS_SAMPLE_RATE, Ext::ENV_ANALYTICS_SAMPLE_RATE_OLD], 1.0) }
22
+ o.lazy
23
+ end
24
+
25
+ option :distributed_tracing, default: true
26
+ option :service_name, default: Ext::SERVICE_NAME
27
+ option :split_by_domain, default: false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Httpclient
4
+ # Httpclient integration constants
5
+ module Ext
6
+ APP = 'httpclient'.freeze
7
+ ENV_ENABLED = 'DD_TRACE_HTTPCLIENT_ENABLED'.freeze
8
+ ENV_ANALYTICS_ENABLED = 'DD_TRACE_HTTPCLIENT_ANALYTICS_ENABLED'.freeze
9
+ ENV_ANALYTICS_ENABLED_OLD = 'DD_HTTPCLIENT_ANALYTICS_ENABLED'.freeze
10
+ ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_HTTPCLIENT_ANALYTICS_SAMPLE_RATE'.freeze
11
+ ENV_ANALYTICS_SAMPLE_RATE_OLD = 'DD_HTTPCLIENT_ANALYTICS_SAMPLE_RATE'.freeze
12
+ SERVICE_NAME = 'httpclient'.freeze
13
+ SPAN_REQUEST = 'httpclient.request'.freeze
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,152 @@
1
+ require 'ddtrace/ext/app_types'
2
+ require 'ddtrace/ext/http'
3
+ require 'ddtrace/ext/net'
4
+ require 'ddtrace/ext/distributed'
5
+ require 'ddtrace/contrib/analytics'
6
+ require 'ddtrace/propagation/http_propagator'
7
+ require 'ddtrace/contrib/http_annotation_helper'
8
+
9
+ module Datadog
10
+ module Contrib
11
+ module Httpclient
12
+ # Instrumentation for Httpclient
13
+ module Instrumentation
14
+ def self.included(base)
15
+ base.send(:prepend, InstanceMethods)
16
+ end
17
+
18
+ # Instance methods for configuration
19
+ module InstanceMethods
20
+ include Datadog::Contrib::HttpAnnotationHelper
21
+
22
+ def do_get_block(req, proxy, conn, &block)
23
+ host = req.header.request_uri.host
24
+ request_options = datadog_configuration(host)
25
+ pin = datadog_pin(request_options)
26
+
27
+ return super unless pin && pin.tracer
28
+
29
+ pin.tracer.trace(Ext::SPAN_REQUEST, on_error: method(:annotate_span_with_error!)) do |span|
30
+ begin
31
+ request_options[:service_name] = pin.service_name
32
+ span.service = service_name(host, request_options)
33
+ span.span_type = Datadog::Ext::HTTP::TYPE_OUTBOUND
34
+
35
+ if pin.tracer.enabled && !should_skip_distributed_tracing?(pin)
36
+ Datadog::HTTPPropagator.inject!(span.context, req.header)
37
+ end
38
+
39
+ # Add additional request specific tags to the span.
40
+ annotate_span_with_request!(span, req, request_options)
41
+ rescue StandardError => e
42
+ logger.error("error preparing span for httpclient request: #{e}, Source: #{e.backtrace}")
43
+ ensure
44
+ res = super
45
+ end
46
+
47
+ # Add additional response specific tags to the span.
48
+ annotate_span_with_response!(span, res)
49
+
50
+ res
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def annotate_span_with_request!(span, req, req_options)
57
+ http_method = req.header.request_method.upcase
58
+ uri = req.header.request_uri
59
+
60
+ span.resource = http_method
61
+ span.set_tag(Datadog::Ext::HTTP::METHOD, http_method)
62
+ span.set_tag(Datadog::Ext::HTTP::URL, uri.path)
63
+ span.set_tag(Datadog::Ext::NET::TARGET_HOST, uri.host)
64
+ span.set_tag(Datadog::Ext::NET::TARGET_PORT, uri.port)
65
+
66
+ # Tag as an external peer service
67
+ span.set_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE, span.service)
68
+
69
+ set_analytics_sample_rate(span, req_options)
70
+ end
71
+
72
+ def annotate_span_with_response!(span, response)
73
+ return unless response && response.status
74
+
75
+ span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
76
+
77
+ case response.status.to_i
78
+ when 400...599
79
+ span.set_error(["Error #{response.status}", response.body])
80
+ end
81
+ end
82
+
83
+ def annotate_span_with_error!(span, error)
84
+ span.set_error(error)
85
+ end
86
+
87
+ def datadog_pin(config = Datadog.configuration[:httprb])
88
+ service = config[:service_name]
89
+ tracer = config[:tracer]
90
+
91
+ @datadog_pin ||= begin
92
+ Datadog::Pin.new(
93
+ service,
94
+ app: Ext::APP,
95
+ app_type: Datadog::Ext::HTTP::TYPE_OUTBOUND,
96
+ tracer: -> { config[:tracer] }
97
+ )
98
+ end
99
+
100
+ if @datadog_pin.service_name == default_datadog_pin.service_name && @datadog_pin.service_name != service
101
+ @datadog_pin.service = service
102
+ end
103
+ if @datadog_pin.tracer == default_datadog_pin.tracer && @datadog_pin.tracer != tracer
104
+ @datadog_pin.tracer = tracer
105
+ end
106
+
107
+ @datadog_pin
108
+ end
109
+
110
+ def default_datadog_pin
111
+ config = Datadog.configuration[:httpclient]
112
+ service = config[:service_name]
113
+
114
+ @default_datadog_pin ||= begin
115
+ Datadog::Pin.new(
116
+ service,
117
+ app: Ext::APP,
118
+ app_type: Datadog::Ext::HTTP::TYPE_OUTBOUND,
119
+ tracer: -> { config[:tracer] }
120
+ )
121
+ end
122
+ end
123
+
124
+ def datadog_configuration(host = :default)
125
+ Datadog.configuration[:httpclient, host]
126
+ end
127
+
128
+ def analytics_enabled?(request_options)
129
+ Contrib::Analytics.enabled?(request_options[:analytics_enabled])
130
+ end
131
+
132
+ def logger
133
+ Datadog.logger
134
+ end
135
+
136
+ def should_skip_distributed_tracing?(pin)
137
+ if pin.config && pin.config.key?(:distributed_tracing)
138
+ return !pin.config[:distributed_tracing]
139
+ end
140
+
141
+ !Datadog.configuration[:httpclient][:distributed_tracing]
142
+ end
143
+
144
+ def set_analytics_sample_rate(span, request_options)
145
+ return unless analytics_enabled?(request_options)
146
+ Contrib::Analytics.set_sample_rate(span, request_options[:analytics_sample_rate])
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,43 @@
1
+ require 'ddtrace/contrib/integration'
2
+ require 'ddtrace/contrib/httpclient/configuration/settings'
3
+ require 'ddtrace/contrib/configuration/resolvers/pattern_resolver'
4
+ require 'ddtrace/contrib/httpclient/patcher'
5
+
6
+ module Datadog
7
+ module Contrib
8
+ module Httpclient
9
+ # Description of Httpclient integration
10
+ class Integration
11
+ include Contrib::Integration
12
+
13
+ MINIMUM_VERSION = Gem::Version.new('2.2.0')
14
+
15
+ register_as :httpclient
16
+
17
+ def self.version
18
+ Gem.loaded_specs['httpclient'] && Gem.loaded_specs['httpclient'].version
19
+ end
20
+
21
+ def self.loaded?
22
+ !defined?(::HTTPClient).nil?
23
+ end
24
+
25
+ def self.compatible?
26
+ super && version >= MINIMUM_VERSION
27
+ end
28
+
29
+ def default_configuration
30
+ Configuration::Settings.new
31
+ end
32
+
33
+ def patcher
34
+ Patcher
35
+ end
36
+
37
+ def resolver
38
+ @resolver ||= Contrib::Configuration::Resolvers::PatternResolver.new
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ require 'ddtrace/contrib/patcher'
2
+ require 'ddtrace/contrib/httpclient/instrumentation'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ # Datadog Httpclient integration.
7
+ module Httpclient
8
+ # Patcher enables patching of 'httpclient' module.
9
+ module Patcher
10
+ include Contrib::Patcher
11
+
12
+ module_function
13
+
14
+ def patched?
15
+ done?(:httpclient)
16
+ end
17
+
18
+ def target_version
19
+ Integration.version
20
+ end
21
+
22
+ # patch applies our patch
23
+ def patch
24
+ do_once(:httpclient) do
25
+ begin
26
+ ::HTTPClient.send(:include, Instrumentation)
27
+ rescue StandardError => e
28
+ Datadog::Logger.error("Unable to apply httpclient integration: #{e}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -42,16 +42,27 @@ module Datadog
42
42
 
43
43
  def patch
44
44
  if !self.class.patchable? || patcher.nil?
45
- desc = "Available?: #{self.class.available?}"
46
- desc += ", Loaded? #{self.class.loaded?}"
47
- desc += ", Compatible? #{self.class.compatible?}"
48
- desc += ", Patchable? #{self.class.patchable?}"
49
-
50
- Datadog.logger.warn("Unable to patch #{self.class.name} (#{desc})")
51
- return
45
+ return {
46
+ name: self.class.name,
47
+ available: self.class.available?,
48
+ loaded: self.class.loaded?,
49
+ compatible: self.class.compatible?,
50
+ patchable: self.class.patchable?
51
+ }
52
52
  end
53
53
 
54
54
  patcher.patch
55
+ true
56
+ end
57
+
58
+ # Can the patch for this integration be applied automatically?
59
+ # For example: test integrations should only be applied
60
+ # by the user explicitly setting `c.use :rspec`
61
+ # and rails sub-modules are auto-instrumented by enabling rails
62
+ # so auto-instrumenting them on their own will cause changes in
63
+ # service naming behavior
64
+ def auto_instrument?
65
+ true
55
66
  end
56
67
  end
57
68
  end
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/contrib/integration'
2
2
  require 'ddtrace/contrib/rack/configuration/settings'
3
3
  require 'ddtrace/contrib/rack/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
@@ -53,7 +53,7 @@ module Datadog
53
53
  tracer.provider.context = context if context.trace_id
54
54
  end
55
55
 
56
- # [experimental] create a root Span to keep track of frontend web servers
56
+ # Create a root Span to keep track of frontend web servers
57
57
  # (i.e. Apache, nginx) if the header is properly set
58
58
  frontend_span = compute_queue_time(env, tracer)
59
59
 
@@ -1,7 +1,12 @@
1
1
  module Datadog
2
2
  module Contrib
3
3
  module Rack
4
- # QueueTime simply...
4
+ # Retrieves the time spent in an upstream proxy
5
+ # for the current Rack request.
6
+ #
7
+ # This time captures the request delay introduced but
8
+ # such proxy before the request made it to the Ruby
9
+ # process.
5
10
  module QueueTime
6
11
  REQUEST_START = 'HTTP_X_REQUEST_START'.freeze
7
12
  QUEUE_START = 'HTTP_X_QUEUE_START'.freeze
@@ -0,0 +1,10 @@
1
+ require 'ddtrace'
2
+
3
+ # Railtie to include AutoInstrumentation in rails loading
4
+ class DatadogAutoInstrumentRailtie < Rails::Railtie
5
+ # we want to load before config initializers so that any user supplied config
6
+ # in config/initializers/datadog.rb will take precedence
7
+ initializer 'datadog.start_tracer', before: :load_config_initializers do
8
+ Datadog::Contrib::AutoInstrument.patch_all
9
+ end
10
+ end
@@ -14,6 +14,10 @@ module Datadog
14
14
  ::Rails.application.class.to_s.underscore
15
15
  end
16
16
  end
17
+
18
+ def self.railtie_supported?
19
+ !(defined?(::Rails::VERSION::MAJOR) && ::Rails::VERSION::MAJOR >= 3 && defined?(::Rails::Railtie)).nil?
20
+ end
17
21
  end
18
22
  end
19
23
  end