ddtrace 0.26.1 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +153 -77
  3. data/.circleci/images/primary/{Dockerfile-1.9.3 → Dockerfile-2.5.6} +8 -8
  4. data/.circleci/images/primary/Dockerfile-2.6.4 +73 -0
  5. data/.github/CODEOWNERS +1 -0
  6. data/Appraisals +201 -94
  7. data/CHANGELOG.md +25 -8
  8. data/Rakefile +153 -47
  9. data/ddtrace.gemspec +1 -1
  10. data/docker-compose.yml +56 -26
  11. data/docs/GettingStarted.md +117 -14
  12. data/lib/ddtrace.rb +4 -0
  13. data/lib/ddtrace/analytics.rb +9 -39
  14. data/lib/ddtrace/configuration.rb +0 -19
  15. data/lib/ddtrace/contrib/action_pack/action_controller/instrumentation.rb +144 -0
  16. data/lib/ddtrace/contrib/action_pack/action_controller/patcher.rb +37 -0
  17. data/lib/ddtrace/contrib/action_pack/configuration/settings.rb +25 -0
  18. data/lib/ddtrace/contrib/action_pack/ext.rb +16 -0
  19. data/lib/ddtrace/contrib/action_pack/integration.rb +36 -0
  20. data/lib/ddtrace/contrib/action_pack/patcher.rb +29 -0
  21. data/lib/ddtrace/contrib/action_pack/utils.rb +36 -0
  22. data/lib/ddtrace/contrib/action_view/configuration/settings.rb +24 -0
  23. data/lib/ddtrace/contrib/action_view/ext.rb +17 -0
  24. data/lib/ddtrace/contrib/action_view/instrumentation.rb +192 -0
  25. data/lib/ddtrace/contrib/action_view/integration.rb +43 -0
  26. data/lib/ddtrace/contrib/action_view/patcher.rb +47 -0
  27. data/lib/ddtrace/contrib/action_view/utils.rb +32 -0
  28. data/lib/ddtrace/contrib/active_support/cache/instrumentation.rb +157 -0
  29. data/lib/ddtrace/contrib/active_support/cache/patcher.rb +62 -0
  30. data/lib/ddtrace/contrib/active_support/cache/redis.rb +47 -0
  31. data/lib/ddtrace/contrib/active_support/configuration/settings.rb +23 -0
  32. data/lib/ddtrace/contrib/active_support/ext.rb +21 -0
  33. data/lib/ddtrace/contrib/active_support/integration.rb +38 -0
  34. data/lib/ddtrace/contrib/active_support/patcher.rb +29 -0
  35. data/lib/ddtrace/contrib/ethon/configuration/settings.rb +24 -0
  36. data/lib/ddtrace/contrib/ethon/easy_patch.rb +139 -0
  37. data/lib/ddtrace/contrib/ethon/ext.rb +15 -0
  38. data/lib/ddtrace/contrib/ethon/integration.rb +35 -0
  39. data/lib/ddtrace/contrib/ethon/multi_patch.rb +80 -0
  40. data/lib/ddtrace/contrib/ethon/patcher.rb +27 -0
  41. data/lib/ddtrace/contrib/patchable.rb +1 -1
  42. data/lib/ddtrace/contrib/rails/configuration/settings.rb +43 -6
  43. data/lib/ddtrace/contrib/rails/ext.rb +0 -15
  44. data/lib/ddtrace/contrib/rails/framework.rb +37 -6
  45. data/lib/ddtrace/contrib/rails/middlewares.rb +2 -1
  46. data/lib/ddtrace/contrib/rails/patcher.rb +0 -8
  47. data/lib/ddtrace/contrib/rails/utils.rb +0 -46
  48. data/lib/ddtrace/contrib/redis/patcher.rb +12 -19
  49. data/lib/ddtrace/correlation.rb +8 -12
  50. data/lib/ddtrace/forced_tracing.rb +10 -38
  51. data/lib/ddtrace/sampler.rb +20 -74
  52. data/lib/ddtrace/span.rb +3 -4
  53. data/lib/ddtrace/tracer.rb +4 -11
  54. data/lib/ddtrace/version.rb +2 -2
  55. metadata +32 -9
  56. data/lib/ddtrace/contrib/rails/action_controller.rb +0 -100
  57. data/lib/ddtrace/contrib/rails/action_controller_patch.rb +0 -78
  58. data/lib/ddtrace/contrib/rails/action_view.rb +0 -19
  59. data/lib/ddtrace/contrib/rails/active_support.rb +0 -67
  60. data/lib/ddtrace/contrib/rails/core_extensions.rb +0 -353
