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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c67b70da15b11329601d55d07a79c4a9ce28173a
4
- data.tar.gz: 5758cdd918f573731673bd41c05a7f2e2d5d1fe2
3
+ metadata.gz: 4b696f89a64327a9095fce4fa8dfe75fd243f3cb
4
+ data.tar.gz: 38fd90000f89a2379882c0ea73c343f1b8f2fd29
5
5
  SHA512:
6
- metadata.gz: 2ee13f333805a8cfadf6e6bd6a125f636f29ce8e26e5bc71be9abacedf15a81af3dfbafc724c97dad4694899272bf5bfd152ae2eefa533cf70093ab1c2b20566
7
- data.tar.gz: 256cb5ae9b387ba0fbb02b091cb044de139e561e524f895433a16a4e27659a5962966cd3c65495bbe3d073c28d520c685f5f093c809f2f439c9929dde9ebdb7f
6
+ metadata.gz: '0999d891c779b691db6faf84a85096f6465aac6a612dbdbaf2b6dceaf2c9293efd562b8cdaeb6a8a430fcf49752299adab64f21ec734ceb315df080acae55b7d'
7
+ data.tar.gz: 51319812e767f73499185daf8bc81f9121fc021aa43eb146ed246fac998aa824da08f2255f55f1535f1992dc87bcfa9645e3f1e56af8961f6f17e95e40ee753b
data/.gitignore CHANGED
@@ -45,9 +45,8 @@ build-iPhoneSimulator/
45
45
 
46
46
  # for a library or gem, you might want to ignore these files since the code is
47
47
  # intended to run in multiple environments; otherwise, check them in:
48
- # Gemfile.lock
49
- # .ruby-version
50
- # .ruby-gemset
48
+ .ruby-version
49
+ .ruby-gemset
51
50
 
52
51
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
52
  .rvmrc
data/Appraisals CHANGED
@@ -40,6 +40,7 @@ if RUBY_VERSION < '2.4.0' && RUBY_PLATFORM != 'java'
40
40
  gem 'pg', '0.15.1', platform: :ruby
41
41
  gem 'activerecord-jdbcpostgresql-adapter', platform: :jruby
42
42
  gem 'redis-rails'
43
+ gem 'redis', '< 4.0'
43
44
  end
44
45
 
45
46
  if RUBY_VERSION < '2.2.2'
@@ -39,5 +39,8 @@ EOS
39
39
  spec.add_development_dependency 'minitest', '= 5.10.1'
40
40
  spec.add_development_dependency 'appraisal', '~> 2.2'
41
41
  spec.add_development_dependency 'yard', '~> 0.9'
42
+ spec.add_development_dependency 'webmock', '~> 2.0'
43
+ # locking transitive dependency of webmock
44
+ spec.add_development_dependency 'addressable', '~> 2.4.0'
42
45
  spec.add_development_dependency 'redcarpet', '~> 3.4' if RUBY_PLATFORM != 'java'
43
46
  end
@@ -141,6 +141,10 @@ either ``sinatra`` or ``sinatra/base``:
141
141
  require 'ddtrace'
142
142
  require 'ddtrace/contrib/sinatra/tracer'
143
143
 
144
+ Datadog.configure do |c|
145
+ c.use :sinatra, service_name: 'my-app'
146
+ end
147
+
144
148
  get '/' do
145
149
  'Hello world!'
146
150
  end
@@ -149,18 +153,11 @@ The tracing extension will be automatically activated.
149
153
 
150
154
  #### Configure the tracer
151
155
 
152
- To modify the default configuration, use the ``settings.datadog_tracer.configure`` method. For example,
153
- to change the default service name and activate the debug mode:
154
-
155
- configure do
156
- settings.datadog_tracer.configure default_service: 'my-app', debug: true
157
- end
158
-
159
156
  Available settings are:
160
157
 
161
158
  * ``enabled``: define if the ``tracer`` is enabled or not. If set to ``false``, the code is still instrumented
162
159
  but no spans are sent to the local trace agent.
163
- * ``default_service``: set the service name used when tracing application requests. Defaults to ``sinatra``
160
+ * ``service_name``: set the service name used when tracing application requests. Defaults to ``sinatra``
164
161
  * ``tracer``: set the tracer to use. Usually you don't need to change that value
165
162
  unless you're already using a different initialized tracer somewhere else
166
163
  * ``debug``: set to ``true`` to enable debug logging.
@@ -719,6 +716,32 @@ overhead.
719
716
  sampler = Datadog::RateSampler.new(0.5) # sample 50% of the traces
720
717
  Datadog.tracer.configure(sampler: sampler)
