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
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