ddtrace 0.13.0 → 0.14.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 (58) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +88 -6
  3. data/.rubocop.yml +10 -0
  4. data/Appraisals +24 -0
  5. data/CHANGELOG.md +108 -2
  6. data/Gemfile +1 -0
  7. data/Rakefile +32 -2
  8. data/benchmarks/postgres_database.yml +9 -0
  9. data/benchmarks/sidekiq_test.rb +154 -0
  10. data/ddtrace.gemspec +3 -0
  11. data/docs/GettingStarted.md +127 -6
  12. data/lib/ddtrace/configuration.rb +19 -7
  13. data/lib/ddtrace/context.rb +8 -0
  14. data/lib/ddtrace/contrib/active_record/configuration/resolver.rb +46 -0
  15. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +28 -0
  16. data/lib/ddtrace/contrib/active_record/events/sql.rb +11 -7
  17. data/lib/ddtrace/contrib/active_record/integration.rb +44 -0
  18. data/lib/ddtrace/contrib/active_record/patcher.rb +4 -28
  19. data/lib/ddtrace/contrib/active_record/utils.rb +5 -11
  20. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -1
  21. data/lib/ddtrace/contrib/base.rb +4 -3
  22. data/lib/ddtrace/contrib/configurable.rb +55 -0
  23. data/lib/ddtrace/contrib/configuration/option.rb +33 -0
  24. data/lib/ddtrace/contrib/configuration/option_definition.rb +29 -0
  25. data/lib/ddtrace/contrib/configuration/option_definition_set.rb +20 -0
  26. data/lib/ddtrace/contrib/configuration/option_set.rb +8 -0
  27. data/lib/ddtrace/contrib/configuration/options.rb +95 -0
  28. data/lib/ddtrace/contrib/configuration/resolver.rb +12 -0
  29. data/lib/ddtrace/contrib/configuration/settings.rb +35 -0
  30. data/lib/ddtrace/contrib/delayed_job/patcher.rb +41 -0
  31. data/lib/ddtrace/contrib/delayed_job/plugin.rb +43 -0
  32. data/lib/ddtrace/contrib/integration.rb +16 -0
  33. data/lib/ddtrace/contrib/patchable.rb +38 -0
  34. data/lib/ddtrace/contrib/patcher.rb +28 -0
  35. data/lib/ddtrace/contrib/rack/middlewares.rb +34 -15
  36. data/lib/ddtrace/contrib/rake/instrumentation.rb +15 -3
  37. data/lib/ddtrace/contrib/registerable.rb +33 -0
  38. data/lib/ddtrace/contrib/resque/resque_job.rb +2 -10
  39. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +22 -0
  40. data/lib/ddtrace/contrib/rest_client/integration.rb +27 -0
  41. data/lib/ddtrace/contrib/rest_client/patcher.rb +25 -0
  42. data/lib/ddtrace/contrib/rest_client/request_patch.rb +90 -0
  43. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +13 -0
  44. data/lib/ddtrace/contrib/sequel/database.rb +1 -1
  45. data/lib/ddtrace/contrib/sequel/integration.rb +38 -0
  46. data/lib/ddtrace/contrib/sequel/patcher.rb +4 -20
  47. data/lib/ddtrace/monkey.rb +0 -7
  48. data/lib/ddtrace/patcher.rb +5 -0
  49. data/lib/ddtrace/pin.rb +1 -1
  50. data/lib/ddtrace/propagation/http_propagator.rb +6 -0
  51. data/lib/ddtrace/span.rb +0 -1
  52. data/lib/ddtrace/tracer.rb +22 -5
  53. data/lib/ddtrace/vendor/active_record/connection_specification.rb +301 -0
  54. data/lib/ddtrace/version.rb +1 -1
  55. data/lib/ddtrace/workers.rb +6 -4
  56. data/lib/ddtrace/writer.rb +4 -5
  57. data/lib/ddtrace.rb +7 -15
  58. metadata +57 -3