721
718
 
719
+ #### Priority sampling
720
+
721
+ Priority sampling consists in deciding if a trace will be kept by using a priority attribute that will be propagated for distributed traces. Its value gives indication to the Agent and to the backend on how important the trace is.
722
+
723
+ * 0: Don’t keep the trace.
724
+ * 1: The sampler automatically decided to keep the trace.
725
+ * 2: The user asked the keep the trace.
726
+
727
+ For now, priority sampling is disabled by default. Enabling it ensures that your sampled distributed traces will be complete. To enable the priority sampling:
728
+
729
+ ```rb
730
+ Datadog.tracer.configure(priority_sampling: true)
731
+ ```
732
+
733
+ Once enabled, the sampler will automatically assign a priority of 0 or 1 to traces, depending on their service and volume.
734
+
735
+ You can also set this priority manually to either drop a non-interesting trace or to keep an important one. For that, set the `context#sampling_priority` to 0 or 2. It has to be done before any context propagation (fork, RPC calls) to be effective:
736
+
737
+ ```rb
738
+ # Indicate to not keep the trace
739
+ span.context.sampling_priority = 0
740
+
741
+ # Indicate to keep the trace
742
+ span.context.sampling_priority = 2
743
+ ```
744
+
722
745
  ### Distributed Tracing
723
746
 
724
747
  To trace requests across hosts, the spans on the secondary hosts must be linked together by setting ``trace_id`` and ``parent_id``:
@@ -7,5 +7,6 @@ gem "rails", "3.2.22.5"
7
7
  gem "pg", "0.15.1", platform: :ruby
8
8
  gem "activerecord-jdbcpostgresql-adapter", platform: :jruby
9
9
  gem "redis-rails"
10
+ gem "redis", "< 4.0"
10
11
 
11
12
  gemspec path: "../"
@@ -1,12 +1,14 @@
1
- require 'ddtrace/monkey'
1
+ require 'ddtrace/registry'
2
2
  require 'ddtrace/pin'
3
3
  require 'ddtrace/tracer'
4
4
  require 'ddtrace/error'
5
5
  require 'ddtrace/pipeline'
6
+ require 'ddtrace/configuration'
6
7
 
7
8
  # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
8
9
  module Datadog
9
- @tracer = Datadog::Tracer.new()
10
+ @tracer = Tracer.new
11
+ @registry = Registry.new
10
12
 
11
13
  # Default tracer that can be used as soon as +ddtrace+ is required:
12
14
  #
@@ -25,42 +27,26 @@ module Datadog
25
27
  def self.tracer
26
28
  @tracer
27
29
  end
28
- end
29
30
 
30
- # Datadog auto instrumentation for frameworks
31
- if defined?(Rails::VERSION)
32
- if !ENV['DISABLE_DATADOG_RAILS']
33
- if Rails::VERSION::MAJOR.to_i >= 3
34
- require 'ddtrace/contrib/rails/framework'
35
- require 'ddtrace/contrib/rails/middlewares'
31
+ def self.registry
32
+ @registry
33
+ end
36
34
 
37
- module Datadog
38
- # Railtie class initializes
39
- class Railtie < Rails::Railtie
40
- # add instrumentation middlewares
41
- options = {}
42
- config.app_middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware, options)
43
- config.app_middleware.use(Datadog::Contrib::Rails::ExceptionMiddleware)
35
+ class << self
36
+ attr_writer :configuration
44
37
 
45
- # auto instrument Rails and third party components after
46
- # the framework initialization
47
- config.after_initialize do |app|
48
- Datadog::Contrib::Rails::Framework.configure(config: app.config)
49
- Datadog::Contrib::Rails::Framework.auto_instrument()
50
- Datadog::Contrib::Rails::Framework.auto_instrument_redis()
51
- Datadog::Contrib::Rails::Framework.auto_instrument_grape()
38
+ def configuration
39
+ @configuration ||= Configuration.new
40
+ end
52
41
 
53
- # override Rack Middleware configurations with Rails
54
- options.update(::Rails.configuration.datadog_trace)
55
- end
56
- end
57
- end
58
- else
59
- Datadog::Tracer.log.warn 'Detected a Rails version < 3.x.'\
60
- 'This version is not supported yet and the'\
61
- 'auto-instrumentation for core components will be disabled.'
42
+ def configure
43
+ yield(configuration)
62
44
  end
63
- else
64
- Datadog::Tracer.log.info 'Skipping Rails auto-instrumentation, DISABLE_DATADOG_RAILS is set.'
65
45
  end
66
46
  end
