ddtrace 0.11.0 → 0.11.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b3bb99f4104ccd84acde3279b412e90d531ecfea
4
- data.tar.gz: 63d063737c4e830b84d24471fc1ff356ca703af6
3
+ metadata.gz: edcd0745f2ad784016b18e4b7e4694e4af12223a
4
+ data.tar.gz: '0952683e8798ee961851a881d0ea4343a121572a'
5
5
  SHA512:
6
- metadata.gz: b271d08b6eae8c40116c68b21ef46c4fb110fba35b331913b902e9fc0b7ce5fa4868e997c7984eff71d8f325f98f3276c6bc11b6da69bdf877903338fb599948
7
- data.tar.gz: d86ca3ce85531ea82e437f2f9495deed831ab10a2e15d35a78a1095607f99c9ef901cb6cf800f084a4f4142f200f339997b60e30fa87b80d66de3c9706f23346
6
+ metadata.gz: 5cae4b99e2e0170f5a7da8a320ae7235ec7cc3431c8dee355a84e921b2f7933bbc2e32058547a8bea6dcbabfe94a4b1aff3bb293d7d059f480fca84f8ca5eb62
7
+ data.tar.gz: b74edf57aeee710af1024f68eee73e804f3bff2cb0ee4426c384b6fc8626f36db85ea7fa95e698d4b3cc32f33ffc78d2e6fdc49368a11a4122cf2a847ebaf9f8
data/Appraisals CHANGED
@@ -114,7 +114,7 @@ end
114
114
  if RUBY_VERSION >= '2.2.2' && RUBY_PLATFORM != 'java'
115
115
  appraise 'contrib' do
116
116
  gem 'elasticsearch-transport'
117
- gem 'mongo'
117
+ gem 'mongo', '< 2.5'
118
118
  gem 'grape'
119
119
  gem 'rack'
120
120
  gem 'rack-test'
@@ -129,11 +129,12 @@ if RUBY_VERSION >= '2.2.2' && RUBY_PLATFORM != 'java'
129
129
  gem 'dalli'
130
130
  gem 'resque', '< 2.0'
131
131
  gem 'racecar', '>= 0.3.5'
132
+ gem 'mysql2', platform: :ruby
132
133
  end
133
134
  else
134
135
  appraise 'contrib-old' do
135
136
  gem 'elasticsearch-transport'
136
- gem 'mongo'
137
+ gem 'mongo', '< 2.5'
137
138
  gem 'redis', '< 4.0'
138
139
  gem 'hiredis'
139
140
  gem 'rack', '1.4.7'
@@ -146,5 +147,7 @@ else
146
147
  gem 'sucker_punch'
147
148
  gem 'dalli'
148
149
  gem 'resque', '< 2.0'
150
+ gem 'mysql2', '0.3.21', platform: :ruby
151
+ gem 'activerecord-mysql-adapter', platform: :ruby
149
152
  end
150
153
  end
data/README.md CHANGED
@@ -75,7 +75,10 @@ you can activate it. The example above would become:
75
75
  require 'sinatra'
76
76
  require 'active_record'
77
77
 
78
- Datadog::Monkey.patch_all # monkey patch all available integrations
78
+ Datadog.configure do |c|
79
+ c.use :sinatra
80
+ c.use :active_record
81
+ end
79
82
 
80
83
  # now write your code naturally, it's traced automatically
81
84
  get '/' do
data/Rakefile CHANGED
@@ -11,11 +11,11 @@ desc 'Run RSpec'
11
11
  namespace :spec do
12
12
  task all: [:main,
13
13
  :rails, :railsredis, :railssidekiq, :railsactivejob,
14
- :elasticsearch, :http, :redis, :sidekiq, :sinatra, :monkey]
14
+ :elasticsearch, :http, :redis, :sidekiq, :sinatra]
15
15
 
16
16
  RSpec::Core::RakeTask.new(:main) do |t|
17
17
  t.pattern = 'spec/**/*_spec.rb'
18
- t.exclude_pattern = 'spec/**/{contrib,benchmark,redis}/**/*_spec.rb,spec/monkey_spec.rb'
18
+ t.exclude_pattern = 'spec/**/{contrib,benchmark,redis}/**/*_spec.rb'
19
19
  end
