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