@@ -26,13 +26,17 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
26
26
  - [Quickstart for OpenTracing](#quickstart-for-opentracing)
27
27
  - [Manual instrumentation](#manual-instrumentation)
28
28
  - [Integration instrumentation](#integration-instrumentation)
29
+ - [Action View](#action-view)
29
30
  - [Active Model Serializers](#active-model-serializers)
31
+ - [Action Pack](#action-pack)
30
32
  - [Active Record](#active-record)
33
+ - [Active Support](#active-support)
31
34
  - [AWS](#aws)
32
35
  - [Concurrent Ruby](#concurrent-ruby)
33
36
  - [Dalli](#dalli)
34
37
  - [DelayedJob](#delayedjob)
35
38
  - [Elastic Search](#elastic-search)
39
+ - [Ethon & Typhoeus](#ethon)
36
40
  - [Excon](#excon)
37
41
  - [Faraday](#faraday)
38
42
  - [Grape](#grape)
@@ -74,16 +78,18 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
74
78
 
75
79
  **Supported Ruby interpreters**:
76
80
 
77
- | Type | Documentation | Version | Support type |
78
- | ----- | -------------------------- | ----- | ------------ |
79
- | MRI | https://www.ruby-lang.org/ | 1.9.1 | Deprecated |
80
- | | | 1.9.3 | Deprecated |
81
- | | | 2.0 | Full |
82
- | | | 2.1 | Full |
83
- | | | 2.2 | Full |
84
- | | | 2.3 | Full |
85
- | | | 2.4 | Full |
86
- | JRuby | http://jruby.org/ | 9.1.5 | Experimental |
81
+ | Type | Documentation | Version | Support type | Gem version support | EOL Date |
82
+ | ----- | -------------------------- | ----- | ------------ | ------------------------- | ---------------- |
83
+ | MRI | https://www.ruby-lang.org/ | 1.9.1 | Maintenance | < 0.27.0 | August 6th, 2020 |
84
+ | | | 1.9.3 | Maintenance | < 0.27.0 | August 6th, 2020 |
85
+ | | | 2.0 | Full | Latest | TBD |
86
+ | | | 2.1 | Full | Latest | TBD |
87
+ | | | 2.2 | Full | Latest | TBD |
88
+ | | | 2.3 | Full | Latest | TBD |
89
+ | | | 2.4 | Full | Latest | TBD |
90
+ | | | 2.5 | Full | Latest | TBD |
91
+ | | | 2.6 | Full | Latest | TBD |
92
+ | JRuby | http://jruby.org/ | 9.1.5 | Experimental | Latest | TBD |
87
93
 
88
94
  **Supported web servers**:
89
95
 
@@ -105,6 +111,10 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
105
111
 
106
112
  *Deprecated* indicates support will be removed in a future release.
107
113
 
114
+ *Maintenance* indicates only critical bugfixes are backported until EOL.
115
+
116
+ *EOL* indicates support is no longer provided.
117
+
108
118
  ## Installation
109
119
 
110
120
  The following steps will help you quickly start tracing your Ruby application.
@@ -316,13 +326,17 @@ For a list of available integrations, and their configuration options, please re
316
326
 
317
327
  | Name | Key | Versions Supported | How to configure | Gem source |
318
328
  | ------------------------ | -------------------------- | ------------------------ | ----------------------------------- | ------------------------------------------------------------------------------ |
329
+ | Action View | `action_view` | `>= 3.2, < 6.0` | *[Link](#action-view)* | *[Link](https://github.com/rails/rails/tree/master/actionview)* |
319
330
  | Active Model Serializers | `active_model_serializers` | `>= 0.9` | *[Link](#active-model-serializers)* | *[Link](https://github.com/rails-api/active_model_serializers)* |
331
+ | Action Pack | `action_pack` | `>= 3.2, < 6.0` | *[Link](#action-pack)* | *[Link](https://github.com/rails/rails/tree/master/actionpack)* |
320
332
  | Active Record | `active_record` | `>= 3.2, < 6.0` | *[Link](#active-record)* | *[Link](https://github.com/rails/rails/tree/master/activerecord)* |
333
+ | Active Support | `active_support` | `>= 3.2, < 6.0` | *[Link](#active-support)* | *[Link](https://github.com/rails/rails/tree/master/activesupport)* |
321
334
  | AWS | `aws` | `>= 2.0` | *[Link](#aws)* | *[Link](https://github.com/aws/aws-sdk-ruby)* |
322
335
  | Concurrent Ruby | `concurrent_ruby` | `>= 0.9` | *[Link](#concurrent-ruby)* | *[Link](https://github.com/ruby-concurrency/concurrent-ruby)* |
323
336
  | Dalli | `dalli` | `>= 2.7` | *[Link](#dalli)* | *[Link](https://github.com/petergoldstein/dalli)* |
324
337
  | DelayedJob | `delayed_job` | `>= 4.1` | *[Link](#delayedjob)* | *[Link](https://github.com/collectiveidea/delayed_job)* |
325
338
  | Elastic Search | `elasticsearch` | `>= 6.0` | *[Link](#elastic-search)* | *[Link](https://github.com/elastic/elasticsearch-ruby)* |
339
+ | Ethon | `ethon` | `>= 0.11.0` | *[Link](#ethon)* | *[Link](https://github.com/typhoeus/ethon)* |
326
340
  | Excon | `excon` | `>= 0.62` | *[Link](#excon)* | *[Link](https://github.com/excon/excon)* |
327
341
  | Faraday | `faraday` | `>= 0.14` | *[Link](#faraday)* | *[Link](https://github.com/lostisland/faraday)* |
328
342
  | Grape | `grape` | `>= 1.0` | *[Link](#grape)* | *[Link](https://github.com/ruby-grape/grape)* |
@@ -333,17 +347,39 @@ For a list of available integrations, and their configuration options, please re
333
347
  | Net/HTTP | `http` | *(Any supported Ruby)* | *[Link](#nethttp)* | *[Link](https://ruby-doc.org/stdlib-2.4.0/libdoc/net/http/rdoc/Net/HTTP.html)* |
334
348
  | Racecar | `racecar` | `>= 0.3.5` | *[Link](#racecar)* | *[Link](https://github.com/zendesk/racecar)* |
335
349
  | Rack | `rack` | `>= 1.4.7` | *[Link](#rack)* | *[Link](https://github.com/rack/rack)* |
336
- | Rails | `rails` | `>= 3.2, <= 6.0` | *[Link](#rails)* | *[Link](https://github.com/rails/rails)* |
350
+ | Rails | `rails` | `>= 3.2, < 6.0` | *[Link](#rails)* | *[Link](https://github.com/rails/rails)* |
337
351
  | Rake | `rake` | `>= 12.0` | *[Link](#rake)* | *[Link](https://github.com/ruby/rake)* |
338
352
  | Redis | `redis` | `>= 3.2, < 4.0` | *[Link](#redis)* | *[Link](https://github.com/redis/redis-rb)* |
339
353
  | Resque | `resque` | `>= 1.0, < 2.0` | *[Link](#resque)* | *[Link](https://github.com/resque/resque)* |
340
354
  | Rest Client | `rest-client` | `>= 1.8` | *[Link](#rest-client)* | *[Link](https://github.com/rest-client/rest-client)* |
341
355
  | Sequel | `sequel` | `>= 3.41` | *[Link](#sequel)* | *[Link](https://github.com/jeremyevans/sequel)* |
342
- | Shoryuken | `shoryuken` | `>= 4.0.2` | *[Link](#shoryuken)* | *[Link](https://github.com/phstc/shoryuken)*
356
+ | Shoryuken | `shoryuken` | `>= 4.0.2` | *[Link](#shoryuken)* | *[Link](https://github.com/phstc/shoryuken)* |
343
357
  | Sidekiq | `sidekiq` | `>= 3.5.4` | *[Link](#sidekiq)* | *[Link](https://github.com/mperham/sidekiq)* |
344
358
  | Sinatra | `sinatra` | `>= 1.4.5` | *[Link](#sinatra)* | *[Link](https://github.com/sinatra/sinatra)* |
345
359
  | Sucker Punch | `sucker_punch` | `>= 2.0` | *[Link](#sucker-punch)* | *[Link](https://github.com/brandonhilkert/sucker_punch)* |
346
360
 
361
+ ### Action View
362
+
363
+ Most of the time, Active Support is set up as part of Rails, but it can be activated separately:
364
+
365
+ ```ruby
366
+ require 'actionview'
367
+ require 'ddtrace'
368
+
369
+ Datadog.configure do |c|
370
+ c.use :action_view, options
371
+ end
372
+ ```
373
+
374
+ Where `options` is an optional `Hash` that accepts the following parameters:
375
+
376
+ | Key | Description | Default |
377
+ | ---| --- | --- |
378
+ | `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
379
+ | `service_name` | Service name used for rendering instrumentation. | `action_view` |
380
+ | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
381
+ | `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/'` |
382
+
347
383
  ### Active Model Serializers
348
384
 
349
385
  The Active Model Serializers integration traces the `serialize` event for version 0.9+ and the `render` event for version 0.10+.
@@ -366,6 +402,27 @@ ActiveModelSerializers::SerializableResource.new(test_obj).serializable_hash
366
402
  | `service_name` | Service name used for `active_model_serializers` instrumentation. | `'active_model_serializers'` |
367
403
  | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
368
404
 
405
+ ### Action Pack
406
+
407
+ Most of the time, Action Pack is set up as part of Rails, but it can be activated separately:
408
+
409
+ ```ruby
410
+ require 'actionpack'
411
+ require 'ddtrace'
412
+
413
+ Datadog.configure do |c|
414
+ c.use :action_pack, options
415
+ end
416
+ ```
417
+
418
+ Where `options` is an optional `Hash` that accepts the following parameters:
419
+
420
+ | Key | Description | Default |
421
+ | ---| --- | --- |
422
+ | `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
423
+ | `service_name` | Service name used for rendering instrumentation. | `action_pack` |
424
+ | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
425
+
369
426
  ### Active Record
370
427
 
371
428
  Most of the time, Active Record is set up as part of a web framework (Rails, Sinatra...) however, it can be set up alone:
@@ -434,6 +491,30 @@ end
434
491
 
435
492
  If ActiveRecord traces an event that uses a connection that matches a key defined by `describes`, it will use the trace settings assigned to that connection. If the connection does not match any of the described connections, it will use default settings defined by `c.use :active_record` instead.
436
493
 
494
+ ### Active Support
495
+
496
+ Most of the time, Active Support is set up as part of Rails, but it can be activated separately:
497
+
498
+ ```ruby
499
+ require 'activesupport'
500
+ require 'ddtrace'
501
+
502
+ Datadog.configure do |c|
503
+ c.use :active_support, options
504
+ end
505
+
506
+ cache = ActiveSupport::Cache::MemoryStore.new
507
+ cache.read('city')
508
+ ```
509
+
510
+ Where `options` is an optional `Hash` that accepts the following parameters:
511
+
512
+ | Key | Description | Default |
513
+ | ---| --- | --- |
514
+ | `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
515
+ | `cache_service` | Service name used for caching with `active_support` instrumentation. | `active_support-cache` |
516
+ | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
517
+
437
518
  ### AWS
438
519
 
439
520
  The AWS integration will trace every interaction (e.g. API calls) with AWS services (S3, ElastiCache etc.).
@@ -559,6 +640,27 @@ Where `options` is an optional `Hash` that accepts the following parameters:
559
640
  | `service_name` | Service name used for `elasticsearch` instrumentation | `'elasticsearch'` |
560
641
  | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
561
642
 
643
+ ### Ethon
644
+
645
+ The `ethon` integration will trace any HTTP request through `Easy` or `Multi` objects. Note that this integration also supports `Typhoeus` library which is based on `Ethon`.
646
+
647
+ ```ruby
648
+ require 'ddtrace'
649
+
650
+ Datadog.configure do |c|
651
+ c.use :ethon, options
652
+ end
653
+ ```
654
+
655
+ Where `options` is an optional `Hash` that accepts the following parameters:
656
+
657
+ | Key | Description | Default |
658
+ | --- | ----------- | ------- |
659
+ | `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
660
+ | `distributed_tracing` | Enables [distributed tracing](#distributed-tracing) | `true` |
661
+ | `service_name` | Service name for `ethon` instrumentation. | `'ethon'` |
662
+ | `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
663
+
562
664
  ### Excon
563
665
 
564
666
  The `excon` integration is available through the `ddtrace` middleware:
@@ -1000,10 +1102,11 @@ Where `options` is an optional `Hash` that accepts the following parameters:
1000
1102
 
1001
1103
  | Ruby Versions | Supported Rails Versions |
1002
1104
  | ------------- | ------------------------ |
1003
- | 1.9.3 - 2.0 | 3.0 - 3.2 |
1105
+ | 2.0 | 3.0 - 3.2 |
1004
1106
  | 2.1 | 3.0 - 4.2 |
1005
1107
  | 2.2 - 2.3 | 3.0 - 5.2 |
1006
1108
  | 2.4 - 2.5 | 4.2.8 - 5.2 |
1109
+ | 2.6 | 5.0 - 5.2 |
1007
1110
 
1008
1111
  ### Rake
1009
1112
 
@@ -1499,7 +1602,7 @@ Service B:
1499
1602
  Priority: 1
1500
1603
 
1501
1604
  |
1502
- | Service B HTTP Request:
1605
+ | Service C HTTP Request:
1503
1606
  | Headers:
1504
1607
  | x-datadog-trace-id: 100000000000000001
1505
1608
  | x-datadog-parent-id: 100000000000000456
data/lib/ddtrace.rb CHANGED
@@ -21,13 +21,17 @@ module Datadog
21
21
  extend Contrib::Extensions
22
22
  end
23
23
 
24
+ require 'ddtrace/contrib/action_pack/integration'
25
+ require 'ddtrace/contrib/action_view/integration'
24
26
  require 'ddtrace/contrib/active_model_serializers/integration'
25
27
  require 'ddtrace/contrib/active_record/integration'
28
+ require 'ddtrace/contrib/active_support/integration'
26
29
  require 'ddtrace/contrib/aws/integration'
27
30
  require 'ddtrace/contrib/concurrent_ruby/integration'
28
31
  require 'ddtrace/contrib/dalli/integration'
29
32
  require 'ddtrace/contrib/delayed_job/integration'
30
33
  require 'ddtrace/contrib/elasticsearch/integration'
34
+ require 'ddtrace/contrib/ethon/integration'
31
35
  require 'ddtrace/contrib/excon/integration'
32
36
  require 'ddtrace/contrib/faraday/integration'
33
37
  require 'ddtrace/contrib/grape/integration'
@@ -12,46 +12,16 @@ module Datadog
12
12
 
13
13
  # Extension for Datadog::Span
14
14
  module Span
15
- def self.included(base)
16
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
17
- base.class_eval do
18
- # Instance methods
19
- include InstanceMethodsCompatibility
20
- include InstanceMethods
21
- end
15
+ def set_tag(key, value)
16
+ case key
17
+ when Ext::Analytics::TAG_ENABLED
18
+ # If true, set rate to 1.0, otherwise set 0.0.
19
+ value = value == true ? Ext::Analytics::DEFAULT_SAMPLE_RATE : 0.0
20
+ Analytics.set_sample_rate(self, value)
21
+ when Ext::Analytics::TAG_SAMPLE_RATE
22
+ Analytics.set_sample_rate(self, value)
22
23
  else
23
- base.send(:prepend, InstanceMethods)
24
- end
25
- end
26
-
27
- # Compatibility shim for Rubies not supporting `.prepend`
28
- module InstanceMethodsCompatibility
29
- def self.included(base)
30
- base.class_eval do
31
- alias_method :set_tag_without_analytics, :set_tag
32
- # DEV: When we stack multiple extensions the method might already be removed
33
- remove_method :set_tag if method_defined?(:set_tag)
34
- end
35
- end
36
-
37
- def set_tag(*args, &block)
38
- set_tag_without_analytics(*args, &block)
39
- end
40
- end
41
-
42
- # Instance methods
43
- module InstanceMethods
44
- def set_tag(key, value)
45
- case key
46
- when Ext::Analytics::TAG_ENABLED
47
- # If true, set rate to 1.0, otherwise set 0.0.
48
- value = value == true ? Ext::Analytics::DEFAULT_SAMPLE_RATE : 0.0
49
- Analytics.set_sample_rate(self, value)
50
- when Ext::Analytics::TAG_SAMPLE_RATE
51
- Analytics.set_sample_rate(self, value)
52
- else
53
- super if defined?(super)
54
- end
24
+ super if defined?(super)
55
25
  end
56
26
  end
57
27
  end
@@ -6,11 +6,6 @@ module Datadog
6
6
  module Configuration
7
7
  attr_writer :configuration
8
8
 
9
- RUBY_19_DEPRECATION_WARNING = %(
10
- Support for Ruby versions < 2.0 in dd-trace-rb is DEPRECATED.
11
- Last version to support Ruby < 2.0 will be 0.26.x, which will only receive critical bugfixes to existing features.
12
- Support for Ruby versions < 2.0 will be REMOVED with version 0.27.0.).freeze
13
-
14
9
  def configuration
15
10
  @configuration ||= Settings.new
16
11
  end
@@ -21,9 +16,6 @@ module Datadog
21
16
  else
22
17
  PinSetup.new(target, opts).call
23
18
  end
24
-
25
- # Raise Ruby 1.9 deprecation warning, if necessary.
26
- raise_ruby_19_deprecation_warning!
27
19
  end
28
20
 
29
21
  # Helper methods
@@ -34,16 +26,5 @@ module Datadog
34
26
  def runtime_metrics
35
27
  tracer.writer.runtime_metrics
36
28
  end
37
-
38
- # TODO: Remove with version 0.27.0
39
- def raise_ruby_19_deprecation_warning!
40
- return unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0')
41
-
42
- require 'ddtrace/patcher'
43
-
44
- Datadog::Patcher.do_once(:ruby_19_deprecation_warning) do
45
- Datadog::Tracer.log.warn(RUBY_19_DEPRECATION_WARNING)
46
- end
47
- end
48
29
  end
49
30
  end
@@ -0,0 +1,144 @@
1
+ require 'ddtrace/ext/http'
2
+
3
+ require 'ddtrace/contrib/action_pack/ext'
4
+ require 'ddtrace/contrib/action_pack/utils'
5
+ require 'ddtrace/contrib/rack/middlewares'
6
+
7
+ module Datadog
8
+ module Contrib
9
+ module ActionPack
10
+ module ActionController
11
+ # Instrumentation for ActionController components
12
+ module Instrumentation
13
+ module_function
14
+
15
+ def start_processing(payload)
16
+ # trace the execution
17
+ tracer = Datadog.configuration[:action_pack][:tracer]
18
+ service = Datadog.configuration[:action_pack][:controller_service]
19
+ type = Datadog::Ext::HTTP::TYPE_INBOUND
20
+ span = tracer.trace(Ext::SPAN_ACTION_CONTROLLER, service: service, span_type: type)
21
+
22
+ # attach the current span to the tracing context
23
+ tracing_context = payload.fetch(:tracing_context)
24
+ tracing_context[:dd_request_span] = span
25
+ rescue StandardError => e
26
+ Datadog::Tracer.log.error(e.message)
27
+ end
28
+
29
+ def finish_processing(payload)
30
+ # retrieve the tracing context and the latest active span
31
+ tracing_context = payload.fetch(:tracing_context)
32
+ env = payload.fetch(:env)
33
+ span = tracing_context[:dd_request_span]
34
+ return unless span && !span.finished?
35
+
36
+ begin
37
+ # Set the resource name, if it's still the default name
38
+ if span.resource == span.name
39
+ span.resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
40
+ end
41
+
42
+ # Set the resource name of the Rack request span unless this is an exception controller.
43
+ unless exception_controller?(payload)
44
+ rack_request_span = env[Datadog::Contrib::Rack::TraceMiddleware::RACK_REQUEST_SPAN]
45
+ rack_request_span.resource = span.resource if rack_request_span
46
+ end
47
+
48
+ # Set analytics sample rate
49
+ Utils.set_analytics_sample_rate(span)
50
+
51
+ # Associate with runtime metrics
52
+ Datadog.runtime_metrics.associate_with_span(span)
53
+
54
+ span.set_tag(Ext::TAG_ROUTE_ACTION, payload.fetch(:action))
55
+ span.set_tag(Ext::TAG_ROUTE_CONTROLLER, payload.fetch(:controller))
56
+
57
+ exception = payload[:exception_object]
58
+ if exception.nil?
59
+ # [christian] in some cases :status is not defined,
60
+ # rather than firing an error, simply acknowledge we don't know it.
61
+ status = payload.fetch(:status, '?').to_s
62
+ span.status = 1 if status.starts_with?('5')
63
+ elsif Utils.exception_is_error?(exception)
64
+ span.set_error(exception)
65
+ end
66
+ ensure
67
+ span.finish
68
+ end
69
+ rescue StandardError => e
70
+ Datadog::Tracer.log.error(e.message)
71
+ end
72
+
73
+ def exception_controller?(payload)
74
+ exception_controller_class = Datadog.configuration[:action_pack][:exception_controller]
75
+ controller = payload.fetch(:controller)
76
+ headers = payload.fetch(:headers)
77
+
78
+ # If no exception controller class has been set,
79
+ # guess whether this is an exception controller from the headers.
80
+ if exception_controller_class.nil?
81
+ !headers[:request_exception].nil?
82
+ # If an exception controller class has been specified,
83
+ # check if the controller is a kind of the exception controller class.
84
+ elsif exception_controller_class.is_a?(Class) || exception_controller_class.is_a?(Module)
85
+ controller <= exception_controller_class
86
+ # Otherwise if the exception controller class is some other value (like false)
87
+ # assume that this controller doesn't handle exceptions.
88
+ else
89
+ false
90
+ end
91
+ end
92
+
93
+ # Instrumentation for ActionController::Metal
94
+ module Metal
95
+ def process_action(*args)
96
+ # mutable payload with a tracing context that is used in two different
97
+ # signals; it propagates the request span so that it can be finished
98
+ # no matter what
99
+ payload = {
100
+ controller: self.class,
101
+ action: action_name,
102
+ env: request.env,
103
+ headers: {
104
+ # The exception this controller was given in the request,
105
+ # which is typical if the controller is configured to handle exceptions.
106
+ request_exception: request.headers['action_dispatch.exception']
107
+ },
108
+ tracing_context: {}
109
+ }
110
+
111
+ begin
112
+ # process and catch request exceptions
113
+ Instrumentation.start_processing(payload)
114
+ result = super(*args)
115
+ status = datadog_response_status
116
+ payload[:status] = status unless status.nil?
117
+ result
118
+ # rubocop:disable Lint/RescueException
119
+ rescue Exception => e
120
+ payload[:exception] = [e.class.name, e.message]
121
+ payload[:exception_object] = e
122
+ raise e
123
+ end
124
+ # rubocop:enable Lint/RescueException
125
+ ensure
126
+ Instrumentation.finish_processing(payload)
127
+ end
128
+
129
+ def datadog_response_status
130
+ case response
131
+ when ::ActionDispatch::Response
132
+ response.status
133
+ when Array
134
+ # Likely a Rack response array: first element is the status.
135
+ status = response.first
136
+ status.class <= Integer ? status : nil
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end