ddtrace 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -3
  3. data/Appraisals +1 -0
  4. data/ddtrace.gemspec +3 -0
  5. data/docs/GettingStarted.md +31 -8
  6. data/gemfiles/rails32_postgres_redis.gemfile +1 -0
  7. data/lib/ddtrace.rb +20 -34
  8. data/lib/ddtrace/buffer.rb +1 -7
  9. data/lib/ddtrace/configurable.rb +77 -0
  10. data/lib/ddtrace/configuration.rb +35 -0
  11. data/lib/ddtrace/configuration/proxy.rb +29 -0
  12. data/lib/ddtrace/configuration/resolver.rb +24 -0
  13. data/lib/ddtrace/context.rb +55 -7
  14. data/lib/ddtrace/contrib/active_record/patcher.rb +4 -1
  15. data/lib/ddtrace/contrib/aws/patcher.rb +3 -0
  16. data/lib/ddtrace/contrib/base.rb +14 -0
  17. data/lib/ddtrace/contrib/dalli/patcher.rb +3 -0
  18. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +3 -0
  19. data/lib/ddtrace/contrib/faraday/middleware.rb +5 -6
  20. data/lib/ddtrace/contrib/faraday/patcher.rb +3 -0
  21. data/lib/ddtrace/contrib/grape/patcher.rb +3 -0
  22. data/lib/ddtrace/contrib/http/patcher.rb +22 -7
  23. data/lib/ddtrace/contrib/mongodb/patcher.rb +3 -0
  24. data/lib/ddtrace/contrib/rack/middlewares.rb +21 -35
  25. data/lib/ddtrace/contrib/rails/action_controller.rb +2 -2
  26. data/lib/ddtrace/contrib/rails/action_view.rb +2 -2
  27. data/lib/ddtrace/contrib/rails/active_record.rb +2 -2
  28. data/lib/ddtrace/contrib/rails/active_support.rb +2 -2
  29. data/lib/ddtrace/contrib/rails/framework.rb +36 -58
  30. data/lib/ddtrace/contrib/rails/middlewares.rb +1 -1
  31. data/lib/ddtrace/contrib/rails/patcher.rb +56 -0
  32. data/lib/ddtrace/contrib/rails/railtie.rb +18 -0
  33. data/lib/ddtrace/contrib/rails/utils.rb +1 -1
  34. data/lib/ddtrace/contrib/redis/patcher.rb +4 -0
  35. data/lib/ddtrace/contrib/redis/quantize.rb +1 -1
  36. data/lib/ddtrace/contrib/redis/tags.rb +1 -0
  37. data/lib/ddtrace/contrib/resque/patcher.rb +9 -0
  38. data/lib/ddtrace/contrib/resque/resque_job.rb +6 -6
  39. data/lib/ddtrace/contrib/sidekiq/tracer.rb +11 -11
  40. data/lib/ddtrace/contrib/sinatra/tracer.rb +23 -63
  41. data/lib/ddtrace/contrib/sucker_punch/patcher.rb +3 -0
  42. data/lib/ddtrace/ext/distributed.rb +2 -0
  43. data/lib/ddtrace/ext/redis.rb +6 -0
  44. data/lib/ddtrace/monkey.rb +20 -37
  45. data/lib/ddtrace/propagation/distributed_headers.rb +48 -0
  46. data/lib/ddtrace/propagation/http_propagator.rb +28 -0
  47. data/lib/ddtrace/registry.rb +42 -0
  48. data/lib/ddtrace/registry/registerable.rb +20 -0
  49. data/lib/ddtrace/sampler.rb +61 -1
  50. data/lib/ddtrace/sync_writer.rb +36 -0
  51. data/lib/ddtrace/tracer.rb +23 -21
  52. data/lib/ddtrace/transport.rb +52 -15
  53. data/lib/ddtrace/version.rb +2 -2
  54. data/lib/ddtrace/workers.rb +33 -31
  55. data/lib/ddtrace/writer.rb +20 -1
  56. metadata +42 -3
  57. data/lib/ddtrace/distributed.rb +0 -38
@@ -21,7 +21,7 @@ module Datadog
21
21
  # It's not a problem since we re-raise it afterwards so for example a
22
22
  # SignalException::Interrupt would still bubble up.
23
23
  rescue Exception => e
24
- tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
24
+ tracer = Datadog.configuration[:rails][:tracer]
25
25
  span = tracer.active_span()
26
26
  span.set_error(e) unless span.nil?
27
27
  raise e
