ddtrace 0.8.2 → 0.9.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -1
  3. data/.gitignore +1 -0
  4. data/Appraisals +10 -0
  5. data/Rakefile +27 -1
  6. data/ddtrace.gemspec +2 -2
  7. data/docker-compose.yml +10 -0
  8. data/docs/GettingStarted.md +119 -0
  9. data/gemfiles/contrib.gemfile +5 -0
  10. data/gemfiles/contrib_old.gemfile +4 -0
  11. data/lib/ddtrace.rb +4 -11
  12. data/lib/ddtrace/buffer.rb +14 -0
  13. data/lib/ddtrace/contrib/aws/instrumentation.rb +43 -0
  14. data/lib/ddtrace/contrib/aws/parsed_context.rb +56 -0
  15. data/lib/ddtrace/contrib/aws/patcher.rb +56 -0
  16. data/lib/ddtrace/contrib/aws/services.rb +115 -0
  17. data/lib/ddtrace/contrib/dalli/instrumentation.rb +35 -0
  18. data/lib/ddtrace/contrib/dalli/patcher.rb +50 -0
  19. data/lib/ddtrace/contrib/dalli/quantize.rb +17 -0
  20. data/lib/ddtrace/contrib/faraday/middleware.rb +75 -0
  21. data/lib/ddtrace/contrib/faraday/patcher.rb +52 -0
  22. data/lib/ddtrace/contrib/mongodb/parsers.rb +57 -0
  23. data/lib/ddtrace/contrib/mongodb/patcher.rb +93 -0
  24. data/lib/ddtrace/contrib/mongodb/subscribers.rb +71 -0
  25. data/lib/ddtrace/contrib/rails/action_controller.rb +18 -19
  26. data/lib/ddtrace/contrib/rails/action_view.rb +51 -61
  27. data/lib/ddtrace/contrib/rails/active_support.rb +29 -73
  28. data/lib/ddtrace/contrib/rails/core_extensions.rb +191 -53
  29. data/lib/ddtrace/contrib/redis/quantize.rb +4 -6
  30. data/lib/ddtrace/contrib/resque/patcher.rb +38 -0
  31. data/lib/ddtrace/contrib/resque/resque_job.rb +31 -0
  32. data/lib/ddtrace/contrib/sucker_punch/exception_handler.rb +26 -0
  33. data/lib/ddtrace/contrib/sucker_punch/instrumentation.rb +60 -0
  34. data/lib/ddtrace/contrib/sucker_punch/patcher.rb +50 -0
  35. data/lib/ddtrace/ext/http.rb +1 -0
  36. data/lib/ddtrace/ext/mongo.rb +12 -0
  37. data/lib/ddtrace/monkey.rb +18 -0
  38. data/lib/ddtrace/pipeline.rb +46 -0
  39. data/lib/ddtrace/pipeline/span_filter.rb +38 -0
  40. data/lib/ddtrace/pipeline/span_processor.rb +20 -0
  41. data/lib/ddtrace/tracer.rb +18 -0
  42. data/lib/ddtrace/utils.rb +23 -3
  43. data/lib/ddtrace/version.rb +2 -2
  44. data/lib/ddtrace/workers.rb +30 -22
  45. data/lib/ddtrace/writer.rb +5 -7
  46. metadata +30 -9
@@ -0,0 +1,50 @@
1
+ module Datadog
2
+ module Contrib
3
+ module SuckerPunch
4
+ SERVICE = 'sucker_punch'.freeze
5
+ COMPATIBLE_WITH = Gem::Version.new('2.0.0')
6
+
7
+ # Responsible for hooking the instrumentation into `sucker_punch`
8
+ module Patcher
9
+ @patched = false
10
+
11
+ module_function
12
+
13
+ def patch
14
+ return @patched if patched? || !compatible?
15
+
16
+ require 'ddtrace/ext/app_types'
17
+ require_relative 'exception_handler'
18
+ require_relative 'instrumentation'
19
+
20
+ add_pin!
21
+ ExceptionHandler.patch!
22
+ Instrumentation.patch!
23
+
24
+ @patched = true
25
+ rescue => e
26
+ Datadog::Tracer.log.error("Unable to apply SuckerPunch integration: #{e}")
27
+ @patched
28
+ end
29
+
30
+ def patched?
31
+ @patched
32
+ end
33
+
34
+ def compatible?
35
+ return unless defined?(::SuckerPunch::VERSION)
36
+
37
+ Gem::Version.new(::SuckerPunch::VERSION) >= COMPATIBLE_WITH
38
+ end
39
+
40
+ def add_pin!
41
+ Pin.new(SERVICE, app_type: Ext::AppTypes::WORKER).tap do |pin|
42
+ pin.onto(::SuckerPunch)
43
+ end
44
+ end
45
+
46
+ private_class_method :compatible?, :add_pin!
47
+ end
48
+ end
49
+ end
50
+ end
@@ -6,6 +6,7 @@ module Datadog
6
6
  URL = 'http.url'.freeze
