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,32 @@
1
+ require 'ddtrace/ext/http'
2
+
3
+ module Datadog
4
+ module Contrib
5
+ # Rails module includes middlewares that are required for Rails to be properly instrumented.
6
+ module Rails
7
+ # This is only here to catch errors, the Rack module does something very similar, however,
8
+ # since it's not in the same place in the stack, when the Rack middleware is called,
9
+ # error is already swallowed and handled by Rails so we miss the call stack, for instance.
10
+ class ExceptionMiddleware
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ @app.call(env)
17
+ # rubocop:disable Lint/RescueException
18
+ # Here we really want to catch *any* exception, not only StandardError,
19
+ # as we really have no clue of what is in the block,
20
+ # and it is user code which should be executed no matter what.
21
+ # It's not a problem since we re-raise it afterwards so for example a
22
+ # SignalException::Interrupt would still bubble up.
23
+ rescue Exception => e
24
+ tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
25
+ span = tracer.active_span()
26
+ span.set_error(e) unless span.nil?
27
+ raise e
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Rails
4
+ # common utilities for Rails
5
+ module Utils
6
+ # in Rails the template name includes the template full path
7
+ # and it's better to avoid storing such information. This method
8
+ # returns the relative path from `views/` or the template name
9
+ # if a `views/` folder is not in the template full path. A wrong
10
+ # usage ensures that this method will not crash the tracing system.
11
+ def self.normalize_template_name(name)
12
+ return if name.nil?
13
+
14
+ base_path = ::Rails.configuration.datadog_trace.fetch(:template_base_path, 'views/')
15
+ sections_view = name.split(base_path)
16
+
17
+ if sections_view.length == 1
18
+ name.split('/')[-1]
19
+ else
20
+ sections_view[-1]
21
+ end
22
+ rescue
23
+ return name.to_s
24
+ end
25
+
26
+ # TODO: Consider moving this out of Rails.
27
+ # Return a canonical name for a type of database
28
+ def self.normalize_vendor(vendor)
29
+ case vendor
30
+ when nil
31
+ 'defaultdb'
32
+ when 'sqlite3'
33
+ 'sqlite'
34
+ when 'postgresql'
35
+ 'postgres'
36
+ else
37
+ vendor
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,118 @@
1
+ # requirements should be kept minimal as Patcher is a shared requirement.
2
+
3
+ module Datadog
4
+ module Contrib
5
+ module Redis
6
+ SERVICE = 'redis'.freeze
7
+ DRIVER = 'redis.driver'.freeze
8
+
9
+ # Patcher enables patching of 'redis' module.
10
+ # This is used in monkey.rb to automatically apply patches
11
+ module Patcher
12
+ @patched = false
13
+
14
+ module_function
15
+
16
+ # patch applies our patch if needed
17
+ def patch
18
+ if !@patched && (defined?(::Redis::VERSION) && \
19
+ Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('3.0.0'))
20
+ begin
21
+ # do not require these by default, but only when actually patching
22
+ require 'ddtrace/monkey'
23
+ require 'ddtrace/ext/app_types'
24
+ require 'ddtrace/contrib/redis/tags'
25
+ require 'ddtrace/contrib/redis/quantize'
26
+
27
+ patch_redis()
28
+ patch_redis_client()
29
+
30
+ @patched = true
31
+ rescue StandardError => e
32
+ Datadog::Tracer.log.error("Unable to apply Redis integration: #{e}")
33
+ end
34
+ end
35
+ @patched
36
+ end
37
+
38
+ def patch_redis
39
+ ::Redis.module_eval do
40
+ def datadog_pin=(pin)
41
+ # Forward the pin to client, which actually traces calls.
42
+ Datadog::Pin.onto(client, pin)
43
+ end
44
+
45
+ def datadog_pin
46
+ # Get the pin from client, which actually traces calls.
47
+ Datadog::Pin.get_from(client)
48
+ end
49
+ end
50
+ end
51
+
52
+ # rubocop:disable Metrics/MethodLength
53
+ # rubocop:disable Metrics/BlockLength
54
+ def patch_redis_client
55
+ ::Redis::Client.class_eval do
56
+ alias_method :initialize_without_datadog, :initialize
57
+ Datadog::Monkey.without_warnings do
58
+ remove_method :initialize
59
+ end
60
+
61
+ def initialize(*args)
62
+ pin = Datadog::Pin.new(SERVICE, app: 'redis', app_type: Datadog::Ext::AppTypes::DB)
63
+ pin.onto(self)
64
+ if pin.tracer && pin.service
65
+ pin.tracer.set_service_info(pin.service, pin.app, pin.app_type)
66
+ end
67
+ initialize_without_datadog(*args)
68
+ end
69
+
70
+ alias_method :call_without_datadog, :call
71
+ remove_method :call
72
+ def call(*args, &block)
73
+ pin = Datadog::Pin.get_from(self)
74
+ return call_without_datadog(*args, &block) unless pin && pin.tracer
75
+
76
+ response = nil
77
+ pin.tracer.trace('redis.command') do |span|
78
+ span.service = pin.service
79
+ span.span_type = Datadog::Ext::Redis::TYPE
80
+ span.resource = Datadog::Contrib::Redis::Quantize.format_command_args(*args)
81
+ Datadog::Contrib::Redis::Tags.set_common_tags(self, span)
82
+
83
+ response = call_without_datadog(*args, &block)
84
+ end
85
+
86
+ response
87
+ end
88
+
89
+ alias_method :call_pipeline_without_datadog, :call_pipeline
90
+ remove_method :call_pipeline
91
+ def call_pipeline(*args, &block)
92
+ pin = Datadog::Pin.get_from(self)
93
+ return call_pipeline_without_datadog(*args, &block) unless pin && pin.tracer
94
+
95
+ response = nil
96
+ pin.tracer.trace('redis.command') do |span|
97
+ span.service = pin.service
98
+ span.span_type = Datadog::Ext::Redis::TYPE
99
+ commands = args[0].commands.map { |c| Datadog::Contrib::Redis::Quantize.format_command_args(c) }
100
+ span.resource = commands.join("\n")
101
+ Datadog::Contrib::Redis::Tags.set_common_tags(self, span)
102
+
103
+ response = call_pipeline_without_datadog(*args, &block)
104
+ end
105
+
106
+ response
107
+ end
108
+ end
109
+ end
110
+
111
+ # patched? tells wether patch has been successfully applied
112
+ def patched?
113
+ @patched
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,30 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Redis
4
+ # Quantize contains Redis-specific resource quantization tools.
5
+ module Quantize
6
+ PLACEHOLDER = '?'.freeze
7
+ TOO_LONG_MARK = '...'.freeze
8
+ VALUE_MAX_LEN = 100
9
+ CMD_MAX_LEN = 1000
10
+
11
+ module_function
12
+
13
+ def format_arg(arg)
14
+ a = arg.to_s
15
+ a = a[0..(VALUE_MAX_LEN - TOO_LONG_MARK.length - 1)] + TOO_LONG_MARK if a.length > VALUE_MAX_LEN
16
+ a
17
+ rescue StandardError => e
18
+ Datadog::Tracer.log.debug("non formattable Redis arg #{a}: #{e}")
19
+ PLACEHOLDER
20
+ end
21
+
22
+ def format_command_args(command_args)
23
+ cmd = command_args.map { |x| format_arg(x) }.join(' ')
24
+ cmd = cmd[0..(CMD_MAX_LEN - TOO_LONG_MARK.length - 1)] + TOO_LONG_MARK if cmd.length > CMD_MAX_LEN
25
+ cmd
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ require 'ddtrace/ext/net'
2
+ require 'ddtrace/ext/redis'
3
+
4
+ module Datadog
5
+ module Contrib
6
+ module Redis
7
+ # Tags handles generic common tags assignment.
8
+ module Tags
9
+ module_function
10
+
11
+ def set_common_tags(client, span)
12
+ span.set_tag Datadog::Ext::NET::TARGET_HOST, client.host
13
+ span.set_tag Datadog::Ext::NET::TARGET_PORT, client.port
14
+ span.set_tag Datadog::Ext::Redis::DB, client.db
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,103 @@
1
+ require 'sidekiq/api'
2
+
3
+ require 'ddtrace/ext/app_types'
4
+
5
+ sidekiq_vs = Gem::Version.new(Sidekiq::VERSION)
6
+ sidekiq_min_vs = Gem::Version.new('4.0.0')
7
+ if sidekiq_vs < sidekiq_min_vs
8
+ raise "sidekiq version #{sidekiq_vs} is not supported yet " \
9
+ + "(supporting versions >=#{sidekiq_min_vs})"
10
+ end
11
+
12
+ Datadog::Tracer.log.debug("Activating instrumentation for Sidekiq '#{sidekiq_vs}'")
13
+
14
+ module Datadog
15
+ module Contrib
16
+ module Sidekiq
17
+ DEFAULT_CONFIG = {
18
+ enabled: true,
19
+ sidekiq_service: 'sidekiq',
20
+ tracer: Datadog.tracer,
21
+ debug: false,
22
+ trace_agent_hostname: Datadog::Writer::HOSTNAME,
23
+ trace_agent_port: Datadog::Writer::PORT
24
+ }.freeze
25
+
26
+ # Middleware is a Sidekiq server-side middleware which traces executed jobs
27
+ class Tracer
28
+ def initialize(options = {})
29
+ # check if Rails configuration is available and use it to override
30
+ # Sidekiq defaults
31
+ rails_config = ::Rails.configuration.datadog_trace rescue {}
32
+ base_config = DEFAULT_CONFIG.merge(rails_config)
33
+ user_config = base_config.merge(options)
34
+ @tracer = user_config[:tracer]
35
+ @sidekiq_service = user_config[:sidekiq_service]
36
+
37
+ # set Tracer status
38
+ @tracer.enabled = user_config[:enabled]
39
+ Datadog::Tracer.debug_logging = user_config[:debug]
40
+
41
+ # configure the Tracer instance
42
+ @tracer.configure(
43
+ hostname: user_config[:trace_agent_hostname],
44
+ port: user_config[:trace_agent_port]
45
+ )
46
+ end
47
+
48
+ def call(worker, job, queue)
49
+ # If class is wrapping something else, the interesting resource info
50
+ # is the underlying, wrapped class, and not the wrapper.
51
+ resource = if job['wrapped']
52
+ job['wrapped']
53
+ else
54
+ job['class']
55
+ end
56
+
57
+ # configure Sidekiq service
58
+ service = sidekiq_service(resource_worker(resource))
59
+ set_service_info(service)
60
+
61
+ @tracer.trace('sidekiq.job', service: service, span_type: 'job') do |span|
62
+ span.resource = resource
63
+ span.set_tag('sidekiq.job.id', job['jid'])
64
+ span.set_tag('sidekiq.job.retry', job['retry'])
65
+ span.set_tag('sidekiq.job.queue', job['queue'])
66
+ span.set_tag('sidekiq.job.wrapper', job['class']) if job['wrapped']
67
+
68
+ yield
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # rubocop:disable Lint/HandleExceptions
75
+ def resource_worker(resource)
76
+ Object.const_get(resource)
77
+ rescue NameError
78
+ end
79
+
80
+ def worker_config(worker)
81
+ if worker.respond_to?(:datadog_tracer_config)
82
+ worker.datadog_tracer_config
83
+ else
84
+ {}
85
+ end
86
+ end
87
+
88
+ def sidekiq_service(resource)
89
+ worker_config(resource).fetch(:service, @sidekiq_service)
90
+ end
91
+
92
+ def set_service_info(service)
93
+ return if @tracer.services[service]
94
+ @tracer.set_service_info(
95
+ service,
96
+ 'sidekiq',
97
+ Datadog::Ext::AppTypes::WORKER
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,169 @@
1
+
2
+ require 'sinatra/base'
3
+
4
+ require 'ddtrace/ext/app_types'
5
+ require 'ddtrace/ext/errors'
6
+ require 'ddtrace/ext/http'
7
+
8
+ sinatra_vs = Gem::Version.new(Sinatra::VERSION)
9
+ sinatra_min_vs = Gem::Version.new('1.4.0')
10
+ if sinatra_vs < sinatra_min_vs
11
+ raise "sinatra version #{sinatra_vs} is not supported yet " \
12
+ + "(supporting versions >=#{sinatra_min_vs})"
13
+ end
14
+
15
+ Datadog::Tracer.log.info("activating instrumentation for sinatra #{sinatra_vs}")
16
+
17
+ module Datadog
18
+ module Contrib
19
+ module Sinatra
20
+ # TracerCfg is used to manipulate the configuration of the Sinatra
21
+ # tracing extension.
22
+ class TracerCfg
23
+ DEFAULT_CFG = {
24
+ enabled: true,
25
+ default_service: 'sinatra',
26
+ tracer: Datadog.tracer,
27
+ debug: false,
28
+ trace_agent_hostname: Datadog::Writer::HOSTNAME,
29
+ trace_agent_port: Datadog::Writer::PORT
30
+ }.freeze()
31
+
32
+ attr_accessor :cfg
33
+
34
+ def initialize
35
+ @cfg = DEFAULT_CFG.dup()
36
+ end
37
+
38
+ def configure(args = {})
39
+ args.each do |name, value|
40
+ self[name] = value
41
+ end
42
+
43
+ apply()
44
+ end
45
+
46
+ def apply
47
+ Datadog::Tracer.debug_logging = @cfg[:debug]
48
+
49
+ tracer = @cfg[:tracer]
50
+
51
+ tracer.enabled = @cfg[:enabled]
52
+ tracer.configure(hostname: @cfg[:trace_agent_hostname],
53
+ port: @cfg[:trace_agent_port])
54
+
55
+ tracer.set_service_info(@cfg[:default_service], 'sinatra',
56
+ Datadog::Ext::AppTypes::WEB)
57
+ end
58
+
59
+ def [](key)
60
+ raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
61
+ @cfg[key]
62
+ end
63
+
64
+ def []=(key, value)
65
+ raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
66
+ @cfg[key] = value
67
+ end
68
+
69
+ def enabled?
70
+ @cfg[:enabled] && !@cfg[:tracer].nil?
71
+ end
72
+ end
73
+
74
+ # Datadog::Contrib::Sinatra::Tracer is a Sinatra extension which traces
75
+ # requests.
76
+ module Tracer
77
+ def route(verb, action, *)
78
+ # Keep track of the route name when the app is instantiated for an
79
+ # incoming request.
80
+ condition do
81
+ @datadog_route = action
82
+ end
83
+
84
+ super
85
+ end
86
+
87
+ # rubocop:disable Metrics/AbcSize
88
+ # rubocop:disable Metrics/MethodLength
89
+ def self.registered(app)
90
+ ::Sinatra::Base.module_eval do
91
+ def render(engine, data, *)
92
+ cfg = settings.datadog_tracer
93
+
94
+ output = ''
95
+ if cfg.enabled?
96
+ tracer = cfg[:tracer]
97
+ tracer.trace('sinatra.render_template') do |span|
98
+ # If data is a string, it is a literal template and we don't
99
+ # want to record it.
100
+ span.set_tag('sinatra.template_name', data) if data.is_a? Symbol
101
+ output = super
102
+ end
103
+ else
104
+ output = super
105
+ end
106
+
107
+ output
108
+ end
109
+ end
110
+
111
+ app.set :datadog_tracer, TracerCfg.new()
112
+
113
+ app.configure do
114
+ app.settings.datadog_tracer.apply()
115
+ end
116
+
117
+ app.before do
118
+ cfg = settings.datadog_tracer
119
+ return unless cfg.enabled?
120
+
121
+ if instance_variable_defined? :@datadog_request_span
122
+ if @datadog_request_span
123
+ Datadog::Tracer.log.error('request span active in :before hook')
124
+ @datadog_request_span.finish()
125
+ @datadog_request_span = nil
126
+ end
127
+ end
128
+
129
+ tracer = cfg[:tracer]
130
+
131
+ span = tracer.trace('sinatra.request',
132
+ service: cfg.cfg[:default_service],
133
+ span_type: Datadog::Ext::HTTP::TYPE)
134
+ span.set_tag(Datadog::Ext::HTTP::URL, request.path)
135
+ span.set_tag(Datadog::Ext::HTTP::METHOD, request.request_method)
136
+
137
+ @datadog_request_span = span
138
+ end
139
+
140
+ app.after do
141
+ cfg = settings.datadog_tracer
142
+ return unless cfg.enabled?
143
+
144
+ span = @datadog_request_span
145
+ begin
146
+ unless span
147
+ Datadog::Tracer.log.error('missing request span in :after hook')
148
+ return
149
+ end
150
+
151
+ span.resource = "#{request.request_method} #{@datadog_route}"
152
+ span.set_tag('sinatra.route.path', @datadog_route)
153
+ span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
154
+ span.set_error(env['sinatra.error']) if response.server_error?
155
+ span.finish()
156
+ ensure
157
+ @datadog_request_span = nil
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # rubocop:disable Style/Documentation
167
+ module Sinatra
168
+ register Datadog::Contrib::Sinatra::Tracer
169
+ end