@@ -0,0 +1,56 @@
1
+ module Datadog
2
+ module Contrib
3
+ module Rails
4
+ # Patcher
5
+ module Patcher
6
+ include Base
7
+ register_as :rails, auto_patch: true
8
+
9
+ option :enabled, default: true
10
+ option :auto_instrument, default: false
11
+ option :auto_instrument_redis, default: false
12
+ option :auto_instrument_grape, default: false
13
+ option :default_service, default: 'rails-app'
14
+ option :default_controller_service, default: 'rails-controller'
15
+ option :default_cache_service, default: 'rails-cache'
16
+ option :default_grape_service, default: 'grape'
17
+ option :default_database_service
18
+ option :distributed_tracing_enabled, default: false
19
+ option :priority_sampling, default: false
20
+ option :template_base_path, default: 'views/'
21
+ option :tracer, default: Datadog.tracer
22
+ option :debug, default: false
23
+ option :trace_agent_hostname, default: Datadog::Writer::HOSTNAME
24
+ option :trace_agent_port, default: Datadog::Writer::PORT
25
+ option :env, default: nil
26
+ option :tags, default: {}
27
+ option :sidekiq_service, default: 'sidekiq'
28
+
29
+ @patched = false
30
+
31
+ class << self
32
+ def patch
33
+ return @patched if patched? || !compatible?
34
+ require_relative 'framework'
35
+ @patched = true
36
+ rescue => e
37
+ Datadog::Tracer.log.error("Unable to apply Rails integration: #{e}")
38
+ @patched
39
+ end
40
+
41
+ def patched?
42
+ @patched
43
+ end
44
+
45
+ def compatible?
46
+ return if ENV['DISABLE_DATADOG_RAILS']
47
+
48
+ defined?(::Rails::VERSION) && ::Rails::VERSION::MAJOR.to_i >= 3
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ require 'ddtrace/contrib/rails/railtie' if Datadog.registry[:rails].compatible?
@@ -0,0 +1,18 @@
1
+ require 'ddtrace/contrib/rails/framework'
2
+ require 'ddtrace/contrib/rails/middlewares'
3
+ require 'ddtrace/contrib/rack/middlewares'
4
+
5
+ module Datadog
6
+ # Railtie class initializes
7
+ class Railtie < Rails::Railtie
8
+ config.app_middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware)
9
+ config.app_middleware.use(Datadog::Contrib::Rails::ExceptionMiddleware)
10
+
11
+ config.after_initialize do |app|
12
+ Datadog::Contrib::Rails::Framework.configure(config: app.config)
13
+ Datadog::Contrib::Rails::Framework.auto_instrument
14
+ Datadog::Contrib::Rails::Framework.auto_instrument_redis
15
+ Datadog::Contrib::Rails::Framework.auto_instrument_grape
16
+ end
17
+ end
18
+ end
@@ -11,7 +11,7 @@ module Datadog
11
11
  def self.normalize_template_name(name)
12
12
  return if name.nil?
13
13
 
14
- base_path = ::Rails.configuration.datadog_trace.fetch(:template_base_path, 'views/')
14
+ base_path = Datadog.configuration[:rails][:template_base_path]
15
15
  sections_view = name.split(base_path)
16
16
 
17
17
  if sections_view.length == 1
@@ -9,6 +9,9 @@ module Datadog
9
9
  # Patcher enables patching of 'redis' module.
10
10
  # This is used in monkey.rb to automatically apply patches
11
11
  module Patcher
12
+ include Base
13
+ register_as :redis, auto_patch: true
14
+
12
15
  @patched = false
13
16
 
14
17
  module_function
@@ -99,6 +102,7 @@ module Datadog
99
102
  commands = args[0].commands.map { |c| Datadog::Contrib::Redis::Quantize.format_command_args(c) }
100
103
  span.resource = commands.join("\n")
101
104
  Datadog::Contrib::Redis::Tags.set_common_tags(self, span)
105
+ span.set_metric Datadog::Ext::Redis::PIPELINE_LEN, commands.length
102
106
 
103
107
  response = call_pipeline_without_datadog(*args, &block)
104
108
  end
@@ -11,7 +11,7 @@ module Datadog
11
11
  module_function
12
12
 
13
13
  def format_arg(arg)
14
- str = arg.to_s
14
+ str = arg.is_a?(Symbol) ? arg.to_s.upcase : arg.to_s
15
15
  Utils.truncate(str, VALUE_MAX_LEN, TOO_LONG_MARK)