7
7
  METHOD = 'http.method'.freeze
8
8
  STATUS_CODE = 'http.status_code'.freeze
9
+ ERROR_RANGE = 500...600
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,12 @@
1
+ module Datadog
2
+ module Ext
3
+ module Mongo
4
+ DB = 'mongodb.db'.freeze
5
+ TYPE = 'mongodb'.freeze
6
+ ROWS = 'mongodb.rows'.freeze
7
+ QUERY = 'mongodb.query'.freeze
8
+ OPERATION = 'mongodb.operation'.freeze
9
+ COLLECTION = 'mongodb.collection'.freeze
10
+ end
11
+ end
12
+ end
@@ -5,9 +5,15 @@ require 'thread'
5
5
  # patching code, which is required on demand, when patching.
6
6
  require 'ddtrace/contrib/active_record/patcher'
7
7
  require 'ddtrace/contrib/elasticsearch/patcher'
8
+ require 'ddtrace/contrib/faraday/patcher'
8
9
  require 'ddtrace/contrib/grape/patcher'
9
10
  require 'ddtrace/contrib/redis/patcher'
10
11
  require 'ddtrace/contrib/http/patcher'
12
+ require 'ddtrace/contrib/aws/patcher'
13
+ require 'ddtrace/contrib/sucker_punch/patcher'
14
+ require 'ddtrace/contrib/mongodb/patcher'
15
+ require 'ddtrace/contrib/dalli/patcher'
16
+ require 'ddtrace/contrib/resque/patcher'
11
17
 
12
18
  module Datadog
13
19
  # Monkey is used for monkey-patching 3rd party libs.
@@ -18,6 +24,12 @@ module Datadog
18
24
  http: true,
19
25
  redis: true,
20
26
  grape: true,
27
+ faraday: true,
28
+ aws: true,
29
+ sucker_punch: true,
30
+ mongo: true,
31
+ dalli: true,
32
+ resque: true,
21
33
  active_record: false
22
34
  }
23
35
  # Patchers should expose 2 methods:
@@ -29,6 +41,12 @@ module Datadog
29
41
  http: Datadog::Contrib::HTTP::Patcher,
30
42
  redis: Datadog::Contrib::Redis::Patcher,
31
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,
32
50
  active_record: Datadog::Contrib::ActiveRecord::Patcher }
33
51
  @mutex = Mutex.new
34
52
 
@@ -0,0 +1,46 @@
1
+ module Datadog
2
+ # Pipeline
3
+ module Pipeline
4
+ require_relative 'pipeline/span_filter'
5
+ require_relative 'pipeline/span_processor'
6
+
7
+ @mutex = Mutex.new
8
+ @processors = []
9
+
10
+ def self.before_flush(*processors, &processor_block)
11
+ processors = [processor_block] if processors.empty?
12
+
13
+ @mutex.synchronize do
14
+ @processors.concat(processors)
15
+ end
16
+ end
17
+
18
+ def self.process!(traces)
19
+ @mutex.synchronize do
20
+ traces
21
+ .map(&method(:apply_processors!))
22
+ .select(&:any?)
23
+ end
24
+ end
25
+
26
+ def self.processors=(value)
27
+ @processors = value
28
+ end
29
+
30
+ def self.apply_processors!(trace)
31
+ result = @processors.inject(trace) do |current_trace, processor|
32
+ processor.call(current_trace)
33
+ end
34
+
35
+ result || []
36
+ rescue => e
37
+ Datadog::Tracer.log.debug(
38
+ "trace dropped entirely due to `Pipeline.before_flush` error: #{e}"
39
+ )
40
+
41
+ []
42
+ end
43
+
44
+ private_class_method :apply_processors!
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ module Datadog
2
+ module Pipeline
3
+ # SpanFilter implements a processor that filters entire span subtrees
4
+ class SpanFilter
5
+ def initialize(filter = nil, &block)
6
+ callable = filter || block
7
+
8
+ raise(ArgumentError) unless callable.respond_to?(:call)
9
+
10
+ @criteria = filter || block
11
+ end
12
+
13
+ def call(trace)
14
+ black_list = trace.select(&method(:drop_it?))
15
+
16
+ clean_trace(black_list, trace) while black_list.any?
17
+
18
+ trace
19
+ end
20
+
21
+ private
22
+
23
+ def drop_it?(span)
24
+ @criteria.call(span) rescue false
25
+ end
26
+
27
+ def clean_trace(black_list, trace)
28
+ current = black_list.shift
29
+
30
+ trace.delete(current)
31
+
32
+ trace.each do |span|
33
+ black_list << span if span.parent == current
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ module Datadog
2
+ module Pipeline
3
+ # SpanProcessor
4
+ class SpanProcessor
5
+ def initialize(operation = nil, &block)
6
+ callable = operation || block
7
+
8
+ raise(ArgumentError) unless callable.respond_to?(:call)
9
+
10
+ @operation = operation || block
11
+ end
12
+
13
+ def call(trace)
14
+ trace.each do |span|
15
+ @operation.call(span) rescue next
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -57,6 +57,23 @@ module Datadog
57
57
  log.level == Logger::DEBUG