20
20
 
21
21
  RSpec::Core::RakeTask.new(:rails) do |t|
@@ -53,30 +53,26 @@ namespace :spec do
53
53
  :mongodb,
54
54
  :racecar,
55
55
  :resque,
56
+ :active_record,
56
57
  :dalli
57
58
  ].each do |contrib|
58
59
  RSpec::Core::RakeTask.new(contrib) do |t|
59
60
  t.pattern = "spec/ddtrace/contrib/#{contrib}/*_spec.rb"
60
61
  end
61
62
  end
62
-
63
- RSpec::Core::RakeTask.new(:monkey) do |t|
64
- t.pattern = 'spec/ddtrace/monkey_spec.rb'
65
- end
66
63
  end
67
64
 
68
65
  namespace :test do
69
66
  task all: [:main,
70
67
  :rails, :railsredis, :railssidekiq, :railsactivejob,
71
- :elasticsearch, :http, :redis, :sidekiq, :sinatra, :monkey]
68
+ :elasticsearch, :http, :redis, :sidekiq, :sinatra]
72
69
 
73
70
  Rake::TestTask.new(:main) do |t|
74
71
  t.libs << %w[test lib]
75
72
  t.test_files = FileList['test/**/*_test.rb'].reject do |path|
76
73
  path.include?('contrib') ||
77
74
  path.include?('benchmark') ||
78
- path.include?('redis') ||
79
- path.include?('monkey_test.rb')
75
+ path.include?('redis')
80
76
  end
81
77
  end
82
78
 
@@ -129,11 +125,6 @@ namespace :test do
129
125
  t.test_files = FileList["test/contrib/#{contrib}/*_test.rb"]
130
126
  end
131
127
  end
132
-
133
- Rake::TestTask.new(:monkey) do |t|
134
- t.libs << %w[test lib]
135
- t.test_files = FileList['test/monkey_test.rb']
136
- end
137
128
  end
138
129
 
139
130
  Rake::TestTask.new(:benchmark) do |t|
@@ -219,7 +210,6 @@ task :ci do
219
210
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:mongodb'
220
211
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:sucker_punch'
221
212
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:resque'
222
- sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:monkey'
223
213
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:elasticsearch'
224
214
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:http'
225
215
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:redis'
@@ -231,9 +221,11 @@ task :ci do
231
221
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:sucker_punch'
232
222
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:resque'
233
223
  # RSpec
224
+ sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:active_record'
234
225
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:dalli'
235
226
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake spec:racecar'
236
227
  sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake spec:dalli'
228
+ sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake spec:active_record'
237
229
  when 2
238
230
  sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:sidekiq'
239
231
  sh 'rvm $SIDEKIQ_OLD_VERSIONS --verbose do appraisal contrib-old rake test:sidekiq'
@@ -77,7 +77,8 @@ Where `options` is an optional `Hash` that accepts the following parameters:
77
77
  | ``service_name`` | Service name used when tracing application requests (on the `rack` level) | ``<app_name>`` (inferred from your Rails application namespace) |
78
78
  | ``controller_service`` | Service name used when tracing a Rails action controller | ``<app_name>-controller`` |
79
79
  | ``cache_service`` | Cache service name used when tracing cache activity | ``<app_name>-cache`` |