16
16
  rescue StandardError => e
17
17
  Datadog::Tracer.log.debug("non formattable Redis arg #{str}: #{e}")
@@ -12,6 +12,7 @@ module Datadog
12
12
  span.set_tag Datadog::Ext::NET::TARGET_HOST, client.host
13
13
  span.set_tag Datadog::Ext::NET::TARGET_PORT, client.port
14
14
  span.set_tag Datadog::Ext::Redis::DB, client.db
15
+ span.set_tag Datadog::Ext::Redis::RAW_COMMAND, span.resource
15
16
  end
16
17
  end
17
18
  end
@@ -1,10 +1,19 @@
1
1
  module Datadog
2
2
  module Contrib
3
+ # Namespace for `resque` integration
3
4
  module Resque
4
5
  SERVICE = 'resque'.freeze
5
6
 
7
+ class << self
8
+ # Globally-acccesible reference for pre-forking optimization
9
+ attr_accessor :sync_writer
10
+ end
11
+
6
12
  # Patcher for Resque integration - sets up the pin for the integration
7
13
  module Patcher
14
+ include Base
15
+ register_as :resque, auto_patch: true
16
+
8
17
  @patched = false
9
18
 
10
19
  class << self
@@ -1,4 +1,5 @@
1
1
  require 'ddtrace/ext/app_types'
2
+ require 'ddtrace/sync_writer'
2
3
  require 'resque'
3
4
 
4
5
  module Datadog
@@ -16,11 +17,6 @@ module Datadog
16
17
  span.service = pin.service
17
18
  end
18
19
  end
19
-
20
- def after_perform(*args)
21
- pin = Pin.get_from(::Resque)
22
- pin.tracer.shutdown! if pin && pin.tracer
23
- end
24
20
  end
25
21
  end
26
22
  end
@@ -30,6 +26,10 @@ Resque.before_first_fork do
30
26
  pin = Datadog::Pin.get_from(Resque)
31
27
  next unless pin && pin.tracer
32
28
  pin.tracer.set_service_info(pin.service, 'resque', Datadog::Ext::AppTypes::WORKER)
29
+
30
+ # Create SyncWriter instance before forking
31
+ sync_writer = Datadog::SyncWriter.new(transport: pin.tracer.writer.transport)
32
+ Datadog::Contrib::Resque.sync_writer = sync_writer
33
33
  end
34
34
 
35
35
  Resque.after_fork do
@@ -38,5 +38,5 @@ Resque.after_fork do
38
38
  next unless pin && pin.tracer
39
39
  # clean the state so no CoW happens
40
40
  pin.tracer.provider.context = nil
41
- pin.tracer.writer.start
41
+ pin.tracer.writer = Datadog::Contrib::Resque.sync_writer
42
42
  end
@@ -14,22 +14,22 @@ Datadog::Tracer.log.debug("Activating instrumentation for Sidekiq '#{sidekiq_vs}
14
14
  module Datadog
15
15
  module Contrib
16
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
17
  # Middleware is a Sidekiq server-side middleware which traces executed jobs
27
18
  class Tracer
19
+ include Base
20
+ register_as :sidekiq
21
+
22
+ option :enabled, default: true
23
+ option :sidekiq_service, default: 'sidekiq'
24
+ option :tracer, default: Datadog.tracer
25
+ option :debug, default: false
26
+ option :trace_agent_hostname, default: Writer::HOSTNAME
27
+ option :trace_agent_port, default: Writer::PORT
28
+
28
29
  def initialize(options = {})
29
30
  # check if Rails configuration is available and use it to override
30
31
  # Sidekiq defaults
31
- rails_config = ::Rails.configuration.datadog_trace rescue {}
32
- base_config = DEFAULT_CONFIG.merge(rails_config)
32
+ base_config = Datadog.configuration[:sidekiq].merge(Datadog.configuration[:rails])
33
33
  user_config = base_config.merge(options)
34
34
  @tracer = user_config[:tracer]
35
35
  @sidekiq_service = user_config[:sidekiq_service]
@@ -17,63 +17,33 @@ Datadog::Tracer.log.info("activating instrumentation for sinatra #{sinatra_vs}")
17
17
  module Datadog
18
18
  module Contrib
19
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
20
+ # Datadog::Contrib::Sinatra::Tracer is a Sinatra extension which traces
21
+ # requests.
22
+ module Tracer
23
+ include Base
24
+ register_as :sinatra
42
25
 
43
- apply()
26
+ option :enabled, default: true, depends_on: [:tracer] do |value|
27
+ get_option(:tracer).enabled = value
44
28
  end
45
29
 
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)
30
+ option :service_name, default: 'sinatra', depends_on: [:tracer] do |value|
31
+ get_option(:tracer).set_service_info(value, 'sinatra', Ext::AppTypes::WEB)
32
+ value
57
33
  end