58
58
  end
59
59
 
60
+ # Shorthand that calls the `shutdown!` method of a registered worker.
61
+ # It's useful to ensure that the Trace Buffer is properly flushed before
62
+ # shutting down the application.
63
+ #
64
+ # For instance:
65
+ #
66
+ # tracer.trace('operation_name', service='rake_tasks') do |span|
67
+ # span.set_tag('task.name', 'script')
68
+ # end
69
+ #
70
+ # tracer.shutdown!
71
+ #
72
+ def shutdown!
73
+ return if !@enabled || @writer.worker.nil?
74
+ @writer.worker.shutdown!
75
+ end
76
+
60
77
  # Return the current active \Context for this traced execution. This method is
61
78
  # automatically called when calling Tracer.trace or Tracer.start_span,
62
79
  # but it can be used in the application code during manual instrumentation.
@@ -303,6 +320,7 @@ module Datadog
303
320
  end
304
321
 
305
322
  @writer.write(trace, @services)
323
+ @services = {}
306
324
  end
307
325
 
308
326
  private :write, :guess_context_and_parent
@@ -1,16 +1,36 @@
1
- require 'thread'
2
-
3
1
  module Datadog
4
2
  # Utils contains low-level utilities, typically to provide pseudo-random trace IDs.
5
3
  module Utils
6
4
  # We use a custom random number generator because we want no interference
7
5
  # with the default one. Using the default prng, we could break code that
8
6
  # would rely on srand/rand sequences.
9
- @rnd = Random.new
10
7
 
11
8
  # Return a span id
12
9
  def self.next_id
10
+ reset! if was_forked?
11
+
13
12
  @rnd.rand(Datadog::Span::MAX_ID)
14
13
  end
14
+
15
+ def self.reset!
16
+ @pid = Process.pid
17
+ @rnd = Random.new
18
+ end
19
+
20
+ def self.was_forked?
21
+ Process.pid != @pid
22
+ end
23
+
24
+ def self.truncate(value, size, omission = '...')
25
+ string = value.to_s
26
+
27
+ return string if string.size <= size
28
+
29
+ string.slice(0, size - omission.size) + omission
30
+ end
31
+
32
+ private_class_method :reset!, :was_forked?
33
+
34
+ reset!
15
35
  end
16
36
  end
@@ -1,8 +1,8 @@
1
1
  module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 8
5
- PATCH = 2
4
+ MINOR = 9
5
+ PATCH = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
@@ -9,14 +9,18 @@ module Datadog
9
9
  # will perform a task at regular intervals. The thread can be stopped
10
10
  # with the +stop()+ method and can start with the +start()+ method.
11
11
  class AsyncTransport
12
- def initialize(span_interval, service_interval, transport, buff_size, trace_task, service_task)
12
+ DEFAULT_TIMEOUT = 5
13
+
14
+ attr_reader :trace_buffer, :service_buffer, :shutting_down
15
+
16
+ def initialize(transport, buff_size, trace_task, service_task, interval)
13
17
  @trace_task = trace_task
14
18
  @service_task = service_task
15
- @span_interval = span_interval
16
- @service_interval = service_interval
19
+ @flush_interval = interval
17
20
  @trace_buffer = TraceBuffer.new(buff_size)
