fair-ddtrace 0.8.2.a

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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/.env +11 -0
  3. data/.gitignore +59 -0
  4. data/.rubocop.yml +61 -0
  5. data/.yardopts +5 -0
  6. data/Appraisals +136 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +24 -0
  9. data/README.md +156 -0
  10. data/Rakefile +176 -0
  11. data/circle.yml +61 -0
  12. data/ddtrace.gemspec +44 -0
  13. data/docker-compose.yml +42 -0
  14. data/docs/GettingStarted.md +735 -0
  15. data/gemfiles/contrib.gemfile +16 -0
  16. data/gemfiles/contrib_old.gemfile +15 -0
  17. data/gemfiles/rails30_postgres.gemfile +10 -0
  18. data/gemfiles/rails30_postgres_sidekiq.gemfile +11 -0
  19. data/gemfiles/rails32_mysql2.gemfile +11 -0
  20. data/gemfiles/rails32_postgres.gemfile +10 -0
  21. data/gemfiles/rails32_postgres_redis.gemfile +11 -0
  22. data/gemfiles/rails32_postgres_sidekiq.gemfile +11 -0
  23. data/gemfiles/rails4_mysql2.gemfile +9 -0
  24. data/gemfiles/rails4_postgres.gemfile +9 -0
  25. data/gemfiles/rails4_postgres_redis.gemfile +10 -0
  26. data/gemfiles/rails4_postgres_sidekiq.gemfile +11 -0
  27. data/gemfiles/rails5_mysql2.gemfile +8 -0
  28. data/gemfiles/rails5_postgres.gemfile +8 -0
  29. data/gemfiles/rails5_postgres_redis.gemfile +9 -0
  30. data/gemfiles/rails5_postgres_sidekiq.gemfile +10 -0
  31. data/lib/ddtrace.rb +73 -0
  32. data/lib/ddtrace/buffer.rb +52 -0
  33. data/lib/ddtrace/context.rb +145 -0
  34. data/lib/ddtrace/contrib/active_record/patcher.rb +94 -0
  35. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +108 -0
  36. data/lib/ddtrace/contrib/elasticsearch/quantize.rb +22 -0
  37. data/lib/ddtrace/contrib/grape/endpoint.rb +164 -0
  38. data/lib/ddtrace/contrib/grape/patcher.rb +73 -0
  39. data/lib/ddtrace/contrib/http/patcher.rb +156 -0
  40. data/lib/ddtrace/contrib/rack/middlewares.rb +150 -0
  41. data/lib/ddtrace/contrib/rails/action_controller.rb +81 -0
  42. data/lib/ddtrace/contrib/rails/action_view.rb +110 -0
  43. data/lib/ddtrace/contrib/rails/active_record.rb +56 -0
  44. data/lib/ddtrace/contrib/rails/active_support.rb +113 -0
  45. data/lib/ddtrace/contrib/rails/core_extensions.rb +137 -0
  46. data/lib/ddtrace/contrib/rails/framework.rb +171 -0
  47. data/lib/ddtrace/contrib/rails/middlewares.rb +32 -0
  48. data/lib/ddtrace/contrib/rails/utils.rb +43 -0
  49. data/lib/ddtrace/contrib/redis/patcher.rb +118 -0
  50. data/lib/ddtrace/contrib/redis/quantize.rb +30 -0
  51. data/lib/ddtrace/contrib/redis/tags.rb +19 -0
  52. data/lib/ddtrace/contrib/sidekiq/tracer.rb +103 -0
  53. data/lib/ddtrace/contrib/sinatra/tracer.rb +169 -0
  54. data/lib/ddtrace/distributed.rb +38 -0
  55. data/lib/ddtrace/encoding.rb +65 -0
  56. data/lib/ddtrace/error.rb +37 -0
  57. data/lib/ddtrace/ext/app_types.rb +10 -0
  58. data/lib/ddtrace/ext/cache.rb +7 -0
  59. data/lib/ddtrace/ext/distributed.rb +10 -0
  60. data/lib/ddtrace/ext/errors.rb +10 -0
  61. data/lib/ddtrace/ext/http.rb +11 -0
  62. data/lib/ddtrace/ext/net.rb +8 -0
  63. data/lib/ddtrace/ext/redis.rb +11 -0
  64. data/lib/ddtrace/ext/sql.rb +8 -0
  65. data/lib/ddtrace/logger.rb +39 -0
  66. data/lib/ddtrace/monkey.rb +84 -0
  67. data/lib/ddtrace/pin.rb +63 -0
  68. data/lib/ddtrace/provider.rb +21 -0
  69. data/lib/ddtrace/sampler.rb +49 -0
  70. data/lib/ddtrace/span.rb +222 -0
  71. data/lib/ddtrace/tracer.rb +310 -0
  72. data/lib/ddtrace/transport.rb +162 -0
  73. data/lib/ddtrace/utils.rb +16 -0
  74. data/lib/ddtrace/version.rb +9 -0
  75. data/lib/ddtrace/workers.rb +108 -0
  76. data/lib/ddtrace/writer.rb +118 -0
  77. metadata +208 -0