58
34
 
59
- def [](key)
60
- raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
61
- @cfg[key]
62
- end
35
+ option :tracer, default: Datadog.tracer
63
36
 
64
- def []=(key, value)
65
- raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
66
- @cfg[key] = value
37
+ option(:debug, default: false) { |value| Tracer.debug_logging = value }
38
+
39
+ option :trace_agent_hostname, default: Writer::HOSTNAME, depends_on: [:tracer] do |value|
40
+ get_option(:tracer).configure(hostname: value)
67
41
  end
68
42
 
69
- def enabled?
70
- @cfg[:enabled] && !@cfg[:tracer].nil?
43
+ option :trace_agent_port, default: Writer::PORT, depends_on: [:tracer] do |value|
44
+ get_option(:tracer).configure(port: value)
71
45
  end
72
- end
73
46
 
74
- # Datadog::Contrib::Sinatra::Tracer is a Sinatra extension which traces
75
- # requests.
76
- module Tracer
77
47
  def route(verb, action, *)
78
48
  # Keep track of the route name when the app is instantiated for an
79
49
  # incoming request.
@@ -89,11 +59,9 @@ module Datadog
89
59
  def self.registered(app)
90
60
  ::Sinatra::Base.module_eval do
91
61
  def render(engine, data, *)
92
- cfg = settings.datadog_tracer
93
-
94
62
  output = ''
95
- if cfg.enabled?
96
- tracer = cfg[:tracer]
63
+ tracer = Datadog.configuration[:sinatra][:tracer]
64
+ if tracer.enabled
97
65
  tracer.trace('sinatra.render_template') do |span|
98
66
  # If data is a string, it is a literal template and we don't
99
67
  # want to record it.
@@ -108,15 +76,8 @@ module Datadog
108
76
  end
109
77
  end
110
78
 
111
- app.set :datadog_tracer, TracerCfg.new()
112
-
113
- app.configure do
114
- app.settings.datadog_tracer.apply()
115
- end
116
-
117
79
  app.before do
118
- cfg = settings.datadog_tracer
119
- return unless cfg.enabled?
80
+ return unless Datadog.configuration[:sinatra][:tracer].enabled
120
81
 
121
82
  if instance_variable_defined? :@datadog_request_span
122
83
  if @datadog_request_span
@@ -126,10 +87,10 @@ module Datadog
126
87
  end
127
88
  end
128
89
 
129
- tracer = cfg[:tracer]
90
+ tracer = Datadog.configuration[:sinatra][:tracer]
130
91
 
131
92
  span = tracer.trace('sinatra.request',
132
- service: cfg.cfg[:default_service],
93
+ service: Datadog.configuration[:sinatra][:service_name],
133
94
  span_type: Datadog::Ext::HTTP::TYPE)
134
95
  span.set_tag(Datadog::Ext::HTTP::URL, request.path)
135
96
  span.set_tag(Datadog::Ext::HTTP::METHOD, request.request_method)
@@ -138,8 +99,7 @@ module Datadog
138
99
  end
139
100
 
140
101
  app.after do
141
- cfg = settings.datadog_tracer
142
- return unless cfg.enabled?
102
+ return unless Datadog.configuration[:sinatra][:tracer].enabled
143
103
 
144
104
  span = @datadog_request_span
145
105
  begin
@@ -6,6 +6,9 @@ module Datadog
6
6
 
7
7
  # Responsible for hooking the instrumentation into `sucker_punch`
8
8
  module Patcher
9
+ include Base
10
+ register_as :sucker_punch, auto_patch: true
11
+
9
12
  @patched = false
10
13
 
11
14
  module_function
@@ -5,6 +5,8 @@ module Datadog
5
5
  # These are cross-language (eg: Python, Go and other implementations should honor these)
6
6
  HTTP_HEADER_TRACE_ID = 'x-datadog-trace-id'.freeze
7
7
  HTTP_HEADER_PARENT_ID = 'x-datadog-parent-id'.freeze
8
+ HTTP_HEADER_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'.freeze
9
+ SAMPLING_PRIORITY_KEY = '_sampling_priority_v1'.freeze
8
10
  end
9
11
  end