18
21
  @service_buffer = TraceBuffer.new(buff_size)
19
22
  @transport = transport
23
+ @shutting_down = false
20
24
 
21
25
  @worker = nil
22
26
  @run = false
@@ -28,6 +32,7 @@ module Datadog
28
32
 
29
33
  begin
30
34
  traces = @trace_buffer.pop()
35
+ traces = Pipeline.process!(traces)
31
36
  @trace_task.call(traces, @transport)
32
37
  rescue StandardError => e
33
38
  # ensures that the thread will not die because of an exception.
@@ -43,16 +48,7 @@ module Datadog
43
48
 
44
49
  begin
45
50
  services = @service_buffer.pop()
46
- # pick up the latest services hash (this is a FIFO list)
47
- # that is different from what we sent before.
48
- different = services.inject(false) { |acc, elem| elem != @last_flushed_services ? elem : acc }
49
- if different
50
- if @service_task.call(different, @transport)
51
- @last_flushed_services = different.clone
52
- end
53
- else
54
- Datadog::Tracer.log.debug('No new different services, skipping flush.')
55
- end
51
+ @service_task.call(services[0], @transport)
56
52
  rescue StandardError => e
57
53
  # ensures that the thread will not die because of an exception.
58
54
  # TODO[manu]: findout the reason and reschedule the send if it's not
@@ -67,17 +63,11 @@ module Datadog
67
63
  @run = true
68
64
  @worker = Thread.new() do
69
65
  Datadog::Tracer.log.debug("Starting thread in the process: #{Process.pid}")
70
- @last_flushed_services = nil
71
- next_send_services = Time.now
72
66
 
73
- # this loop assumes spans are flushed more often than services
74
67
  while @run
75
68
  callback_traces
76
- if Time.now >= next_send_services
77
- next_send_services = Time.now + @service_interval
78
- callback_services
79
- end
80
- sleep(@span_interval)
69
+ callback_services
70
+ sleep(@flush_interval) if @run
81
71
  end
82
72
  end
83
73
  end
@@ -87,9 +77,27 @@ module Datadog
87
77
  @run = false
88
78
  end
89
79
 
80
+ # Closes all available queues and waits for the trace and service buffer to flush
81
+ def shutdown!
82
+ return false if @shutting_down
83
+ @shutting_down = true
84
+ @trace_buffer.close
85
+ @service_buffer.close
86
+ sleep(0.1)
87
+ timeout_time = Time.now + DEFAULT_TIMEOUT
88
+ while (!@trace_buffer.empty? || !@service_buffer.empty?) && Time.now <= timeout_time
89
+ sleep(0.05)
90
+ Datadog::Tracer.log.debug('Waiting for the buffers to clear before exiting')
91
+ end
92
+ stop
93
+ join
94
+ @shutting_down = false
95
+ true
96
+ end
97
+
90
98
  # Block until executor shutdown is complete or until timeout seconds have passed.
91
99
  def join
92
- @worker.join(10)
100
+ @worker.join(5)
93
101
  end
94
102
 
95
103
  # Enqueue an item in the trace internal buffer. This operation is thread-safe
@@ -5,7 +5,7 @@ require 'ddtrace/workers'
5
5
  module Datadog
6
6
  # Traces and services writer that periodically sends data to the trace-agent
7
7
  class Writer
8
- attr_reader :transport
8
+ attr_reader :transport, :worker
9
9
 
10
10
  HOSTNAME = 'localhost'.freeze
11
11
  PORT = '8126'.freeze
@@ -13,8 +13,7 @@ module Datadog
13
13
  def initialize(options = {})
14
14
  # writer and transport parameters
15
15
  @buff_size = options.fetch(:buffer_size, 100)
16
- @span_interval = options.fetch(:spans_interval, 1)
17
- @service_interval = options.fetch(:services_interval, 120)
16
+ @flush_interval = options.fetch(:flush_interval, 1)
18
17
 
19
18
  # transport and buffers
20
19
  @transport = options.fetch(:transport, Datadog::HTTPTransport.new(HOSTNAME, PORT))
@@ -36,12 +35,11 @@ module Datadog
36
35
  def start
37
36
  @trace_handler = ->(items, transport) { send_spans(items, transport) }
38
37
  @service_handler = ->(items, transport) { send_services(items, transport) }
39
- @worker = Datadog::Workers::AsyncTransport.new(@span_interval,
40
- @service_interval,
41
- @transport,
38
+ @worker = Datadog::Workers::AsyncTransport.new(@transport,
42
39
  @buff_size,
43
40
  @trace_handler,
44
- @service_handler)
41
+ @service_handler,
42
+ @flush_interval)
45
43
 