47
+
48
+ # Monkey currently is responsible for loading all contributions, which in turn
49
+ # rely on the registry defined above. We should make our code less dependent on
50
+ # the load order, by letting things be lazily loaded while keeping
51
+ # thread-safety.
52
+ require 'ddtrace/monkey'
@@ -16,8 +16,8 @@ module Datadog
16
16
  # Add a new ``trace`` in the local queue. This method doesn't block the execution
17
17
  # even if the buffer is full. In that case, a random trace is discarded.
18
18
  def push(trace)
19
- return if @closed
20
19
  @mutex.synchronize do
20
+ return if @closed
21
21
  len = @traces.length
22
22
  if len < @max_size || @max_size <= 0
23
23
  @traces << trace
@@ -56,11 +56,5 @@ module Datadog
56
56
  @closed = true
57
57
  end
58
58
  end
59
-
60
- def closed?
61
- @mutex.synchronise do
62
- return @closed
63
- end
64
- end
65
59
  end
66
60
  end
@@ -0,0 +1,77 @@
1
+ module Datadog
2
+ InvalidOptionError = Class.new(StandardError)
3
+ # Configurable provides configuration methods for a given class/module
4
+ module Configurable
5
+ IDENTITY = ->(x) { x }
6
+
7
+ def self.included(base)
8
+ base.singleton_class.send(:include, ClassMethods)
9
+ end
10
+
11
+ # ClassMethods
12
+ module ClassMethods
13
+ def set_option(name, value)
14
+ __assert_valid!(name)
15
+
16
+ __options[name][:value] = __options[name][:setter].call(value)
17
+ __options[name][:set_flag] = true
18
+ end
19
+
20
+ def get_option(name)
21
+ __assert_valid!(name)
22
+
23
+ return __options[name][:default] unless __options[name][:set_flag]
24
+
25
+ __options[name][:value]
26
+ end
27
+
28
+ def to_h
29
+ __options.each_with_object({}) do |(key, _), hash|
30
+ hash[key] = get_option(key)
31
+ end
32
+ end
33
+
34
+ def reset_options!
35
+ __options.each do |name, meta|
36
+ set_option(name, meta[:default])
37
+ end
38
+ end
39
+
40
+ def sorted_options
41
+ Configuration::Resolver.new(__dependency_graph).call
42
+ end
43
+
44
+ private
45
+
46
+ def option(name, meta = {}, &block)
47
+ name = name.to_sym
48
+ meta[:setter] ||= (block || IDENTITY)
49
+ meta[:depends_on] ||= []
50
+ __options[name] = meta
51
+ end
52
+
53
+ def __options
54
+ @__options ||= {}
55
+ end
56
+
57
+ def __assert_valid!(name)
58
+ return if __options.key?(name)
59
+ raise(InvalidOptionError, "#{__pretty_name} doesn't have the option: #{name}")
60
+ end
61
+
62
+ def __pretty_name
63
+ entry = Datadog.registry.find { |el| el.klass == self }
64
+
65
+ return entry.name if entry
66
+
67
+ to_s
68
+ end
69
+
70
+ def __dependency_graph
71
+ __options.each_with_object({}) do |(name, meta), graph|
72
+ graph[name] = meta[:depends_on]
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'configuration/proxy'
2
+ require_relative 'configuration/resolver'
3
+
4
+ module Datadog
5
+ # Configuration provides a unique access point for configurations
6
+ class Configuration
7
+ InvalidIntegrationError = Class.new(StandardError)
8
+
9
+ def initialize(options = {})
10
+ @registry = options.fetch(:registry, Datadog.registry)
11
+ end
12
+
13
+ def [](integration_name)
14
+ integration = fetch_integration(integration_name)
15
+ Proxy.new(integration)
16
+ end
17
+
18
+ def use(integration_name, options = {})
19
+ integration = fetch_integration(integration_name)
20
+
21
+ integration.sorted_options.each do |name|
22
+ integration.set_option(name, options[name]) if options.key?(name)
23
+ end
24
+
25
+ integration.patch if integration.respond_to?(:patch)
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_integration(name)
31
+ @registry[name] ||
32
+ raise(InvalidIntegrationError, "'#{name}' is not a valid integration.")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ require 'forwardable'
2
+
3
+ module Datadog
4
+ class Configuration
5
+ # Proxy provides a hash-like interface for fetching/setting configurations
6
+ class Proxy
7
+ extend Forwardable
8
+
9
+ def initialize(integration)
10
+ @integration = integration
11
+ end
12
+
13
+ def [](param)
14
+ value = @integration.get_option(param)
15
+
16
+ return value.call if value.respond_to?(:call)
17
+
18
+ value
19
+ end
20
+
21
+ def []=(param, value)
22
+ @integration.set_option(param, value)
23
+ end
24
+
25
+ def_delegators :@integration, :to_h, :reset_options!
26
+ def_delegators :to_h, :to_hash, :merge
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ require 'tsort'
2
+
3
+ module Datadog
4
+ class Configuration
5
+ # Resolver performs a topological sort over the dependency graph
6
+ class Resolver
7
+ include TSort
8
+
9
+ def initialize(dependency_graph = {})
10
+ @dependency_graph = dependency_graph
11
+ end
12
+
13
+ def tsort_each_node(&blk)
14
+ @dependency_graph.each_key(&blk)
15
+ end
16
+
17
+ def tsort_each_child(node, &blk)
18
+ @dependency_graph.fetch(node).each(&blk)
19
+ end
20
+
21
+ alias call tsort
22
+ end
23
+ end
24
+ end
@@ -15,18 +15,45 @@ module Datadog
15
15
  # This data structure is thread-safe.
