ddtrace 0.7.2 → 0.8.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.
- checksums.yaml +5 -13
- data/Appraisals +38 -19
- data/README.md +13 -0
- data/Rakefile +9 -9
- data/circle.yml +14 -11
- data/ddtrace.gemspec +1 -0
- data/docs/GettingStarted.md +96 -6
- data/gemfiles/rails30_postgres.gemfile +10 -0
- data/gemfiles/rails30_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/{rails3_mysql2.gemfile → rails32_mysql2.gemfile} +0 -0
- data/gemfiles/{rails3_postgres.gemfile → rails32_postgres.gemfile} +0 -0
- data/gemfiles/{rails3_postgres_redis.gemfile → rails32_postgres_redis.gemfile} +0 -0
- data/gemfiles/{rails3_postgres_sidekiq.gemfile → rails32_postgres_sidekiq.gemfile} +1 -1
- data/lib/ddtrace/buffer.rb +1 -26
- data/lib/ddtrace/context.rb +145 -0
- data/lib/ddtrace/contrib/active_record/patcher.rb +2 -2
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +1 -0
- data/lib/ddtrace/contrib/grape/endpoint.rb +4 -4
- data/lib/ddtrace/contrib/http/patcher.rb +36 -9
- data/lib/ddtrace/contrib/rack/middlewares.rb +53 -12
- data/lib/ddtrace/contrib/rails/action_controller.rb +14 -5
- data/lib/ddtrace/contrib/rails/action_view.rb +3 -3
- data/lib/ddtrace/contrib/rails/active_record.rb +1 -1
- data/lib/ddtrace/contrib/rails/active_support.rb +2 -2
- data/lib/ddtrace/contrib/rails/core_extensions.rb +45 -20
- data/lib/ddtrace/contrib/rails/framework.rb +11 -0
- data/lib/ddtrace/distributed.rb +38 -0
- data/lib/ddtrace/ext/distributed.rb +10 -0
- data/lib/ddtrace/pin.rb +6 -4
- data/lib/ddtrace/provider.rb +16 -0
- data/lib/ddtrace/span.rb +54 -22
- data/lib/ddtrace/tracer.rb +120 -64
- data/lib/ddtrace/utils.rb +9 -2
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace/workers.rb +1 -2
- data/lib/ddtrace/writer.rb +0 -1
- metadata +43 -27
@@ -46,7 +46,7 @@ module Datadog
|
|
46
46
|
span.set_tag('rails.db.vendor', adapter_name)
|
47
47
|
span.set_tag('rails.db.cached', cached) if cached
|
48
48
|
span.start_time = start
|
49
|
-
span.
|
49
|
+
span.finish(finish)
|
50
50
|
rescue StandardError => e
|
51
51
|
Datadog::Tracer.log.error(e.message)
|
52
52
|
end
|
@@ -8,7 +8,7 @@ module Datadog
|
|
8
8
|
module ActiveSupport
|
9
9
|
def self.instrument
|
10
10
|
# patch Rails core components
|
11
|
-
Datadog::
|
11
|
+
Datadog::RailsCachePatcher.patch_cache_store()
|
12
12
|
|
13
13
|
# subscribe when a cache read starts being processed
|
14
14
|
::ActiveSupport::Notifications.subscribe('start_cache_read.active_support') do |*args|
|
@@ -106,7 +106,7 @@ module Datadog
|
|
106
106
|
end
|
107
107
|
ensure
|
108
108
|
span.start_time = start
|
109
|
-
span.
|
109
|
+
span.finish(finish)
|
110
110
|
end
|
111
111
|
rescue StandardError => e
|
112
112
|
Datadog::Tracer.log.error(e.message)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Datadog
|
2
|
-
#
|
3
|
-
module
|
2
|
+
# RailsRendererPatcher contains function to patch Rails rendering libraries.
|
3
|
+
module RailsRendererPatcher
|
4
4
|
module_function
|
5
5
|
|
6
6
|
def patch_renderer
|
@@ -8,33 +8,58 @@ module Datadog
|
|
8
8
|
patch_renderer_render_partial
|
9
9
|
end
|
10
10
|
|
11
|
-
def patch_cache_store
|
12
|
-
patch_cache_store_read
|
13
|
-
patch_cache_store_fetch
|
14
|
-
patch_cache_store_write
|
15
|
-
patch_cache_store_delete
|
16
|
-
patch_cache_store_instrument
|
17
|
-
end
|
18
|
-
|
19
11
|
def patch_renderer_render_template
|
20
|
-
::ActionView::Renderer
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
12
|
+
if defined?(::ActionView::Renderer)
|
13
|
+
::ActionView::Renderer.class_eval do
|
14
|
+
alias_method :render_template_without_datadog, :render_template
|
15
|
+
def render_template(*args, &block)
|
16
|
+
ActiveSupport::Notifications.instrument('start_render_template.action_view')
|
17
|
+
render_template_without_datadog(*args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
else # Rails < 3.1
|
21
|
+
::ActionView::Template.class_eval do
|
22
|
+
alias_method :render_template_without_datadog, :render
|
23
|
+
def render(*args, &block)
|
24
|
+
ActiveSupport::Notifications.instrument('start_render_template.action_view')
|
25
|
+
render_template_without_datadog(*args, &block)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
31
|
def patch_renderer_render_partial
|
30
|
-
::ActionView::PartialRenderer
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
if defined?(::ActionView::PartialRenderer)
|
33
|
+
::ActionView::PartialRenderer.class_eval do
|
34
|
+
alias_method :render_partial_without_datadog, :render_partial
|
35
|
+
def render_partial(*args, &block)
|
36
|
+
ActiveSupport::Notifications.instrument('start_render_partial.action_view')
|
37
|
+
render_partial_without_datadog(*args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else # Rails < 3.1
|
41
|
+
::ActionView::Partials::PartialRenderer.class_eval do
|
42
|
+
alias_method :render_partial_without_datadog, :render
|
43
|
+
def render(*args, &block)
|
44
|
+
ActiveSupport::Notifications.instrument('start_render_partial.action_view')
|
45
|
+
render_partial_without_datadog(*args, &block)
|
46
|
+
end
|
35
47
|
end
|
36
48
|
end
|
37
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# RailsCachePatcher contains function to patch Rails caching libraries.
|
53
|
+
module RailsCachePatcher
|
54
|
+
module_function
|
55
|
+
|
56
|
+
def patch_cache_store
|
57
|
+
patch_cache_store_read
|
58
|
+
patch_cache_store_fetch
|
59
|
+
patch_cache_store_write
|
60
|
+
patch_cache_store_delete
|
61
|
+
patch_cache_store_instrument
|
62
|
+
end
|
38
63
|
|
39
64
|
def cache_store_class(k)
|
40
65
|
# When Redis is used, we can't only patch Cache::Store as it is
|
@@ -11,6 +11,17 @@ require 'ddtrace/contrib/rails/active_record'
|
|
11
11
|
require 'ddtrace/contrib/rails/active_support'
|
12
12
|
require 'ddtrace/contrib/rails/utils'
|
13
13
|
|
14
|
+
# Rails < 3.1
|
15
|
+
unless defined?(ActiveRecord::Base.connection_config)
|
16
|
+
ActiveRecord::Base.class_eval do
|
17
|
+
class << self
|
18
|
+
def connection_config
|
19
|
+
connection_pool.spec.config
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
14
25
|
module Datadog
|
15
26
|
module Contrib
|
16
27
|
# TODO[manu]: write docs
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'ddtrace/span'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
# Common code related to distributed tracing.
|
5
|
+
module Distributed
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Parses a trace_id and a parent_id, typically sent as headers in
|
9
|
+
# a distributed tracing context, and returns a couple of trace_id,parent_id
|
10
|
+
# which are garanteed to be both non-zero. This does not 100% ensure they
|
11
|
+
# are valid (after all, the caller could mess up data) but at least it
|
12
|
+
# sorts out most common errors, such as syntax, nil values, etc.
|
13
|
+
# Both headers must be set, else nil values are returned, for both.
|
14
|
+
# Reports problem on debug log.
|
15
|
+
def parse_trace_headers(trace_id_header, parent_id_header)
|
16
|
+
return nil, nil if trace_id_header.nil? || parent_id_header.nil?
|
17
|
+
trace_id = trace_id_header.to_i
|
18
|
+
parent_id = parent_id_header.to_i
|
19
|
+
if trace_id.zero?
|
20
|
+
Datadog::Tracer.log.debug("invalid trace_id header: #{trace_id_header}")
|
21
|
+
return nil, nil
|
22
|
+
end
|
23
|
+
if parent_id.zero?
|
24
|
+
Datadog::Tracer.log.debug("invalid parent_id header: #{parent_id_header}")
|
25
|
+
return nil, nil
|
26
|
+
end
|
27
|
+
if trace_id < 0 || trace_id >= Datadog::Span::MAX_ID
|
28
|
+
Datadog::Tracer.log.debug("trace_id out of range: #{trace_id_header}")
|
29
|
+
return nil, nil
|
30
|
+
end
|
31
|
+
if parent_id < 0 || parent_id >= Datadog::Span::MAX_ID
|
32
|
+
Datadog::Tracer.log.debug("parent_id out of range: #{parent_id_header}")
|
33
|
+
return nil, nil
|
34
|
+
end
|
35
|
+
[trace_id, parent_id]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Ext
|
3
|
+
module DistributedTracing
|
4
|
+
# HTTP headers one should set for distributed tracing.
|
5
|
+
# These are cross-language (eg: Python, Go and other implementations should honor these)
|
6
|
+
HTTP_HEADER_TRACE_ID = 'x-datadog-trace-id'.freeze
|
7
|
+
HTTP_HEADER_PARENT_ID = 'x-datadog-parent-id'.freeze
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/ddtrace/pin.rb
CHANGED
@@ -16,15 +16,17 @@ module Datadog
|
|
16
16
|
attr_accessor :app_type
|
17
17
|
attr_accessor :name
|
18
18
|
attr_accessor :tracer
|
19
|
+
attr_accessor :config
|
19
20
|
|
20
21
|
# [ruby19] named parameters would be more idiomatic here, but would break backward compatibility
|
21
|
-
def initialize(service, options = { app: nil, tags: nil, app_type: nil, tracer: nil })
|
22
|
+
def initialize(service, options = { app: nil, tags: nil, app_type: nil, tracer: nil, config: nil })
|
22
23
|
@service = service
|
23
|
-
@app = options
|
24
|
-
@tags = options
|
25
|
-
@app_type = options
|
24
|
+
@app = options.fetch(:app, nil)
|
25
|
+
@tags = options.fetch(:tags, nil)
|
26
|
+
@app_type = options.fetch(:app_type, nil)
|
26
27
|
@name = nil # this would rarely be overriden as it's really span-specific
|
27
28
|
@tracer = options[:tracer] || Datadog.tracer
|
29
|
+
@config = options.fetch(:config, nil)
|
28
30
|
end
|
29
31
|
|
30
32
|
def enabled?
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Datadog
|
2
|
+
# DefaultContextProvider is a default context provider that retrieves
|
3
|
+
# all contexts from the current thread-local storage. It is suitable for
|
4
|
+
# synchronous programming.
|
5
|
+
class DefaultContextProvider
|
6
|
+
# Initializes the default context provider with a thread-bound context.
|
7
|
+
def initialize
|
8
|
+
@context = Datadog::ThreadLocalContext.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return the current context.
|
12
|
+
def context
|
13
|
+
@context.local
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/ddtrace/span.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'time'
|
2
|
+
require 'thread'
|
2
3
|
|
3
4
|
require 'ddtrace/utils'
|
4
5
|
require 'ddtrace/ext/errors'
|
@@ -10,23 +11,30 @@ module Datadog
|
|
10
11
|
# within a larger operation. Spans can be nested within each other, and in those instances
|
11
12
|
# will have a parent-child relationship.
|
12
13
|
class Span
|
13
|
-
# The max value for a \Span identifier
|
14
|
-
|
14
|
+
# The max value for a \Span identifier.
|
15
|
+
# Span and trace identifiers should be strictly positive and strictly inferior to this limit.
|
16
|
+
#
|
17
|
+
# Limited to 63-bit positive integers, as some other languages might be limited to this,
|
18
|
+
# and IDs need to be easy to port across various languages and platforms.
|
19
|
+
MAX_ID = 2**63
|
15
20
|
|
16
21
|
attr_accessor :name, :service, :resource, :span_type,
|
17
22
|
:start_time, :end_time,
|
18
23
|
:span_id, :trace_id, :parent_id,
|
19
|
-
:status, :
|
24
|
+
:status, :sampled,
|
25
|
+
:tracer, :context
|
26
|
+
|
27
|
+
attr_reader :parent
|
20
28
|
|
21
|
-
# Create a new span linked to the given tracer. Call the <tt>
|
22
|
-
#
|
23
|
-
# given +time+. Available options are:
|
29
|
+
# Create a new span linked to the given tracer. Call the \Tracer method <tt>start_span()</tt>
|
30
|
+
# and then <tt>finish()</tt> once the tracer operation is over.
|
24
31
|
#
|
25
32
|
# * +service+: the service name for this span
|
26
33
|
# * +resource+: the resource this span refers, or +name+ if it's missing
|
27
34
|
# * +span_type+: the type of the span (such as +http+, +db+ and so on)
|
28
35
|
# * +parent_id+: the identifier of the parent span
|
29
36
|
# * +trace_id+: the identifier of the root span for this trace
|
37
|
+
# * +context+: the context of the span
|
30
38
|
def initialize(tracer, name, options = {})
|
31
39
|
@tracer = tracer
|
32
40
|
|
@@ -35,9 +43,11 @@ module Datadog
|
|
35
43
|
@resource = options.fetch(:resource, name)
|
36
44
|
@span_type = options.fetch(:span_type, nil)
|
37
45
|
|
38
|
-
@span_id = Datadog::Utils.next_id
|
46
|
+
@span_id = Datadog::Utils.next_id
|
39
47
|
@parent_id = options.fetch(:parent_id, 0)
|
40
|
-
@trace_id = options.fetch(:trace_id,
|
48
|
+
@trace_id = options.fetch(:trace_id, Datadog::Utils.next_id)
|
49
|
+
|
50
|
+
@context = options.fetch(:context, nil)
|
41
51
|
|
42
52
|
@meta = {}
|
43
53
|
@metrics = {}
|
@@ -46,8 +56,8 @@ module Datadog
|
|
46
56
|
@parent = nil
|
47
57
|
@sampled = true
|
48
58
|
|
49
|
-
@start_time =
|
50
|
-
@end_time = nil
|
59
|
+
@start_time = nil # set by Tracer.start_span
|
60
|
+
@end_time = nil # set by Span.finish
|
51
61
|
end
|
52
62
|
|
53
63
|
# Set the given key / value tag pair on the span. Keys and values
|
@@ -65,8 +75,8 @@ module Datadog
|
|
65
75
|
@meta[key]
|
66
76
|
end
|
67
77
|
|
68
|
-
#
|
69
|
-
#
|
78
|
+
# This method sets a tag with a floating point value for the given key. It acts
|
79
|
+
# like `set_tag()` and it simply add a tag without further processing.
|
70
80
|
def set_metric(key, value)
|
71
81
|
# enforce that the value is a floating point number
|
72
82
|
value = Float(value)
|
@@ -91,18 +101,34 @@ module Datadog
|
|
91
101
|
|
92
102
|
# Mark the span finished at the current time and submit it.
|
93
103
|
def finish(finish_time = nil)
|
104
|
+
# A span should not be finished twice. Note that this is not thread-safe,
|
105
|
+
# finish is called from multiple threads, a given span might be finished
|
106
|
+
# several times. Again, one should not do this, so this test is more a
|
107
|
+
# fallback to avoid very bad things and protect you in most common cases.
|
94
108
|
return if finished?
|
95
109
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
110
|
+
# Provide a default start_time if unset, but this should have been set by start_span.
|
111
|
+
# Using now here causes 0-duration spans, still, this is expected, as we never
|
112
|
+
# explicitely say when it started.
|
113
|
+
@start_time ||= Time.now.utc
|
114
|
+
|
115
|
+
@end_time = finish_time.nil? ? Time.now.utc : finish_time # finish this
|
116
|
+
|
117
|
+
# Finish does not really do anything if the span is not bound to a tracer and a context.
|
118
|
+
return self if @tracer.nil? || @context.nil?
|
100
119
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
120
|
+
# spans without a service would be dropped, so here we provide a default.
|
121
|
+
# This should really never happen with integrations in contrib, as a default
|
122
|
+
# service is always set. It's only for custom instrumentation.
|
123
|
+
@service ||= @tracer.default_service unless @tracer.nil?
|
124
|
+
|
125
|
+
begin
|
126
|
+
@context.close_span(self)
|
127
|
+
@tracer.record(self)
|
128
|
+
rescue StandardError => e
|
129
|
+
Datadog::Tracer.log.debug("error recording finished trace: #{e}")
|
130
|
+
end
|
131
|
+
self
|
106
132
|
end
|
107
133
|
|
108
134
|
# Return whether the span is finished or not.
|
@@ -115,9 +141,14 @@ module Datadog
|
|
115
141
|
"Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})"
|
116
142
|
end
|
117
143
|
|
144
|
+
# DEPRECATED: remove this function in the next release, replaced by ``parent=``
|
145
|
+
def set_parent(parent)
|
146
|
+
self.parent = parent
|
147
|
+
end
|
148
|
+
|
118
149
|
# Set this span's parent, inheriting any properties not explicitly set.
|
119
150
|
# If the parent is nil, set the span zero values.
|
120
|
-
def
|
151
|
+
def parent=(parent)
|
121
152
|
@parent = parent
|
122
153
|
|
123
154
|
if parent.nil?
|
@@ -127,6 +158,7 @@ module Datadog
|
|
127
158
|
@trace_id = parent.trace_id
|
128
159
|
@parent_id = parent.span_id
|
129
160
|
@service ||= parent.service
|
161
|
+
@sampled = parent.sampled
|
130
162
|
end
|
131
163
|
end
|
132
164
|
|
data/lib/ddtrace/tracer.rb
CHANGED
@@ -4,7 +4,8 @@ require 'logger'
|
|
4
4
|
require 'pathname'
|
5
5
|
|
6
6
|
require 'ddtrace/span'
|
7
|
-
require 'ddtrace/
|
7
|
+
require 'ddtrace/context'
|
8
|
+
require 'ddtrace/provider'
|
8
9
|
require 'ddtrace/logger'
|
9
10
|
require 'ddtrace/writer'
|
10
11
|
require 'ddtrace/sampler'
|
@@ -15,6 +16,7 @@ module Datadog
|
|
15
16
|
# example, a trace can be used to track the entire time spent processing a complicated web request.
|
16
17
|
# Even though the request may require multiple resources and machines to handle the request, all
|
17
18
|
# of these function calls and sub-requests would be encapsulated within a single trace.
|
19
|
+
# rubocop:disable Metrics/ClassLength
|
18
20
|
class Tracer
|
19
21
|
attr_reader :writer, :sampler, :services, :tags
|
20
22
|
attr_accessor :enabled
|
@@ -55,6 +57,16 @@ module Datadog
|
|
55
57
|
log.level == Logger::DEBUG
|
56
58
|
end
|
57
59
|
|
60
|
+
# Return the current active \Context for this traced execution. This method is
|
61
|
+
# automatically called when calling Tracer.trace or Tracer.start_span,
|
62
|
+
# but it can be used in the application code during manual instrumentation.
|
63
|
+
#
|
64
|
+
# This method makes use of a \ContextProvider that is automatically set during the tracer
|
65
|
+
# initialization, or while using a library instrumentation.
|
66
|
+
def call_context
|
67
|
+
@provider.context
|
68
|
+
end
|
69
|
+
|
58
70
|
# Initialize a new \Tracer used to create, sample and submit spans that measure the
|
59
71
|
# time of sections of code. Available +options+ are:
|
60
72
|
#
|
@@ -65,10 +77,10 @@ module Datadog
|
|
65
77
|
@writer = options.fetch(:writer, Datadog::Writer.new)
|
66
78
|
@sampler = options.fetch(:sampler, Datadog::AllSampler.new)
|
67
79
|
|
68
|
-
@
|
80
|
+
@provider = options.fetch(:context_provider, Datadog::DefaultContextProvider.new)
|
81
|
+
@provider ||= Datadog::DefaultContextProvider.new # @provider should never be nil
|
69
82
|
|
70
83
|
@mutex = Mutex.new
|
71
|
-
@spans = []
|
72
84
|
@services = {}
|
73
85
|
@tags = {}
|
74
86
|
end
|
@@ -113,10 +125,10 @@ module Datadog
|
|
113
125
|
# for non-root spans which have a parent. However, root spans without
|
114
126
|
# a service would be invalid and rejected.
|
115
127
|
def default_service
|
116
|
-
return @default_service if @default_service
|
128
|
+
return @default_service if instance_variable_defined?(:@default_service) && @default_service
|
117
129
|
begin
|
118
130
|
@default_service = File.basename($PROGRAM_NAME, '.*')
|
119
|
-
rescue => e
|
131
|
+
rescue StandardError => e
|
120
132
|
Datadog::Tracer.log.error("unable to guess default service: #{e}")
|
121
133
|
@default_service = 'ruby'.freeze
|
122
134
|
end
|
@@ -132,6 +144,69 @@ module Datadog
|
|
132
144
|
@tags.update(tags)
|
133
145
|
end
|
134
146
|
|
147
|
+
# Guess context and parent from child_of entry.
|
148
|
+
def guess_context_and_parent(options = {})
|
149
|
+
child_of = options.fetch(:child_of, nil) # can be context or span
|
150
|
+
|
151
|
+
ctx = nil
|
152
|
+
parent = nil
|
153
|
+
unless child_of.nil?
|
154
|
+
if child_of.respond_to?(:current_span)
|
155
|
+
ctx = child_of
|
156
|
+
parent = child_of.current_span
|
157
|
+
elsif child_of.is_a?(Datadog::Span)
|
158
|
+
parent = child_of
|
159
|
+
ctx = child_of.context
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
ctx ||= call_context
|
164
|
+
|
165
|
+
[ctx, parent]
|
166
|
+
end
|
167
|
+
|
168
|
+
# Return a span that will trace an operation called \name. This method allows
|
169
|
+
# parenting passing \child_of as an option. If it's missing, the newly created span is a
|
170
|
+
# root span. Available options are:
|
171
|
+
#
|
172
|
+
# * +service+: the service name for this span
|
173
|
+
# * +resource+: the resource this span refers, or \name if it's missing
|
174
|
+
# * +span_type+: the type of the span (such as \http, \db and so on)
|
175
|
+
# * +child_of+: a \Span or a \Context instance representing the parent for this span.
|
176
|
+
# * +start_time+: when the span actually starts (defaults to \now)
|
177
|
+
# * +tags+: extra tags which should be added to the span.
|
178
|
+
def start_span(name, options = {})
|
179
|
+
start_time = options.fetch(:start_time, Time.now.utc)
|
180
|
+
tags = options.fetch(:tags, {})
|
181
|
+
|
182
|
+
opts = options.select do |k, _v|
|
183
|
+
# Filter options, we want no side effects with unexpected args.
|
184
|
+
# Plus, this documents the code (Ruby 2 named args would be better but we're Ruby 1.9 compatible)
|
185
|
+
[:service, :resource, :span_type].include?(k)
|
186
|
+
end
|
187
|
+
|
188
|
+
ctx, parent = guess_context_and_parent(options)
|
189
|
+
opts[:context] = ctx unless ctx.nil?
|
190
|
+
|
191
|
+
span = Span.new(self, name, opts)
|
192
|
+
if parent.nil?
|
193
|
+
# root span
|
194
|
+
@sampler.sample(span)
|
195
|
+
span.set_tag('system.pid', Process.pid)
|
196
|
+
else
|
197
|
+
# child span
|
198
|
+
span.parent = parent # sets service, trace_id, parent_id, sampled
|
199
|
+
end
|
200
|
+
tags.each { |k, v| span.set_tag(k, v) } unless tags.empty?
|
201
|
+
@tags.each { |k, v| span.set_tag(k, v) } unless @tags.empty?
|
202
|
+
span.start_time = start_time
|
203
|
+
|
204
|
+
# this could at some point be optional (start_active_span vs start_manual_span)
|
205
|
+
ctx.add_span(span) unless ctx.nil?
|
206
|
+
|
207
|
+
span
|
208
|
+
end
|
209
|
+
|
135
210
|
# Return a +span+ that will trace an operation called +name+. You could trace your code
|
136
211
|
# using a <tt>do-block</tt> like:
|
137
212
|
#
|
@@ -160,22 +235,20 @@ module Datadog
|
|
160
235
|
# parent2 = tracer.trace('parent2') # has no parent span
|
161
236
|
# parent2.finish()
|
162
237
|
#
|
238
|
+
# Available options are:
|
239
|
+
#
|
240
|
+
# * +service+: the service name for this span
|
241
|
+
# * +resource+: the resource this span refers, or \name if it's missing
|
242
|
+
# * +span_type+: the type of the span (such as \http, \db and so on)
|
243
|
+
# * +tags+: extra tags which should be added to the span.
|
163
244
|
def trace(name, options = {})
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
span.set_parent(parent)
|
169
|
-
@buffer.set(span)
|
170
|
-
|
171
|
-
@tags.each { |k, v| span.set_tag(k, v) } unless @tags.empty?
|
172
|
-
|
173
|
-
# sampling
|
174
|
-
if parent.nil?
|
175
|
-
@sampler.sample(span)
|
176
|
-
else
|
177
|
-
span.sampled = span.parent.sampled
|
245
|
+
opts = options.select do |k, _v|
|
246
|
+
# Filter options, we want no side effects with unexpected args.
|
247
|
+
# Plus, this documents the code (Ruby 2 named args would be better but we're Ruby 1.9 compatible)
|
248
|
+
[:service, :resource, :span_type, :tags].include?(k)
|
178
249
|
end
|
250
|
+
opts[:child_of] = call_context
|
251
|
+
span = start_span(name, opts)
|
179
252
|
|
180
253
|
# call the finish only if a block is given; this ensures
|
181
254
|
# that a call to tracer.trace() without a block, returns
|
@@ -183,9 +256,15 @@ module Datadog
|
|
183
256
|
if block_given?
|
184
257
|
begin
|
185
258
|
yield(span)
|
186
|
-
|
259
|
+
# rubocop:disable Lint/RescueException
|
260
|
+
# Here we really want to catch *any* exception, not only StandardError,
|
261
|
+
# as we really have no clue of what is in the block,
|
262
|
+
# and it is user code which should be executed no matter what.
|
263
|
+
# It's not a problem since we re-raise it afterwards so for example a
|
264
|
+
# SignalException::Interrupt would still bubble up.
|
265
|
+
rescue Exception => e
|
187
266
|
span.set_error(e)
|
188
|
-
raise
|
267
|
+
raise e
|
189
268
|
ensure
|
190
269
|
span.finish()
|
191
270
|
end
|
@@ -194,61 +273,38 @@ module Datadog
|
|
194
273
|
end
|
195
274
|
end
|
196
275
|
|
197
|
-
# Record the given
|
198
|
-
#
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
# the case when the parent finished after its parent.
|
208
|
-
parent = parent.parent while !parent.nil? && parent.finished?
|
209
|
-
@buffer.set(parent)
|
210
|
-
|
211
|
-
return unless parent.nil?
|
212
|
-
|
213
|
-
# In general, all spans within the buffer belong to the same trace.
|
214
|
-
# But in heavily multithreaded contexts and/or when using lots of callbacks
|
215
|
-
# hooks and other non-linear programming style, one can technically
|
216
|
-
# end up in different situations. So we only extract the spans which
|
217
|
-
# are associated to the root span that just finished, and save the
|
218
|
-
# others for later.
|
219
|
-
trace_spans = []
|
220
|
-
alien_spans = []
|
221
|
-
@spans.each do |s|
|
222
|
-
if s.trace_id == span.trace_id
|
223
|
-
trace_spans << s
|
224
|
-
else
|
225
|
-
alien_spans << s
|
226
|
-
end
|
227
|
-
end
|
228
|
-
spans = trace_spans
|
229
|
-
@spans = alien_spans
|
230
|
-
end
|
231
|
-
|
232
|
-
return if spans.empty? || !span.sampled
|
233
|
-
write(spans)
|
276
|
+
# Record the given +context+. For compatibility with previous versions,
|
277
|
+
# +context+ can also be a span. It is similar to the +child_of+ argument,
|
278
|
+
# method will figure out what to do, submitting a +span+ for recording
|
279
|
+
# is like trying to record its +context+.
|
280
|
+
def record(context)
|
281
|
+
context = context.context if context.is_a?(Datadog::Span)
|
282
|
+
return if context.nil?
|
283
|
+
trace, sampled = context.get
|
284
|
+
ready = !trace.nil? && !trace.empty? && sampled
|
285
|
+
write(trace) if ready
|
234
286
|
end
|
235
287
|
|
236
288
|
# Return the current active span or +nil+.
|
237
289
|
def active_span
|
238
|
-
|
290
|
+
call_context.current_span
|
239
291
|
end
|
240
292
|
|
241
|
-
|
293
|
+
# Send the trace to the writer to enqueue the spans list in the agent
|
294
|
+
# sending queue.
|
295
|
+
def write(trace)
|
242
296
|
return if @writer.nil? || !@enabled
|
243
297
|
|
244
298
|
if Datadog::Tracer.debug_logging
|
245
|
-
Datadog::Tracer.log.debug("Writing #{
|
246
|
-
|
299
|
+
Datadog::Tracer.log.debug("Writing #{trace.length} spans (enabled: #{@enabled})")
|
300
|
+
str = String.new('')
|
301
|
+
PP.pp(trace, str)
|
302
|
+
Datadog::Tracer.log.debug(str)
|
247
303
|
end
|
248
304
|
|
249
|
-
@writer.write(
|
305
|
+
@writer.write(trace, @services)
|
250
306
|
end
|
251
307
|
|
252
|
-
private :write
|
308
|
+
private :write, :guess_context_and_parent
|
253
309
|
end
|
254
310
|
end
|