46
44
  @worker.start()
47
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-08 00:00:00.000000000 Z
11
+ date: 2017-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: rubocop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: '0.47'
47
+ version: 0.49.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: '0.47'
54
+ version: 0.49.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '2.1'
75
+ version: '2.2'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '2.1'
82
+ version: '2.2'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: yard
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -137,11 +137,23 @@ files:
137
137
  - lib/ddtrace/buffer.rb
138
138
  - lib/ddtrace/context.rb
139
139
  - lib/ddtrace/contrib/active_record/patcher.rb
140
+ - lib/ddtrace/contrib/aws/instrumentation.rb
141
+ - lib/ddtrace/contrib/aws/parsed_context.rb
142
+ - lib/ddtrace/contrib/aws/patcher.rb
143
+ - lib/ddtrace/contrib/aws/services.rb
144
+ - lib/ddtrace/contrib/dalli/instrumentation.rb
145
+ - lib/ddtrace/contrib/dalli/patcher.rb
146
+ - lib/ddtrace/contrib/dalli/quantize.rb
140
147
  - lib/ddtrace/contrib/elasticsearch/patcher.rb
141
148
  - lib/ddtrace/contrib/elasticsearch/quantize.rb
149
+ - lib/ddtrace/contrib/faraday/middleware.rb
150
+ - lib/ddtrace/contrib/faraday/patcher.rb
142
151
  - lib/ddtrace/contrib/grape/endpoint.rb
143
152
  - lib/ddtrace/contrib/grape/patcher.rb
144
153
  - lib/ddtrace/contrib/http/patcher.rb
154
+ - lib/ddtrace/contrib/mongodb/parsers.rb
155
+ - lib/ddtrace/contrib/mongodb/patcher.rb
156
+ - lib/ddtrace/contrib/mongodb/subscribers.rb
145
157
  - lib/ddtrace/contrib/rack/middlewares.rb
146
158
  - lib/ddtrace/contrib/rails/action_controller.rb
147
159
  - lib/ddtrace/contrib/rails/action_view.rb
@@ -154,8 +166,13 @@ files:
154
166
  - lib/ddtrace/contrib/redis/patcher.rb
155
167
  - lib/ddtrace/contrib/redis/quantize.rb
156
168
  - lib/ddtrace/contrib/redis/tags.rb
169
+ - lib/ddtrace/contrib/resque/patcher.rb
170
+ - lib/ddtrace/contrib/resque/resque_job.rb
157
171
  - lib/ddtrace/contrib/sidekiq/tracer.rb
158
172
  - lib/ddtrace/contrib/sinatra/tracer.rb
173
+ - lib/ddtrace/contrib/sucker_punch/exception_handler.rb
174
+ - lib/ddtrace/contrib/sucker_punch/instrumentation.rb
175
+ - lib/ddtrace/contrib/sucker_punch/patcher.rb
159
176
  - lib/ddtrace/distributed.rb
160
177
  - lib/ddtrace/encoding.rb
161
178
  - lib/ddtrace/error.rb
@@ -164,12 +181,16 @@ files:
164
181
  - lib/ddtrace/ext/distributed.rb
165
182
  - lib/ddtrace/ext/errors.rb
166
183
  - lib/ddtrace/ext/http.rb
184
+ - lib/ddtrace/ext/mongo.rb
167
185
  - lib/ddtrace/ext/net.rb
168
186
  - lib/ddtrace/ext/redis.rb
169
187
  - lib/ddtrace/ext/sql.rb
170
188
  - lib/ddtrace/logger.rb
171
189
  - lib/ddtrace/monkey.rb
172
190
  - lib/ddtrace/pin.rb
191
+ - lib/ddtrace/pipeline.rb
192
+ - lib/ddtrace/pipeline/span_filter.rb
193
+ - lib/ddtrace/pipeline/span_processor.rb
173
194
  - lib/ddtrace/provider.rb
174
195
  - lib/ddtrace/sampler.rb
175
196
  - lib/ddtrace/span.rb
@@ -200,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
221
  version: '0'
201
222
  requirements: []
202
223
  rubyforge_project:
203
- rubygems_version: 2.4.5.2
224
+ rubygems_version: 2.6.9
204
225
  signing_key:
205
226
  specification_version: 4
206
227
  summary: Datadog tracing code for your Ruby applications