16
16
  class Context
17
17
  # Initialize a new thread-safe \Context.
18
- def initialize
18
+ def initialize(options = {})
19
19
  @mutex = Mutex.new
20
- reset
20
+ reset(options)
21
21
  end
22
22
 
23
- def reset
23
+ def reset(options = {})
24
24
  @trace = []
25
- @sampled = false
25
+ @parent_trace_id = options.fetch(:trace_id, nil)
26
+ @parent_span_id = options.fetch(:span_id, nil)
27
+ @sampled = options.fetch(:sampled, false)
28
+ @sampling_priority = options.fetch(:sampling_priority, nil)
26
29
  @finished_spans = 0
27
30
  @current_span = nil
28
31
  end
29
32
 
33
+ def trace_id
34
+ @mutex.synchronize do
35
+ @parent_trace_id
36
+ end
37
+ end
38
+
39
+ def span_id
40
+ @mutex.synchronize do
41
+ @parent_span_id
42
+ end
43
+ end
44
+
45
+ def sampling_priority
46
+ @mutex.synchronize do
47
+ @sampling_priority
48
+ end
49
+ end
50
+
51
+ def sampling_priority=(priority)
52
+ @mutex.synchronize do
53
+ @sampling_priority = priority
54
+ end
55
+ end
56
+
30
57
  # Return the last active span that corresponds to the last inserted
31
58
  # item in the trace list. This cannot be considered as the current active
32
59
  # span in asynchronous environments, because some spans can be closed
@@ -37,11 +64,21 @@ module Datadog
37
64
  end
38
65
  end
39
66
 
67
+ def set_current_span(span)
68
+ @current_span = span
69
+ if span
70
+ @parent_trace_id = span.trace_id
71
+ @parent_span_id = span.span_id
72
+ @sampled = span.sampled
73
+ else
74
+ @parent_span_id = nil
75
+ end
76
+ end
77
+
40
78
  # Add a span to the context trace list, keeping it as the last active span.
41
79
  def add_span(span)
42
80
  @mutex.synchronize do
43
- @current_span = span
44
- @sampled = span.sampled
81
+ set_current_span(span)
45
82
  @trace << span
46
83
  span.context = self
47
84
  end
@@ -55,7 +92,7 @@ module Datadog
55
92
  # Current span is only meaningful for linear tree-like traces,
56
93
  # in other cases, this is just broken and one should rely
57
94
  # on per-instrumentation code to retrieve handle parent/child relations.
58
- @current_span = span.parent
95
+ set_current_span(span.parent)
59
96
  return if span.tracer.nil?
60
97
  return unless Datadog::Tracer.debug_logging
61
98
  if span.parent.nil? && !check_finished_spans
@@ -102,6 +139,8 @@ module Datadog
102
139
 
103
140
  trace = @trace
104
141
  sampled = @sampled
142
+ attach_sampling_priority if sampled && @sampling_priority
143
+
105
144
  reset
106
145
  return trace, sampled
107
146
  end
@@ -115,8 +154,17 @@ module Datadog
115
154
  end
116
155
  end
117
156
 
157
+ def attach_sampling_priority
158
+ @trace.first.set_metric(
159
+ Ext::DistributedTracing::SAMPLING_PRIORITY_KEY,
160
+ @sampling_priority
161
+ )
162
+ end
163
+
118
164
  private :reset
119
165
  private :check_finished_spans
166
+ private :set_current_span
167
+ private :attach_sampling_priority
120
168
  end
121
169
 
122
170
  # ThreadLocalContext can be used as a tracer global reference to create