80
- | ``database_service`` | Database service name used when tracing database activity | ``<app_name>-<adapter_name>``. |
80
+ | ``database_service`` | Database service name used when tracing database activity | ``<app_name>-<adapter_name>`` |
81
+ | ``exception_controller`` | Class or Module which identifies a custom exception controller class. Tracer provides improved error behavior when it can identify custom exception controllers. By default, without this option, it 'guesses' what a custom exception controller looks like. Providing this option aids this identification. | ``nil`` |
81
82
  | ``distributed_tracing`` | Enables [distributed tracing](#Distributed_Tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `false` |
82
83
  | ``template_base_path`` | Used when the template name is parsed. If you don't store your templates in the ``views/`` folder, you may need to change this value | ``views/`` |
83
84
  | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
@@ -107,6 +108,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
107
108
  | --- | --- | --- |
108
109
  | ``service_name`` | Service name used for `sinatra` instrumentation | sinatra |
109
110
  | ``resource_script_names`` | Prepend resource names with script name | ``false`` |
111
+ | ``distributed_tracing`` | Enables [distributed tracing](#Distributed_Tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `false` |
110
112
  | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
111
113
 
112
114
  ### Rack
@@ -4,7 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "pry-nav", git: "https://github.com/nixme/pry-nav.git", branch: "master"
6
6
  gem "elasticsearch-transport"
7
- gem "mongo"
7
+ gem "mongo", "< 2.5"
8
8
  gem "grape"
9
9
  gem "rack"
10
10
  gem "rack-test"
@@ -19,5 +19,6 @@ gem "sucker_punch"
19
19
  gem "dalli"
20
20
  gem "resque", "< 2.0"
21
21
  gem "racecar", ">= 0.3.5"
22
+ gem "mysql2", platform: :ruby
22
23
 
23
24
  gemspec path: "../"
@@ -4,7 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "pry-nav", git: "https://github.com/nixme/pry-nav.git", branch: "master"
6
6
  gem "elasticsearch-transport"
7
- gem "mongo"
7
+ gem "mongo", "< 2.5"
8
8
  gem "redis", "< 4.0"
9
9
  gem "hiredis"
10
10
  gem "rack", "1.4.7"
@@ -17,5 +17,7 @@ gem "aws-sdk", "~> 2.0"
17
17
  gem "sucker_punch"
18
18
  gem "dalli"
19
19
  gem "resque", "< 2.0"
20
+ gem "mysql2", "0.3.21", platform: :ruby
21
+ gem "activerecord-mysql-adapter", platform: :ruby
20
22
 
21
23
  gemspec path: "../"
data/lib/ddtrace.rb CHANGED
@@ -1,9 +1,12 @@
1
+ require 'thread'
2
+
1
3
  require 'ddtrace/registry'
2
4
  require 'ddtrace/pin'
3
5
  require 'ddtrace/tracer'
4
6
  require 'ddtrace/error'
5
7
  require 'ddtrace/pipeline'
6
8
  require 'ddtrace/configuration'
9
+ require 'ddtrace/patcher'
7
10
 
8
11
  # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
9
12
  module Datadog
@@ -49,8 +52,19 @@ module Datadog
49
52
  end
50
53
  end
51
54
 
52
- # Monkey currently is responsible for loading all contributions, which in turn
53
- # rely on the registry defined above. We should make our code less dependent on
54
- # the load order, by letting things be lazily loaded while keeping
55
- # thread-safety.
56
- require 'ddtrace/monkey'
55
+ require 'ddtrace/contrib/base'
56
+ require 'ddtrace/contrib/rack/patcher'
57
+ require 'ddtrace/contrib/rails/patcher'
58
+ require 'ddtrace/contrib/active_record/patcher'
59
+ require 'ddtrace/contrib/elasticsearch/patcher'
60
+ require 'ddtrace/contrib/faraday/patcher'
61
+ require 'ddtrace/contrib/grape/patcher'
62
+ require 'ddtrace/contrib/redis/patcher'
63
+ require 'ddtrace/contrib/http/patcher'
64
+ require 'ddtrace/contrib/aws/patcher'
65
+ require 'ddtrace/contrib/sucker_punch/patcher'
66
+ require 'ddtrace/contrib/mongodb/patcher'
67
+ require 'ddtrace/contrib/dalli/patcher'
68
+ require 'ddtrace/contrib/resque/patcher'
69
+ require 'ddtrace/contrib/racecar/patcher'
70
+ require 'ddtrace/contrib/sidekiq/patcher'
@@ -2,11 +2,11 @@ module Datadog
2
2
  module Contrib
3
3
  module ActiveRecord
4
4
  # Patcher enables patching of 'active_record' module.
5
- # This is used in monkey.rb to manually apply patches
6
5
  module Patcher
7
6
  include Base
8
7
  register_as :active_record, auto_patch: false
9
8
  option :service_name
9
+ option :tracer, default: Datadog.tracer
10
10
 
11
11
  @patched = false
12
12
 
@@ -58,22 +58,18 @@ module Datadog
58
58
  @adapter_port ||= Datadog::Contrib::Rails::Utils.adapter_port
59
59
  end
60
60
 
61
- def self.tracer
62
- @tracer ||= Datadog.configuration[:sinatra][:tracer]
63
- end
64
-
65
61
  def self.database_service
66
62
  return @database_service if defined?(@database_service)
67
63
 
68
64
  @database_service = get_option(:service_name) || adapter_name
69
- tracer.set_service_info(@database_service, 'sinatra', Ext::AppTypes::DB)
65
+ get_option(:tracer).set_service_info(@database_service, 'active_record', Ext::AppTypes::DB)
70
66
  @database_service
71
67
  end
72
68
 
73
69
  def self.sql(_name, start, finish, _id, payload)
74
70
  span_type = Datadog::Ext::SQL::TYPE
75
71
 
76
- span = tracer.trace(
72
+ span = get_option(:tracer).trace(
77
73
  "#{adapter_name}.query",
78
74
  resource: payload.fetch(:sql),
79
75
  service: database_service,
@@ -8,6 +8,7 @@ module Datadog
8
8
  def self.included(base)
9
9
  base.send(:include, Registry::Registerable)
10
10
  base.send(:include, Configurable)
11
+ base.send(:include, Patcher)
11
12
  end
12
13
  end
13
14
  end
@@ -11,7 +11,6 @@ module Datadog
11
11
  SERVICE = 'elasticsearch'.freeze
12
12
 
13
13
  # Patcher enables patching of 'elasticsearch/transport' module.
14
- # This is used in monkey.rb to automatically apply patches
15
14
  module Patcher
16
15
  include Base
17
16
  register_as :elasticsearch, auto_patch: true
@@ -28,7 +27,6 @@ module Datadog
28
27
  begin
29
28
  require 'uri'
30
29
  require 'json'
31
- require 'ddtrace/monkey'
32
30
  require 'ddtrace/pin'
33
31
  require 'ddtrace/ext/app_types'
34
32
  require 'ddtrace/contrib/elasticsearch/quantize'
@@ -49,7 +47,7 @@ module Datadog
49
47
  # rubocop:disable Metrics/BlockLength
50
48
  ::Elasticsearch::Transport::Client.class_eval do
51
49
  alias_method :initialize_without_datadog, :initialize
52
- Datadog::Monkey.without_warnings do
50
+ Datadog::Patcher.without_warnings do
53
51
  remove_method :initialize
54
52
  end
55
53
 
@@ -44,7 +44,6 @@ module Datadog
44
44
  end
45
45
 
46
46
  # Patcher enables patching of 'net/http' module.
47
- # This is used in monkey.rb to automatically apply patches
48
47
  module Patcher
49
48
  include Base
50
49
  register_as :http, auto_patch: true
@@ -60,7 +59,6 @@ module Datadog
60
59
  begin
61
60
  require 'uri'
62
61
  require 'ddtrace/pin'
63
- require 'ddtrace/monkey'
64
62
  require 'ddtrace/ext/app_types'
65
63
  require 'ddtrace/ext/http'
66
64
  require 'ddtrace/ext/net'
@@ -87,7 +85,7 @@ module Datadog
87
85
  def patch_http
88
86
  ::Net::HTTP.class_eval do
89
87
  alias_method :initialize_without_datadog, :initialize
90
- Datadog::Monkey.without_warnings do
88
+ Datadog::Patcher.without_warnings do
91
89
  remove_method :initialize
92
90
  end
93
91
 
@@ -8,8 +8,6 @@ module Datadog
8
8
  SERVICE = 'mongodb'.freeze
9
9
 
10
10
  # Patcher adds subscribers to the MongoDB driver so that each command is traced.
11
- # Use the `Datadog::Monkey.patch_module(:mongodb)` to activate tracing for
12
- # this module.
13
11
  module Patcher
14
12
  include Base
15
13
  register_as :mongo, auto_patch: true
@@ -57,7 +55,7 @@ module Datadog
57
55
  def patch_mongo_client
58
56
  ::Mongo::Client.class_eval do
59
57
  alias_method :initialize_without_datadog, :initialize
60
- Datadog::Monkey.without_warnings do
58
+ Datadog::Patcher.without_warnings do
61
59
  remove_method :initialize
62
60
  end
63
61
 
@@ -16,10 +16,8 @@ module Datadog
16
16
  def patch
17
17
  return patched? if patched? || !compatible?
18
18
 
19
- ::ActiveSupport::Notifications.subscribe('start_process_batch.racecar', &method(:start))
20
- ::ActiveSupport::Notifications.subscribe('start_process_message.racecar', &method(:start))
21
- ::ActiveSupport::Notifications.subscribe('process_batch.racecar', &method(:finish))
22
- ::ActiveSupport::Notifications.subscribe('process_message.racecar', &method(:finish))
19
+ ::ActiveSupport::Notifications.subscribe('process_batch.racecar', self)
20
+ ::ActiveSupport::Notifications.subscribe('process_message.racecar', self)
23
21
 
24
22
  configuration[:tracer].set_service_info(
25
23
  configuration[:service_name],
@@ -35,17 +33,7 @@ module Datadog
35
33
  @patched = false
36
34
  end
37
35
 
38
- private
39
-
40
- def configuration
41
- Datadog.configuration[:racecar]
42
- end
43
-
44
- def compatible?
45
- defined?(::Racecar) && defined?(::ActiveSupport::Notifications)
46
- end
47
-
48
- def start(event, *_, payload)
36
+ def start(event, _, payload)
49
37
  ensure_clean_context!
50
38
 
51
39
  name = event[/message/] ? NAME_MESSAGE : NAME_BATCH
@@ -60,7 +48,7 @@ module Datadog
60
48
  span.set_tag('kafka.message_count', payload[:message_count]) if payload.key?(:message_count)
61
49
  end
62
50
 
63
- def finish(*_, payload)
51
+ def finish(_, _, payload)
64
52
  current_span = configuration[:tracer].call_context.current_span
65
53
 
66
54
  return unless current_span
@@ -69,6 +57,16 @@ module Datadog
69
57
  current_span.finish
70
58
  end
71
59
 
60
+ private
61
+
62
+ def configuration
63
+ Datadog.configuration[:racecar]
64
+ end
65
+
66
+ def compatible?
67
+ defined?(::Racecar) && defined?(::ActiveSupport::Notifications)
68
+ end
69
+
72
70
  def ensure_clean_context!
73
71
  return unless configuration[:tracer].call_context.current_span
74
72
 
@@ -20,16 +20,14 @@ module Datadog
20
20
  def call(env)
21
21
  # retrieve integration settings
22
22
  tracer = Datadog.configuration[:rack][:tracer]
23
- service = Datadog.configuration[:rack][:service_name]
24
- distributed_tracing = Datadog.configuration[:rack][:distributed_tracing]
25
23
 
26
24
  trace_options = {
27
- service: service,
25
+ service: Datadog.configuration[:rack][:service_name],
28
26
  resource: nil,
29
27
  span_type: Datadog::Ext::HTTP::TYPE
30
28
  }
31
29
 
32
- if distributed_tracing
30
+ if Datadog.configuration[:rack][:distributed_tracing]
33
31
  context = HTTPPropagator.extract(env)
34
32
  tracer.provider.context = context if context.trace_id
35
33
  end
@@ -56,35 +54,13 @@ module Datadog
56
54
  request_span.set_error(e) unless request_span.nil?
57
55
  raise e
58
56
  ensure
59
- # the source of truth in Rack is the PATH_INFO key that holds the
60
- # URL for the current request; some framework may override that
61
- # value, especially during exception handling and because of that
62
- # we prefer using the `REQUEST_URI` if this is available.
63
- # NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
64
- url = env['REQUEST_URI'] || env['PATH_INFO']
65
-
66
57
  # Rack is a really low level interface and it doesn't provide any
67
58
  # advanced functionality like routers. Because of that, we assume that
68
59
  # the underlying framework or application has more knowledge about
69
60
  # the result for this request; `resource` and `tags` are expected to
70
61
  # be set in another level but if they're missing, reasonable defaults
71
62
  # are used.
72
- request_span.resource ||= resource_name_for(env, status)
73
- if request_span.get_tag(Datadog::Ext::HTTP::METHOD).nil?
74
- request_span.set_tag(Datadog::Ext::HTTP::METHOD, env['REQUEST_METHOD'])
75
- end
76
- if request_span.get_tag(Datadog::Ext::HTTP::URL).nil?
77
- request_span.set_tag(Datadog::Ext::HTTP::URL, url)
78
- end
79
- if request_span.get_tag(Datadog::Ext::HTTP::STATUS_CODE).nil? && status
80
- request_span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
81
- end
82
-
83
- # detect if the status code is a 5xx and flag the request span as an error
84
- # unless it has been already set by the underlying framework
85
- if status.to_s.start_with?('5') && request_span.status.zero?
86
- request_span.status = 1
87
- end
63
+ set_request_tags!(request_span, env, status, headers, response)
88
64
 
89
65
  # ensure the request_span is finished and the context reset;
90
66
  # this assumes that the Rack middleware creates a root span
@@ -103,6 +79,44 @@ module Datadog
103
79
  "#{env['REQUEST_METHOD']} #{status}".strip
104
80
  end
105
81
  end
82
+
83
+ def set_request_tags!(request_span, env, status, headers, response)
84
+ # the source of truth in Rack is the PATH_INFO key that holds the
85
+ # URL for the current request; some framework may override that
86
+ # value, especially during exception handling and because of that
87
+ # we prefer using the `REQUEST_URI` if this is available.
88
+ # NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
89
+ url = env['REQUEST_URI'] || env['PATH_INFO']
90
+
91
+ request_span.resource ||= resource_name_for(env, status)
92
+ if request_span.get_tag(Datadog::Ext::HTTP::METHOD).nil?
93
+ request_span.set_tag(Datadog::Ext::HTTP::METHOD, env['REQUEST_METHOD'])
94
+ end
95
+ if request_span.get_tag(Datadog::Ext::HTTP::URL).nil?
96
+ request_span.set_tag(Datadog::Ext::HTTP::URL, url)
97
+ end
98
+ if request_span.get_tag(Datadog::Ext::HTTP::BASE_URL).nil?
99
+ request_obj = ::Rack::Request.new(env)
100
+
101
+ base_url = if request_obj.respond_to?(:base_url)
102
+ request_obj.base_url
103
+ else
104
+ # Compatibility for older Rack versions
105
+ request_obj.url.chomp(request_obj.fullpath)
106
+ end
107
+
108
+ request_span.set_tag(Datadog::Ext::HTTP::BASE_URL, base_url)
109
+ end
110
+ if request_span.get_tag(Datadog::Ext::HTTP::STATUS_CODE).nil? && status
111
+ request_span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
112
+ end
113
+
114
+ # detect if the status code is a 5xx and flag the request span as an error
115
+ # unless it has been already set by the underlying framework
116
+ if status.to_s.start_with?('5') && request_span.status.zero?
117
+ request_span.status = 1
118
+ end
119
+ end
106
120
  end
107
121
  end
108
122
  end
@@ -32,12 +32,15 @@ module Datadog
32
32
  return unless span && !span.finished?
33
33
 
34
34
  begin
35
- resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
36
- span.resource = resource
35
+ # Set the resource name, if it's still the default name
36
+ if span.resource == span.name
37
+ span.resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
38
+ end
37
39
 
38
- # set the parent resource if it's a `rack.request` span
39
- if !span.parent.nil? && span.parent.name == 'rack.request'
40
- span.parent.resource = resource
40
+ # Set the parent resource if it's a `rack.request` span,
41
+ # but not if its an exception contoller.
42
+ if !span.parent.nil? && span.parent.name == 'rack.request' && !exception_controller?(payload)
43
+ span.parent.resource = span.resource
41
44
  end
42
45
 
43
46
  span.set_tag('rails.route.action', payload.fetch(:action))
@@ -64,6 +67,26 @@ module Datadog
64
67
  rescue StandardError => e
65
68
  Datadog::Tracer.log.error(e.message)
66
69
  end
70
+
71
+ def self.exception_controller?(payload)
72
+ exception_controller_class = Datadog.configuration[:rails][:exception_controller]
73
+ controller = payload.fetch(:controller)
74
+ headers = payload.fetch(:headers)
75
+
76
+ # If no exception controller class has been set,
77
+ # guess whether this is an exception controller from the headers.
78
+ if exception_controller_class.nil?
79
+ !headers[:request_exception].nil?
80
+ # If an exception controller class has been specified,
81
+ # check if the controller is a kind of the exception controller class.
82
+ elsif exception_controller_class.is_a?(Class) || exception_controller_class.is_a?(Module)
83
+ controller <= exception_controller_class
84
+ # Otherwise if the exception controller class is some other value (like false)
85
+ # assume that this controller doesn't handle exceptions.
86
+ else
87
+ false
88
+ end
89
+ end
67
90
  end
68
91
  end
69
92
  end
@@ -150,8 +150,13 @@ module Datadog
150
150
  # signals; it propagates the request span so that it can be finished
151
151
  # no matter what
152
152
  payload = {
153
- controller: self.class.name,
153
+ controller: self.class,
154
154
  action: action_name,
155
+ headers: {
156
+ # The exception this controller was given in the request,
157
+ # which is typical if the controller is configured to handle exceptions.
158
+ request_exception: request.headers['action_dispatch.exception']
159
+ },
155
160
  tracing_context: {}
156
161
  }
157
162
 
@@ -12,6 +12,7 @@ module Datadog
12
12
  option :database_service
13
13
  option :distributed_tracing, default: false
14
14
  option :template_base_path, default: 'views/'
15
+ option :exception_controller, default: nil
15
16
  option :tracer, default: Datadog.tracer
16
17
 
17
18
  @patched = false
@@ -7,7 +7,6 @@ module Datadog
7
7
  DRIVER = 'redis.driver'.freeze
8
8
 
9
9
  # Patcher enables patching of 'redis' module.
10
- # This is used in monkey.rb to automatically apply patches
11
10
  module Patcher
12
11
  include Base
13
12
  register_as :redis, auto_patch: true
@@ -23,7 +22,6 @@ module Datadog
23
22
  if !@patched && compatible?
24
23
  begin
25
24
  # do not require these by default, but only when actually patching
26
- require 'ddtrace/monkey'
27
25
  require 'ddtrace/ext/app_types'
28
26
  require 'ddtrace/contrib/redis/tags'
29
27
  require 'ddtrace/contrib/redis/quantize'
@@ -47,7 +45,7 @@ module Datadog
47
45
  def patch_redis_client
48
46
  ::Redis::Client.class_eval do
49
47
  alias_method :initialize_without_datadog, :initialize
50
- Datadog::Monkey.without_warnings do
48
+ Datadog::Patcher.without_warnings do
51
49
  remove_method :initialize
52
50
  end
53
51
 
@@ -4,6 +4,7 @@ require 'sinatra/base'
4
4
  require 'ddtrace/ext/app_types'
5
5
  require 'ddtrace/ext/errors'
6
6
  require 'ddtrace/ext/http'
7
+ require 'ddtrace/propagation/http_propagator'
7
8
 
8
9
  sinatra_vs = Gem::Version.new(Sinatra::VERSION)
9
10
  sinatra_min_vs = Gem::Version.new('1.4.0')
@@ -29,8 +30,8 @@ module Datadog
29
30
  end
30
31
 
31
32
  option :tracer, default: Datadog.tracer
32
-
33
33
  option :resource_script_names, default: false
34
+ option :distributed_tracing, default: false
34
35
 
35
36
  def route(verb, action, *)
36
37
  # Keep track of the route name when the app is instantiated for an
@@ -82,6 +83,12 @@ module Datadog
82
83
  end
83
84
 
84
85
  tracer = Datadog.configuration[:sinatra][:tracer]
86
+ distributed_tracing = Datadog.configuration[:sinatra][:distributed_tracing]
87
+
88
+ if distributed_tracing && tracer.provider.context.trace_id.nil?
89
+ context = HTTPPropagator.extract(request.env)
90
+ tracer.provider.context = context if context.trace_id
91
+ end
85
92
 
86
93
  span = tracer.trace('sinatra.request',
87
94
  service: Datadog.configuration[:sinatra][:service_name],
@@ -4,6 +4,7 @@ module Datadog
4
4
  TYPE = 'http'.freeze
5
5
  TEMPLATE = 'template'.freeze
6
6
  URL = 'http.url'.freeze
7
+ BASE_URL = 'http.base_url'.freeze
7
8
  METHOD = 'http.method'.freeze
8
9
  STATUS_CODE = 'http.status_code'.freeze
9
10
  ERROR_RANGE = 500...600
@@ -0,0 +1,18 @@
1
+ module Datadog
2
+ # Defines some useful patching methods for integrations
3
+ module Patcher
4
+ module_function
5
+
6
+ def without_warnings
7
+ # This is typically used when monkey patching functions such as
8
+ # intialize, which Ruby advices you not to. Use cautiously.
9
+ v = $VERBOSE
10
+ $VERBOSE = nil
11
+ begin
12
+ yield
13
+ ensure
14
+ $VERBOSE = v
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,7 +2,7 @@ module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 11
5
- PATCH = 0
5
+ PATCH = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-17 00:00:00.000000000 Z
11
+ date: 2018-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -310,7 +310,7 @@ files:
310
310
  - lib/ddtrace/ext/redis.rb
311
311
  - lib/ddtrace/ext/sql.rb
312
312
  - lib/ddtrace/logger.rb
313
- - lib/ddtrace/monkey.rb
313
+ - lib/ddtrace/patcher.rb
314
314
  - lib/ddtrace/pin.rb
315
315
  - lib/ddtrace/pipeline.rb
316
316
  - lib/ddtrace/pipeline/span_filter.rb
@@ -1,88 +0,0 @@
1
- require 'thread'
2
-
3
- # We import all patchers for every module we support, but this is fine
4
- # because patchers do not include any 3rd party module nor even our
5
- # patching code, which is required on demand, when patching.
6
- require 'ddtrace/contrib/base'
7
- require 'ddtrace/contrib/rack/patcher'
8
- require 'ddtrace/contrib/rails/patcher'
9
- require 'ddtrace/contrib/active_record/patcher'
10
- require 'ddtrace/contrib/elasticsearch/patcher'
11
- require 'ddtrace/contrib/faraday/patcher'
12
- require 'ddtrace/contrib/grape/patcher'
13
- require 'ddtrace/contrib/redis/patcher'
14
- require 'ddtrace/contrib/http/patcher'
15
- require 'ddtrace/contrib/aws/patcher'
16
- require 'ddtrace/contrib/sucker_punch/patcher'
17
- require 'ddtrace/contrib/mongodb/patcher'
18
- require 'ddtrace/contrib/dalli/patcher'
19
- require 'ddtrace/contrib/resque/patcher'
20
- require 'ddtrace/contrib/racecar/patcher'
21
- require 'ddtrace/contrib/sidekiq/patcher'
22
-
23
- module Datadog
24
- # Monkey is used for monkey-patching 3rd party libs.
25
- module Monkey
26
- # Patchers should expose 2 methods:
27
- # - patch, which applies our patch if needed. Should be idempotent,
28
- # can be call twice but should just do nothing the second time.
29
- # - patched?, which returns true if the module has been succesfully
30
- # patched (patching might have failed if requirements were not here)
31
-
32
- @mutex = Mutex.new
33
- @registry = Datadog.registry
34
-
35
- module_function
36
-
37
- attr_accessor :registry
38
-
39
- def autopatch_modules
40
- registry.to_h
41
- end
42
-
43
- def patch_all
44
- patch(autopatch_modules)
45
- end
46
-
47
- def patch_module(m)
48
- @mutex.synchronize do
49
- patcher = registry[m]
50
- raise "Unsupported module #{m}" unless patcher
51
- patcher.patch if patcher.respond_to?(:patch)
52
- end
53
- end
54
-
55
- def patch(modules)
56
- modules.each do |k, v|
57
- patch_module(k) if v
58
- end
59
- end
60
-
61
- def get_patched_modules
62
- @mutex.synchronize do
63
- registry.each_with_object({}) do |entry, patched|
64
- next unless entry.klass.respond_to?(:patched?)
65
- patched[entry.name] = entry.klass.patched?
66
- end
67
- end
68
- end
69
-
70
- def without_warnings
71
- # This is typically used when monkey patching functions such as
72
- # intialize, which Ruby advices you not to. Use cautiously.
73
- v = $VERBOSE
74
- $VERBOSE = nil
75
- begin
76
- yield
77
- ensure
78
- $VERBOSE = v
79
- end
80
- end
81
-
82
- class << self
83
- attr_accessor :registry
84
- end
85
- end
86
- end
87
-
88
- Datadog::Monkey.patch_module(:rails)