@@ -0,0 +1,94 @@
1
+ module Datadog
2
+ module Contrib
3
+ module ActiveRecord
4
+ # Patcher enables patching of 'active_record' module.
5
+ # This is used in monkey.rb to manually apply patches
6
+ module Patcher
7
+ @patched = false
8
+
9
+ module_function
10
+
11
+ # patched? tells whether patch has been successfully applied
12
+ def patched?
13
+ @patched
14
+ end
15
+
16
+ def patch
17
+ if !@patched && defined?(::ActiveRecord)
18
+ begin
19
+ require 'ddtrace/contrib/rails/utils'
20
+ require 'ddtrace/ext/sql'
21
+ require 'ddtrace/ext/app_types'
22
+
23
+ patch_active_record()
24
+
25
+ @patched = true
26
+ rescue StandardError => e
27
+ Datadog::Tracer.log.error("Unable to apply Active Record integration: #{e}")
28
+ end
29
+ end
30
+
31
+ @patched
32
+ end
33
+
34
+ def patch_active_record
35
+ # subscribe when the active record query has been processed
36
+ ::ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
37
+ sql(*args)
38
+ end
39
+ end
40
+
41
+ def self.datadog_trace
42
+ # TODO: Consider using patcher for Rails as well.
43
+ # @tracer ||= defined?(::Rails) && ::Rails.configuration.datadog_trace
44
+ @datadog_trace ||= defined?(::Sinatra) && ::Sinatra::Application.settings.datadog_tracer.cfg
45
+ end
46
+
47
+ def self.adapter_name
48
+ @adapter_name ||= Datadog::Contrib::Rails::Utils.normalize_vendor(
49
+ ::ActiveRecord::Base.connection_config[:adapter]
50
+ )
51
+ end
52
+
53
+ def self.tracer
54
+ return Datadog.tracer unless datadog_trace
55
+ @tracer ||= datadog_trace.fetch(:tracer)
56
+ end
57
+
58
+ def self.database_service
59
+ @database_service ||= if defined?(::Sinatra)
60
+ datadog_trace.fetch(:default_database_service, adapter_name())
61
+ else
62
+ adapter_name()
63
+ end
64
+ if @database_service
65
+ tracer().set_service_info(@database_service, 'sinatra',
66
+ Datadog::Ext::AppTypes::DB)
67
+ end
68
+ @database_service
69
+ end
70
+
71
+ def self.sql(_name, start, finish, _id, payload)
72
+ span_type = Datadog::Ext::SQL::TYPE
73
+
74
+ span = tracer.trace(
75
+ "#{adapter_name}.query",
76
+ resource: payload.fetch(:sql),
77
+ service: database_service,
78
+ span_type: span_type
79
+ )
80
+
81
+ # the span should have the query ONLY in the Resource attribute,
82
+ # so that the ``sql.query`` tag will be set in the agent with an
83
+ # obfuscated version
84
+ span.span_type = Datadog::Ext::SQL::TYPE
85
+ span.set_tag('active_record.db.vendor', adapter_name)
86
+ span.start_time = start
87
+ span.finish(finish)
88
+ rescue StandardError => e
89
+ Datadog::Tracer.log.error(e.message)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,108 @@
1
+ # requirements should be kept minimal as Patcher is a shared requirement.
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Elasticsearch
6
+ URL = 'elasticsearch.url'.freeze
7
+ METHOD = 'elasticsearch.method'.freeze
8
+ PARAMS = 'elasticsearch.params'.freeze
9
+ BODY = 'elasticsearch.body'.freeze
10
+
11
+ SERVICE = 'elasticsearch'.freeze
12
+ SPAN_TYPE = 'elasticsearch'.freeze
13
+
14
+ # Patcher enables patching of 'elasticsearch/transport' module.
15
+ # This is used in monkey.rb to automatically apply patches
16
+ module Patcher
17
+ @patched = false
18
+
19
+ module_function
20
+
21
+ # patch applies our patch if needed
22
+ def patch
23
+ if !@patched && (defined?(::Elasticsearch::Transport::VERSION) && \
24
+ Gem::Version.new(::Elasticsearch::Transport::VERSION) >= Gem::Version.new('1.0.0'))
25
+ begin
26
+ require 'uri'
27
+ require 'json'
28
+ require 'ddtrace/monkey'
29
+ require 'ddtrace/pin'
30
+ require 'ddtrace/ext/app_types'
31
+ require 'ddtrace/contrib/elasticsearch/quantize'
32
+
33
+ patch_elasticsearch_transport_client()
34
+
35
+ @patched = true
36
+ rescue StandardError => e
37
+ Datadog::Tracer.log.error("Unable to apply Elastic Search integration: #{e}")
38
+ end
39
+ end
40
+ @patched
41
+ end
42
+
43
+ # rubocop:disable Metrics/MethodLength
44
+ def patch_elasticsearch_transport_client
45
+ ::Elasticsearch::Transport::Client.class_eval do
46
+ alias_method :initialize_without_datadog, :initialize
47
+ Datadog::Monkey.without_warnings do
48
+ remove_method :initialize
49
+ end
50
+
51
+ def initialize(*args)
52
+ pin = Datadog::Pin.new(SERVICE, app: 'elasticsearch', app_type: Datadog::Ext::AppTypes::DB)
53
+ pin.onto(self)
54
+ initialize_without_datadog(*args)
55
+ end
56
+
57
+ alias_method :perform_request_without_datadog, :perform_request
58
+ remove_method :perform_request
59
+
60
+ def perform_request(*args)
61
+ pin = Datadog::Pin.get_from(self)
62
+ return perform_request_without_datadog(*args) unless pin && pin.tracer
63
+
64
+ method = args[0]
65
+ path = args[1]
66
+ params = args[2]
67
+ body = args[3]
68
+ full_url = URI.parse(path)
69
+
70
+ url = full_url.path
71
+ response = nil
72
+ pin.tracer.trace('elasticsearch.query') do |span|
73
+ begin
74
+ span.service = pin.service
75
+ span.span_type = SPAN_TYPE
76
+
77
+ # load JSON for the following fields unless they're already strings
78
+ params = JSON.generate(params) if params && !params.is_a?(String)
79
+ body = JSON.generate(body) if body && !body.is_a?(String)
80
+
81
+ span.set_tag(METHOD, method)
82
+ span.set_tag(URL, url)
83
+ span.set_tag(PARAMS, params) if params
84
+ span.set_tag(BODY, body) if body
85
+
86
+ quantized_url = Datadog::Contrib::Elasticsearch::Quantize.format_url(url)
87
+ span.resource = "#{method} #{quantized_url}"
88
+ rescue StandardError => e
89
+ Datadog::Tracer.log.error(e.message)
90
+ ensure
91
+ # the call is still executed
92
+ response = perform_request_without_datadog(*args)
93
+ span.set_tag('http.status_code', response.status)
94
+ end
95
+ end
96
+ response
97
+ end
98
+ end
99
+ end
100
+
101
+ # patched? tells wether patch has been successfully applied
102
+ def patched?
103
+ @patched
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,22 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Elasticsearch
4
+ # Quantize contains ES-specific resource quantization tools.
5
+ module Quantize
6
+ ID_REGEXP = %r{\/([0-9]+)([\/\?]|$)}
7
+ ID_PLACEHOLDER = '/?\2'.freeze
8
+
9
+ INDEX_REGEXP = /[0-9]{2,}/
10
+ INDEX_PLACEHOLDER = '?'.freeze
11
+
12
+ module_function
13
+
14
+ # Very basic quantization, complex processing should be done in the agent
15
+ def format_url(url)
16
+ quantized_url = url.gsub(ID_REGEXP, ID_PLACEHOLDER)
17
+ quantized_url.gsub(INDEX_REGEXP, INDEX_PLACEHOLDER)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,164 @@
1
+ require 'ddtrace/ext/http'
2
+ require 'ddtrace/ext/errors'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Grape
7
+ # rubocop:disable Metrics/ModuleLength
8
+ # Endpoint module includes a list of subscribers to create
9
+ # traces when a Grape endpoint is hit
10
+ module Endpoint
11
+ KEY_RUN = 'datadog_grape_endpoint_run'.freeze
12
+ KEY_RENDER = 'datadog_grape_endpoint_render'.freeze
13
+
14
+ def self.subscribe
15
+ # Grape is instrumented only if it's available
16
+ return unless defined?(::Grape) && defined?(::ActiveSupport::Notifications)
17
+
18
+ # subscribe when a Grape endpoint is hit
19
+ ::ActiveSupport::Notifications.subscribe('endpoint_run.grape.start_process') do |*args|
20
+ endpoint_start_process(*args)
21
+ end
22
+ ::ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |*args|
23
+ endpoint_run(*args)
24
+ end
25
+ ::ActiveSupport::Notifications.subscribe('endpoint_render.grape.start_render') do |*args|
26
+ endpoint_start_render(*args)
27
+ end
28
+ ::ActiveSupport::Notifications.subscribe('endpoint_render.grape') do |*args|
29
+ endpoint_render(*args)
30
+ end
31
+ ::ActiveSupport::Notifications.subscribe('endpoint_run_filters.grape') do |*args|
32
+ endpoint_run_filters(*args)
33
+ end
34
+ end
35
+
36
+ def self.endpoint_start_process(*)
37
+ return if Thread.current[KEY_RUN]
38
+
39
+ # retrieve the tracer from the PIN object
40
+ pin = Datadog::Pin.get_from(::Grape)
41
+ return unless pin && pin.enabled?
42
+
43
+ # store the beginning of a trace
44
+ tracer = pin.tracer
45
+ service = pin.service
46
+ type = Datadog::Ext::HTTP::TYPE
47
+ tracer.trace('grape.endpoint_run', service: service, span_type: type)
48
+
49
+ Thread.current[KEY_RUN] = true
50
+ rescue StandardError => e
51
+ Datadog::Tracer.log.error(e.message)
52
+ end
53
+
54
+ def self.endpoint_run(name, start, finish, id, payload)
55
+ return unless Thread.current[KEY_RUN]
56
+ Thread.current[KEY_RUN] = false
57
+
58
+ # retrieve the tracer from the PIN object
59
+ pin = Datadog::Pin.get_from(::Grape)
60
+ return unless pin && pin.enabled?
61
+
62
+ tracer = pin.tracer
63
+ span = tracer.active_span()
64
+ return unless span
65
+
66
+ begin
67
+ # collect endpoint details
68
+ api_view = payload[:endpoint].options[:for].to_s
69
+ path = payload[:endpoint].options[:path].join('/')
70
+ resource = "#{api_view}##{path}"
71
+ span.resource = resource
72
+
73
+ # set the request span resource if it's a `rack.request` span
74
+ request_span = payload[:env][:datadog_rack_request_span]
75
+ if !request_span.nil? && request_span.name == 'rack.request'
76
+ request_span.resource = resource
77
+ end
78
+
79
+ # catch thrown exceptions
80
+ span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
81
+
82
+ # override the current span with this notification values
83
+ span.set_tag('grape.route.endpoint', api_view)
84
+ span.set_tag('grape.route.path', path)
85
+ ensure
86
+ span.start_time = start
87
+ span.finish(finish)
88
+ end
89
+ rescue StandardError => e
90
+ Datadog::Tracer.log.error(e.message)
91
+ end
92
+
93
+ def self.endpoint_start_render(*)
94
+ return if Thread.current[KEY_RENDER]
95
+
96
+ # retrieve the tracer from the PIN object
97
+ pin = Datadog::Pin.get_from(::Grape)
98
+ return unless pin && pin.enabled?
99
+
100
+ # store the beginning of a trace
101
+ tracer = pin.tracer
102
+ service = pin.service
103
+ type = Datadog::Ext::HTTP::TYPE
104
+ tracer.trace('grape.endpoint_render', service: service, span_type: type)
105
+
106
+ Thread.current[KEY_RENDER] = true
107
+ rescue StandardError => e
108
+ Datadog::Tracer.log.error(e.message)
109
+ end
110
+
111
+ def self.endpoint_render(name, start, finish, id, payload)
112
+ return unless Thread.current[KEY_RENDER]
113
+ Thread.current[KEY_RENDER] = false
114
+
115
+ # retrieve the tracer from the PIN object
116
+ pin = Datadog::Pin.get_from(::Grape)
117
+ return unless pin && pin.enabled?
118
+
119
+ tracer = pin.tracer
120
+ span = tracer.active_span()
121
+ return unless span
122
+
123
+ # catch thrown exceptions
124
+ begin
125
+ span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
126
+ ensure
127
+ span.start_time = start
128
+ span.finish(finish)
129
+ end
130
+ rescue StandardError => e
131
+ Datadog::Tracer.log.error(e.message)
132
+ end
133
+
134
+ def self.endpoint_run_filters(name, start, finish, id, payload)
135
+ # retrieve the tracer from the PIN object
136
+ pin = Datadog::Pin.get_from(::Grape)
137
+ return unless pin && pin.enabled?
138
+
139
+ # safe-guard to prevent submitting empty filters
140
+ zero_length = (finish - start).zero?
141
+ filters = payload[:filters]
142
+ type = payload[:type]
143
+ return if (!filters || filters.empty?) || !type || zero_length
144
+
145
+ tracer = pin.tracer
146
+ service = pin.service
147
+ type = Datadog::Ext::HTTP::TYPE
148
+ span = tracer.trace('grape.endpoint_run_filters', service: service, span_type: type)
149
+
150
+ begin
151
+ # catch thrown exceptions
152
+ span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
153
+ span.set_tag('grape.filter.type', type.to_s)
154
+ ensure
155
+ span.start_time = start
156
+ span.finish(finish)
157
+ end
158
+ rescue StandardError => e
159
+ Datadog::Tracer.log.error(e.message)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,73 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Grape
4
+ SERVICE = 'grape'.freeze
5
+
6
+ # Patcher that introduces more instrumentation for Grape endpoints, so that
7
+ # new signals are executed at the beginning of each step (filters, render and run)
8
+ module Patcher
9
+ @patched = false
10
+
11
+ module_function
12
+
13
+ def patched?
14
+ @patched
15
+ end
16
+
17
+ def patch
18
+ if !@patched && defined?(::Grape)
19
+ begin
20
+ # do not require these by default, but only when actually patching
21
+ require 'ddtrace'
22
+ require 'ddtrace/ext/app_types'
23
+ require 'ddtrace/contrib/grape/endpoint'
24
+
25
+ @patched = true
26
+ # patch all endpoints
27
+ patch_endpoint_run()
28
+ patch_endpoint_render()
29
+
30
+ # attach a PIN object globally and set the service once
31
+ pin = Datadog::Pin.new(SERVICE, app: 'grape', app_type: Datadog::Ext::AppTypes::WEB)
32
+ pin.onto(::Grape)
33
+ if pin.tracer && pin.service
34
+ pin.tracer.set_service_info(pin.service, 'grape', pin.app_type)
35
+ end
36
+
37
+ # subscribe to ActiveSupport events
38
+ Datadog::Contrib::Grape::Endpoint.subscribe()
39
+ rescue StandardError => e
40
+ Datadog::Tracer.log.error("Unable to apply Grape integration: #{e}")
41
+ end
42
+ end
43
+ @patched
44
+ end
45
+
46
+ def patch_endpoint_run
47
+ ::Grape::Endpoint.class_eval do
48
+ alias_method :run_without_datadog, :run
49
+ def run(*args)
50
+ ::ActiveSupport::Notifications.instrument('endpoint_run.grape.start_process')
51
+ run_without_datadog(*args)
52
+ end
53
+ end
54
+ end
55
+
56
+ def patch_endpoint_render
57
+ ::Grape::Endpoint.class_eval do
58
+ class << self
59
+ alias_method :generate_api_method_without_datadog, :generate_api_method
60
+ def generate_api_method(*params, &block)
61
+ method_api = generate_api_method_without_datadog(*params, &block)
62
+ proc do |*args|
63
+ ::ActiveSupport::Notifications.instrument('endpoint_render.grape.start_render')
64
+ method_api.call(*args)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end