@@ -0,0 +1,154 @@
1
+ ENV['RAILS_ENV'] = 'production'
2
+ require 'English'
3
+
4
+ # Benchmark Configuration container
5
+ module TestConfiguration
6
+ module_function
7
+
8
+ def sidekiq
9
+ Sidekiq.options.tap do |options|
10
+ options[:tag] = 'test'
11
+ options[:queues] << 'default'
12
+ options[:concurrency] = 20
13
+ options[:timeout] = 2
14
+ end
15
+ end
16
+
17
+ def redis
18
+ { pool_size: 30, timeout: 3 }
19
+ end
20
+
21
+ def iteration_count
22
+ 1000
23
+ end
24
+ end
25
+
26
+ require 'bundler/setup'
27
+ require 'rails/all'
28
+ Bundler.require(*Rails.groups)
29
+
30
+ # Example Rails App
31
+ module SampleApp
32
+ class Application < Rails::Application; end
33
+ end
34
+
35
+ # Overrides rails configueration locations
36
+ module OverrideConfiguration
37
+ def paths
38
+ super.tap { |path| path.add 'config/database', with: 'benchmarks/postgres_database.yml' }
39
+ end
40
+ end
41
+
42
+ Rails::Application::Configuration.prepend(OverrideConfiguration)
43
+
44
+ Rails.application.configure do
45
+ config.cache_classes = true
46
+ config.eager_load = true
47
+ config.active_job.queue_adapter = :sidekiq
48
+ end
49
+
50
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
51
+ Rails.application.initialize!
52
+
53
+ ActiveRecord::Schema.define do
54
+ drop_table(:samples) if connection.table_exists?(:samples)
55
+
56
+ create_table :samples do |t|
57
+ t.string :name
58
+ t.timestamps
59
+ end
60
+ end
61
+
62
+ class Sample < ActiveRecord::Base; end
63
+
64
+ require 'sidekiq/launcher'
65
+ require 'sidekiq/cli'
66
+ require 'concurrent/atomic/atomic_fixnum'
67
+
68
+ Sidekiq.configure_server do |config|
69
+ redis_conn = proc do
70
+ Redis.new(
71
+ host: ENV.fetch('TEST_REDIS_HOST', '127.0.0.1'),
72
+ port: ENV.fetch('TEST_REDIS_PORT', 6379)
73
+ )
74
+ end
75
+ config.redis = ConnectionPool.new(size: TestConfiguration.redis[:pool_size],
76
+ timeout: TestConfiguration.redis[:timeout],
77
+ &redis_conn)
78
+ end
79
+
80
+ # Simple Sidekiq worker performing the real benchmark
81
+ class Worker
82
+ class << self
83
+ attr_reader :iterations, :conditional_variable
84
+ end
85
+
86
+ @iterations = Concurrent::AtomicFixnum.new(0)
87
+ @conditional_variable = ConditionVariable.new
88
+
89
+ include Sidekiq::Worker
90
+ def perform(iter, max_iterations)
91
+ self.class.iterations.increment
92
+ self.class.conditional_variable.broadcast if self.class.iterations.value > max_iterations
93
+
94
+ Sample.create!(name: iter.to_s).save
95
+
96
+ 100.times do
97
+ Sample.last.name
98
+ end
99
+
100
+ Sample.last(100).to_a
101
+ end
102
+ end
103
+
104
+ if Datadog.respond_to?(:configure)
105
+ Datadog.configure do |d|
106
+ d.use :rails, enabled: true, tags: { 'tag' => 'value' }
107
+ d.use :http
108
+ d.use :sidekiq, service_name: 'service'
109
+ d.use :redis
110
+ d.use :dalli
111
+ d.use :resque, workers: [Worker]
112
+
113
+ processor = Datadog::Pipeline::SpanProcessor.new do |span|
114
+ true if span.service == 'B'
115
+ end
116
+
117
+ Datadog::Pipeline.before_flush(processor)
118
+ end
119
+ end
120
+
121
+ def current_memory
122
+ `ps -o rss #{$PROCESS_ID}`.split("\n")[1].to_f / 1024
123
+ end
124
+
125
+ def time
126
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
127
+ end
128
+
129
+ def launch(iterations, options)
130
+ iterations.times do |i|
131
+ Worker.perform_async(i, iterations)
132
+ end
133
+
134
+ launcher = Sidekiq::Launcher.new(options)
135
+ launcher.run
136
+ end
137
+
138
+ def wait_and_measure(iterations)
139
+ start = time
140
+
141
+ STDERR.puts "#{time - start}, #{current_memory}"
142
+
143
+ mutex = Mutex.new
144
+
145
+ while Worker.iterations.value < iterations
146
+ mutex.synchronize do
147
+ Worker.conditional_variable.wait(mutex, 1)
148
+ STDERR.puts "#{time - start}, #{current_memory}"
149
+ end
150
+ end
151
+ end
152
+
153
+ launch(TestConfiguration.iteration_count, TestConfiguration.sidekiq)
154
+ wait_and_measure(TestConfiguration.iteration_count)
data/ddtrace.gemspec CHANGED
@@ -43,6 +43,9 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency 'yard', '~> 0.9'
44
44
  spec.add_development_dependency 'webmock', '~> 2.0'
