ddtrace 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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