ddtrace 0.9.2 → 0.10.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 (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)