10
12
  end
@@ -6,6 +6,12 @@ module Datadog
6
6
 
7
7
  # net extension
8
8
  DB = 'out.redis_db'.freeze
9
+
10
+ # raw command
11
+ RAW_COMMAND = 'redis.raw_command'.freeze
12
+
13
+ # pipeline length
14
+ PIPELINE_LEN = 'redis.pipeline_length'.freeze
9
15
  end
10
16
  end
11
17
  end
@@ -3,6 +3,8 @@ require 'thread'
3
3
  # We import all patchers for every module we support, but this is fine
4
4
  # because patchers do not include any 3rd party module nor even our
5
5
  # patching code, which is required on demand, when patching.
6
+ require 'ddtrace/contrib/base'
7
+ require 'ddtrace/contrib/rails/patcher'
6
8
  require 'ddtrace/contrib/active_record/patcher'
7
9
  require 'ddtrace/contrib/elasticsearch/patcher'
8
10
  require 'ddtrace/contrib/faraday/patcher'
@@ -18,53 +20,32 @@ require 'ddtrace/contrib/resque/patcher'
18
20
  module Datadog
19
21
  # Monkey is used for monkey-patching 3rd party libs.
20
22
  module Monkey
21
- @patched = []
22
- @autopatch_modules = {
23
- elasticsearch: true,
24
- http: true,
25
- redis: true,
26
- grape: true,
27
- faraday: true,
28
- aws: true,
29
- sucker_punch: true,
30
- mongo: true,
31
- dalli: true,
32
- resque: true,
33
- active_record: false
34
- }
35
23
  # Patchers should expose 2 methods:
36
24
  # - patch, which applies our patch if needed. Should be idempotent,
37
25
  # can be call twice but should just do nothing the second time.
38
26
  # - patched?, which returns true if the module has been succesfully
39
27
  # patched (patching might have failed if requirements were not here)
40
- @patchers = { elasticsearch: Datadog::Contrib::Elasticsearch::Patcher,
41
- http: Datadog::Contrib::HTTP::Patcher,
42
- redis: Datadog::Contrib::Redis::Patcher,
43
- grape: Datadog::Contrib::Grape::Patcher,
44
- faraday: Datadog::Contrib::Faraday::Patcher,
45
- aws: Datadog::Contrib::Aws::Patcher,
46
- sucker_punch: Datadog::Contrib::SuckerPunch::Patcher,
47
- mongo: Datadog::Contrib::MongoDB::Patcher,
48
- dalli: Datadog::Contrib::Dalli::Patcher,
49
- resque: Datadog::Contrib::Resque::Patcher,
50
- active_record: Datadog::Contrib::ActiveRecord::Patcher }
28
+
51
29
  @mutex = Mutex.new
30
+ @registry = Datadog.registry
52
31
 
53
32
  module_function
54
33
 
34
+ attr_accessor :registry
35
+
55
36
  def autopatch_modules
56
- @autopatch_modules.clone
37
+ registry.to_h
57
38
  end
58
39
 
59
40
  def patch_all
60
- patch @autopatch_modules
41
+ patch(autopatch_modules)
61
42
  end
62
43
 
63
44
  def patch_module(m)
64
45
  @mutex.synchronize do
65
- patcher = @patchers[m]
46
+ patcher = registry[m]
66
47
  raise "Unsupported module #{m}" unless patcher
67
- patcher.patch
48
+ patcher.patch if patcher.respond_to?(:patch)
68
49
  end
69
50
  end
70
51
 
@@ -75,16 +56,12 @@ module Datadog
75
56
  end
76
57
 
77
58
  def get_patched_modules
78
- patched = autopatch_modules
79
- @patchers.each do |k, v|
80
- @mutex.synchronize do
81
- if v
82
- patcher = @patchers[k]
83
- patched[k] = patcher.patched? if patcher
84
- end
59
+ @mutex.synchronize do
60
+ registry.each_with_object({}) do |entry, patched|
61
+ next unless entry.klass.respond_to?(:patched?)
62
+ patched[entry.name] = entry.klass.patched?
85
63
  end
86
64
  end
87
- patched
88
65
  end
89
66
 
90
67
  def without_warnings
@@ -98,5 +75,11 @@ module Datadog
98
75
  $VERBOSE = v
99
76
  end
100
77
  end
78
+
79
+ class << self
80
+ attr_accessor :registry
81
+ end
101
82
  end
102
83
  end
84
+
85
+ Datadog::Monkey.patch_module(:rails)