45
45
  spec.add_development_dependency 'builder'
46
+ spec.add_development_dependency 'ruby-prof'
47
+ spec.add_development_dependency 'sqlite3'
48
+
46
49
  # locking transitive dependency of webmock
47
50
  spec.add_development_dependency 'addressable', '~> 2.4.0'
48
51
  spec.add_development_dependency 'redcarpet', '~> 3.4' if RUBY_PLATFORM != 'java'
@@ -26,6 +26,7 @@ For descriptions of terminology used in APM, take a look at the [official docume
26
26
  - [Active Record](#active-record)
27
27
  - [AWS](#aws)
28
28
  - [Dalli](#dalli)
29
+ - [DelayedJob](#delayedjob)
29
30
  - [Elastic Search](#elastic-search)
30
31
  - [Excon](#excon)
31
32
  - [Faraday](#faraday)
@@ -33,12 +34,14 @@ For descriptions of terminology used in APM, take a look at the [official docume
33
34
  - [Grape](#grape)
34
35
  - [GraphQL](#graphql)
35
36
  - [MongoDB](#mongodb)
37
+ - [MySQL2](#mysql2)
36
38
  - [Net/HTTP](#nethttp)
37
39
  - [Racecar](#racecar)
38
40
  - [Rack](#rack)
39
41
  - [Rails](#rails)
40
42
  - [Rake](#rake)
41
43
  - [Redis](#redis)
44
+ - [Rest Client](#restclient)
42
45
  - [Resque](#resque)
43
46
  - [Sequel](#sequel)
44
47
  - [Sidekiq](#sidekiq)
@@ -96,7 +99,7 @@ The Ruby APM tracer sends trace data through the Datadog Agent.
96
99
  ### Quickstart for Rails applications
97
100
 
98
101
  1. Add the `ddtrace` gem to your Gemfile:
99
-
102
+
100
103
  ```ruby
101
104
  source 'https://rubygems.org'
102
105
  gem 'ddtrace'
@@ -162,6 +165,7 @@ And `options` is an optional `Hash` that accepts the following parameters:
162
165
  | ``child_of`` | `Datadog::Span` / `Datadog::Context` | Parent for this span. If not provided, will automatically become current active span. | `nil` |
163
166
  | ``start_time`` | `Integer` | When the span actually starts. Useful when tracing events that have already happened. | `Time.now.utc` |
164
167
  | ``tags`` | `Hash` | Extra tags which should be added to the span. | `{}` |
168
+ | ``on_error`` | `Proc` | Handler invoked when a block is provided to trace, and it raises an error. Provided `span` and `error` as arguments. Sets error on the span by default. | `proc { |span, error| span.set_error(error) unless span.nil? }` |
165
169
 
166
170
  It's highly recommended you set both `service` and `resource` at a minimum. Spans without a `service` or `resource` as `nil` will be discarded by the Datadog agent.
167
171
 
@@ -226,17 +230,26 @@ def finish(name, id, payload)
226
230
  end
227
231
  end
228
232
  ```
229
- #####Enriching traces from nested methods
233
+ ##### Enriching traces from nested methods
230
234
 
231
- You can tag additional information to current active span from any method. Note however that if the method is called and there is no span currently active `active_span` will be nil.
235
+ You can tag additional information to current active span from any method. Note however that if the method is called and there is no span currently active `active_span` will be nil.
232
236
 
233
237
  ```ruby
234
238
  # e.g. adding tag to active span
235
-
239
+
236
240
  current_span = Datadog.tracer.active_span
237
241
  current_span.set_tag('my_tag', 'my_value') unless current_span.nil?
238
242
  ```
239
243
 
244
+ You can also get the root span of the current active trace using the `active_root_span` method. This method will return `nil` if there is no active trace.
245
+
246
+ ```ruby
247
+ # e.g. adding tag to active root span
248
+
249
+ current_root_span = Datadog.tracer.active_root_span
250
+ current_root_span.set_tag('my_tag', 'my_value') unless current_root_span.nil?
251
+ ```
252
+
240
253
  ## Integration instrumentation
241
254
 
242
255
  Many popular libraries and frameworks are supported out-of-the-box, which can be auto-instrumented. Although they are not activated automatically, they can be easily activated and configured by using the `Datadog.configure` API:
@@ -257,6 +270,7 @@ For a list of available integrations, and their configuration options, please re
257
270
  | Active Record | `active_record` | `>= 3.2, < 5.2` | *[Link](#active-record)* | *[Link](https://github.com/rails/rails/tree/master/activerecord)* |
258
271
  | AWS | `aws` | `>= 2.0` | *[Link](#aws)* | *[Link](https://github.com/aws/aws-sdk-ruby)* |
259
272
  | Dalli | `dalli` | `>= 2.7` | *[Link](#dalli)* | *[Link](https://github.com/petergoldstein/dalli)* |
273
+ | DelayedJob | `delayed_job` | `>= 4.1` | *[Link](#delayedjob)* | *[Link](https://github.com/collectiveidea/delayed_job)* |
260
274
  | Elastic Search | `elasticsearch` | `>= 6.0` | *[Link](#elastic-search)* | *[Link](https://github.com/elastic/elasticsearch-ruby)* |
261
275
  | Excon | `excon` | `>= 0.62` | *[Link](#excon)* | *[Link](https://github.com/excon/excon)* |
262
276
  | Faraday | `faraday` | `>= 0.14` | *[Link](#faraday)* | *[Link](https://github.com/lostisland/faraday)* |
@@ -264,12 +278,14 @@ For a list of available integrations, and their configuration options, please re
264
278
  | Grape | `grape` | `>= 1.0` | *[Link](#grape)* | *[Link](https://github.com/ruby-grape/grape)* |
265
279
  | GraphQL | `graphql` | `>= 1.7.9` | *[Link](#graphql)* | *[Link](https://github.com/rmosolgo/graphql-ruby)* |
266
280
  | MongoDB | `mongo` | `>= 2.0, < 2.5` | *[Link](#mongodb)* | *[Link](https://github.com/mongodb/mongo-ruby-driver)* |
281
+ | MySQL2 | `mysql2` | `>= 0.5` | *[Link](#mysql2)* | *[Link](https://github.com/brianmario/mysql2)* |
267
282
  | Net/HTTP | `http` | *(Any supported Ruby)* | *[Link](#nethttp)* | *[Link](https://ruby-doc.org/stdlib-2.4.0/libdoc/net/http/rdoc/Net/HTTP.html)* |
268
283
  | Racecar | `racecar` | `>= 0.3.5` | *[Link](#racecar)* | *[Link](https://github.com/zendesk/racecar)* |
269
284
  | Rack | `rack` | `>= 1.4.7` | *[Link](#rack)* | *[Link](https://github.com/rack/rack)* |
270
285
  | Rails | `rails` | `>= 3.2, < 5.2` | *[Link](#rails)* | *[Link](https://github.com/rails/rails)* |
271
286
  | Rake | `rake` | `>= 12.0` | *[Link](#rake)* | *[Link](https://github.com/ruby/rake)* |
272
287
  | Redis | `redis` | `>= 3.2, < 4.0` | *[Link](#redis)* | *[Link](https://github.com/redis/redis-rb)* |
288
+ | Rest Client | `rest-client` | `>= 1.8` | *[Link](#restclient)* | *[Link](https://github.com/rest-client/rest-client)* |
273
289
  | Resque | `resque` | `>= 1.0, < 2.0` | *[Link](#resque)* | *[Link](https://github.com/resque/resque)* |
274
290
  | Sequel | `sequel` | `>= 3.41` | *[Link](#sequel)* | *[Link](https://github.com/jeremyevans/sequel)* |
275
291
  | Sidekiq | `sidekiq` | `>= 4.0` | *[Link](#sidekiq)* | *[Link](https://github.com/mperham/sidekiq)* |
@@ -304,6 +320,44 @@ Where `options` is an optional `Hash` that accepts the following parameters:
304
320
  | ``service_name`` | Service name used for database portion of `active_record` instrumentation. | Name of database adapter (e.g. `mysql2`) |
305
321
  | ``orm_service_name`` | Service name used for the Ruby ORM portion of `active_record` instrumentation. Overrides service name for ORM spans if explicitly set, which otherwise inherit their service from their parent. | ``active_record`` |
306
322
 
323
+ **Configuring trace settings per database**
324
+
325
+ You can provide the `databases` option to configure trace settings by database connection:
326
+
327
+ ```ruby
328
+ # Provide a `:describes` option with a connection key.
329
+ # Any of the following keys are acceptable, and equivalent to one another.
330
+ # If a block is provided, it yields a Settings object that
331
+ # accepts any of the configuration options listed above.
332
+
333
+ Datadog.configure do |c|
334
+ # Symbol matching your database connection in config/database.yml
335
+ # Only available if you are using Rails with ActiveRecord.
336
+ c.use :active_record, describes: :secondary_database, service_name: 'secondary-db'
337
+
338
+ c.use :active_record, describes: :secondary_database do |second_db|
339
+ second_db.service_name = 'secondary-db'
340
+ end
341
+
342
+ # Connection string with the following connection settings:
343
+ # Adapter, user, host, port, database
344
+ c.use :active_record, describes: 'mysql2://root@127.0.0.1:3306/mysql', service_name: 'secondary-db'
345
+
346
+ # Hash with following connection settings
347
+ # Adapter, user, host, port, database
348
+ c.use :active_record, describes: {
349
+ adapter: 'mysql2',
350
+ host: '127.0.0.1',
351
+ port: '3306',
352
+ database: 'mysql',
353
+ username: 'root'
354
+ },
355
+ service_name: 'secondary-db'
356
+ end
357
+ ```
358
+
359
+ If ActiveRecord traces an event that uses a connection described within `databases`, it will use the trace settings assigned to that connection. If the connection does not match any in the `databases` option, it will use settings defined by `c.use :active_record` instead.
360
+
307
361
  ### AWS
308
362
 
309
363
  The AWS integration will trace every interaction (e.g. API calls) with AWS services (S3, ElastiCache etc.).
@@ -455,7 +509,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
455
509
 
456
510
  ### gRPC
457
511
 
458
- The `grpc` integration adds both client and server interceptors, which run as middleware prior to executing the service's remote procedure call. As gRPC applications are often distributed, the integration shares trace information between client and server.
512
+ The `grpc` integration adds both client and server interceptors, which run as middleware prior to executing the service's remote procedure call. As gRPC applications are often distributed, the integration shares trace information between client and server.
459
513
 
460
514
  To setup your integration, use the ``Datadog.configure`` method like so:
461
515
 
@@ -613,6 +667,29 @@ Where `options` is an optional `Hash` that accepts the following parameters:
613
667
  | ``service_name`` | Service name used for `mongo` instrumentation | mongodb |
614
668
  | ``quantize`` | Hash containing options for quantization. May include `:show` with an Array of keys to not quantize (or `:all` to skip quantization), or `:exclude` with Array of keys to exclude entirely. | ```{ show: [:collection, :database, :operation] }``` |
615
669
 
670
+ ### MySQL2
671
+
672
+ The MySQL2 integration traces any SQL command sent through `mysql2` gem.
673
+
674
+ ```ruby
675
+ require 'mysql2'
676
+ require 'ddtrace'
677
+
678
+ Datadog.configure do |c|
679
+ c.use :mysql2, options
680
+ end
681
+
682
+ client = Mysql2::Client.new(:host => "localhost", :username => "root")
683
+ client.query("SELECT * FROM users WHERE group='x'")
684
+ ```
685
+
686
+ Where `options` is an optional `Hash` that accepts the following parameters:
687
+
688
+ | Key | Description | Default |
689
+ | --- | --- | --- |
690
+ | ``service_name`` | Service name used for MySQL2 instrumentation | `mysql2` |
691
+ | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
692
+
616
693
  ### Net/HTTP
617
694
 
618
695
  The Net/HTTP integration will trace any HTTP call using the standard lib Net::HTTP module.
@@ -759,7 +836,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
759
836
  | Key | Description | Default |
760
837
  | --- | --- | --- |
761
838
  | ``service_name`` | Service name used when tracing application requests (on the `rack` level) | ``<app_name>`` (inferred from your Rails application namespace) |
762
- | ``controller_service`` | Service name used when tracing a Rails action controller | ``<app_name>-controller`` |
839
+ | ``controller_service`` | Service name used when tracing a Rails action controller | ``<app_name>`` |
763
840
  | ``cache_service`` | Cache service name used when tracing cache activity | ``<app_name>-cache`` |
764
841
  | ``database_service`` | Database service name used when tracing database activity | ``<app_name>-<adapter_name>`` |
765
842
  | ``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`` |
@@ -767,6 +844,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
767
844
  | ``middleware_names`` | Enables any short-circuited middleware requests to display the middleware name as resource for the trace. | `false` |
768
845
  | ``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/`` |
769
846
  | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
847
+ | ``databases`` | Hash of tracer settings to use for each database connection. See [ActiveRecord](#activerecord) for more details. | ``{}`` |
770
848
 
771
849
  ### Rake
772
850
 
@@ -869,6 +947,27 @@ customer_cache.get(...) # traced call will belong to `customer-cache` service
869
947
  invoice_cache.get(...) # traced call will belong to `invoice-cache` service
870
948
  ```
871
949
 
950
+ ### Rest Client
951
+
952
+ The `rest-client` integration is available through the `ddtrace` middleware:
953
+
954
+ ```ruby
955
+ require 'rest_client'
956
+ require 'ddtrace'
957
+
958
+ Datadog.configure do |c|
959
+ c.use :rest_client, service_name: 'rest_client' # global service name
960
+ end
961
+ ```
962
+
963
+ Where `options` is an optional `Hash` that accepts the following parameters:
964
+
965
+ | Key | Description | Default |
966
+ | --- | --- | --- |
967
+ | `service_name` | Service name for Rest Client instrumentation. | `'rest_client'` |
968
+ | `distributed_tracing` | Enables [distributed tracing](#distributed-tracing) | `false` |
969
+ | `tracer` | A `Datadog::Tracer` instance used to instrument the application. Usually you don't need to set that. | `Datadog.tracer` |
970
+
872
971
  ### Resque
873
972
 
874
973
  The Resque integration uses Resque hooks that wraps the ``perform`` method.
@@ -963,6 +1062,27 @@ Where `options` is an optional `Hash` that accepts the following parameters:
963
1062
  | --- | --- | --- |
964
1063
  | ``service_name`` | Service name used for `sidekiq` instrumentation | sidekiq |
965
1064
 
1065
+ ### DelayedJob
1066
+
1067
+ The DelayedJob integration uses lifecycle hooks to trace the job executions.
1068
+
1069
+ You can enable it through `Datadog.configure`:
1070
+
1071
+ ```ruby
1072
+ require 'ddtrace'
1073
+
1074
+ Datadog.configure do |c|
1075
+ c.use :delayed_job, options
1076
+ end
1077
+ ```
1078
+
1079
+ Where `options` is an optional `Hash` that accepts the following parameters:
1080
+
1081
+ | Key | Description | Default |
1082
+ | --- | --- | --- |
1083
+ | ``service_name`` | Service name used for `DelayedJob` instrumentation | delayed_job |
1084
+ | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` |
1085
+
966
1086
  ### Sinatra
967
1087
 
968
1088
  The Sinatra integration traces requests and template rendering.
@@ -1238,6 +1358,7 @@ For more details on how to activate distributed tracing for integrations, see th
1238
1358
 
1239
1359
  - [Excon](#excon)
1240
1360
  - [Faraday](#faraday)
1361
+ - [Rest Client](#restclient)
1241
1362
  - [Net/HTTP](#nethttp)
1242
1363
  - [Rack](#rack)
1243
1364
  - [Rails](#rails)
@@ -8,20 +8,32 @@ module Datadog
8
8
  InvalidIntegrationError = Class.new(StandardError)
9
9
 
10
10
  def initialize(options = {})
11
- @registry = options.fetch(:registry, Datadog.registry)
11
+ @registry = options.fetch(:registry) { Datadog.registry }
12
12
  @wrapped_registry = {}
13
13
  end
14
14
 
15
- def [](integration_name)
16
- @wrapped_registry[integration_name] ||= Proxy.new(fetch_integration(integration_name))
15
+ def [](integration_name, configuration_name = :default)
16
+ integration = fetch_integration(integration_name)
17
+
18
+ if integration.class <= Datadog::Contrib::Integration
19
+ integration.configuration(configuration_name)
20
+ else
21
+ @wrapped_registry[integration_name] ||= Proxy.new(integration)
22
+ end
17
23
  end
18
24
 
19
- def use(integration_name, options = {})
25
+ def use(integration_name, options = {}, &block)
20
26
  integration = fetch_integration(integration_name)
21
- settings = Proxy.new(integration)
22
27
 
23
- integration.sorted_options.each do |name|
24
- settings[name] = options.fetch(name, settings[name])
28
+ if integration.class <= Datadog::Contrib::Integration
29
+ configuration_name = options[:describes] || :default
30
+ filtered_options = options.reject { |k, _v| k == :describes }
31
+ integration.configure(configuration_name, filtered_options, &block)
32
+ else
33
+ settings = Proxy.new(integration)
34
+ integration.sorted_options.each do |name|
35
+ settings[name] = options.fetch(name, settings[name])
36
+ end
25
37
  end
26
38
 
27
39
  integration.patch if integration.respond_to?(:patch)
@@ -63,6 +63,12 @@ module Datadog
63
63
  end
64
64
  end
65
65
 
66
+ def current_root_span
67
+ @mutex.synchronize do
68
+ return @current_root_span
69
+ end
70
+ end
71
+
66
72
  # Add a span to the context trace list, keeping it as the last active span.
67
73
  def add_span(span)
68
74
  @mutex.synchronize do
@@ -77,6 +83,7 @@ module Datadog
77
83
  return
78
84
  end
79
85
  set_current_span(span)
86
+ @current_root_span = span if @trace.empty?
80
87
  @trace << span
81
88
  span.context = self
82
89
  end
@@ -158,6 +165,7 @@ module Datadog
158
165
  @sampling_priority = options.fetch(:sampling_priority, nil)
159
166
  @finished_spans = 0
160
167
  @current_span = nil
168
+ @current_root_span = nil
161
169
  end
162
170
 
163
171
  def set_current_span(span)
@@ -0,0 +1,46 @@
1
+ require 'ddtrace/contrib/configuration/resolver'
2
+ require 'ddtrace/vendor/active_record/connection_specification'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module ActiveRecord
7
+ module Configuration
8
+ # Converts Symbols, Strings, and Hashes to a normalized connection settings Hash.
9
+ class Resolver < Contrib::Configuration::Resolver
10
+ def initialize(configurations = nil)
11
+ @configurations = configurations
12
+ end
13
+
14
+ def configurations
15
+ @configurations || ::ActiveRecord::Base.configurations
16
+ end
17
+
18
+ def connection_resolver
19
+ @resolver ||= begin
20
+ if defined?(::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver)
21
+ ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configurations)
22
+ else
23
+ ::Datadog::Vendor::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configurations)
24
+ end
25
+ end
26
+ end
27
+
28
+ def resolve(spec)
29
+ return :default if spec == :default
30
+ normalize(connection_resolver.resolve(spec).symbolize_keys)
31
+ end
32
+
33
+ def normalize(hash)
34
+ {
35
+ adapter: hash[:adapter],
36
+ host: hash[:host],
37
+ port: hash[:port],
38
+ database: hash[:database],
39
+ username: hash[:username]
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ require 'ddtrace/contrib/configuration/settings'
2
+ require 'ddtrace/contrib/active_record/utils'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module ActiveRecord
7
+ module Configuration
8
+ # Unique settings for ActiveRecord
9
+ class Settings < Contrib::Configuration::Settings
10
+ option :orm_service_name
11
+ option :service_name, depends_on: [:tracer] do |value|
12
+ (value || Utils.adapter_name).tap do |service_name|
13
+ tracer.set_service_info(service_name, 'active_record', Ext::AppTypes::DB)
14
+ end
15
+ end
16
+
17
+ option :tracer, default: Datadog.tracer do |value|
18
+ value.tap do
19
+ Events.subscriptions.each do |subscription|
20
+ subscription.tracer = value
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -22,9 +22,13 @@ module Datadog
22
22
  end
23
23
 
24
24
  def process(span, event, _id, payload)
25
- connection_config = Utils.connection_config(payload[:connection_id])
26
- span.name = "#{connection_config[:adapter_name]}.query"
27
- span.service = configuration[:service_name]
25
+ config = Utils.connection_config(payload[:connection_id])
26
+ settings = Datadog.configuration[:active_record, config]
27
+ adapter_name = Datadog::Utils::Database.normalize_vendor(config[:adapter])
28
+ service_name = !settings.nil? ? settings.service_name : configuration[:service_name]
29
+
30
+ span.name = "#{adapter_name}.query"
31
+ span.service = service_name
28
32
  span.resource = payload.fetch(:sql)
29
33
  span.span_type = Datadog::Ext::SQL::TYPE
30
34
 
@@ -33,11 +37,11 @@ module Datadog
33
37
  # is simply cached from memory, so the notification is fired with start == finish.
34
38
  cached = payload[:cached] || (payload[:name] == 'CACHE')
35
39
 
36
- span.set_tag('active_record.db.vendor', connection_config[:adapter_name])
37
- span.set_tag('active_record.db.name', connection_config[:database_name])
40
+ span.set_tag('active_record.db.vendor', adapter_name)
41
+ span.set_tag('active_record.db.name', config[:database])
38
42
  span.set_tag('active_record.db.cached', cached) if cached
39
- span.set_tag('out.host', connection_config[:adapter_host])
40
- span.set_tag('out.port', connection_config[:adapter_port])
43
+ span.set_tag('out.host', config[:host]) if config[:host]
44
+ span.set_tag('out.port', config[:port]) if config[:port]
41
45
  rescue StandardError => e
42
46
  Datadog::Tracer.log.debug(e.message)
43
47
  end
@@ -0,0 +1,44 @@
1
+ require 'ddtrace/ext/sql'
2
+ require 'ddtrace/ext/app_types'
3
+ require 'ddtrace/contrib/integration'
4
+ require 'ddtrace/contrib/active_record/events'
5
+ require 'ddtrace/contrib/active_record/configuration/resolver'
6
+ require 'ddtrace/contrib/active_record/configuration/settings'
7
+ require 'ddtrace/contrib/active_record/patcher'
8
+
9
+ module Datadog
10
+ module Contrib
11
+ module ActiveRecord
12
+ # Describes the ActiveRecord integration
13
+ class Integration
14
+ include Contrib::Integration
15
+
16
+ register_as :active_record, auto_patch: false
17
+
18
+ def self.version
19
+ Gem.loaded_specs['activerecord'] && Gem.loaded_specs['activerecord'].version
20
+ end
21
+
22
+ def self.present?
23
+ super && defined?(::ActiveRecord)
24
+ end
25
+
26
+ def self.compatible?
27
+ super && version >= Gem::Version.new('3.0')
28
+ end
29
+
30
+ def default_configuration
31
+ Configuration::Settings.new
32
+ end
33
+
34
+ def patcher
35
+ ActiveRecord::Patcher
36
+ end
37
+
38
+ def resolver
39
+ @resolver ||= Configuration::Resolver.new
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end