fair-ddtrace 0.8.2.a
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 +7 -0
- data/.env +11 -0
- data/.gitignore +59 -0
- data/.rubocop.yml +61 -0
- data/.yardopts +5 -0
- data/Appraisals +136 -0
- data/Gemfile +3 -0
- data/LICENSE +24 -0
- data/README.md +156 -0
- data/Rakefile +176 -0
- data/circle.yml +61 -0
- data/ddtrace.gemspec +44 -0
- data/docker-compose.yml +42 -0
- data/docs/GettingStarted.md +735 -0
- data/gemfiles/contrib.gemfile +16 -0
- data/gemfiles/contrib_old.gemfile +15 -0
- data/gemfiles/rails30_postgres.gemfile +10 -0
- data/gemfiles/rails30_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails32_mysql2.gemfile +11 -0
- data/gemfiles/rails32_postgres.gemfile +10 -0
- data/gemfiles/rails32_postgres_redis.gemfile +11 -0
- data/gemfiles/rails32_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails4_mysql2.gemfile +9 -0
- data/gemfiles/rails4_postgres.gemfile +9 -0
- data/gemfiles/rails4_postgres_redis.gemfile +10 -0
- data/gemfiles/rails4_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails5_mysql2.gemfile +8 -0
- data/gemfiles/rails5_postgres.gemfile +8 -0
- data/gemfiles/rails5_postgres_redis.gemfile +9 -0
- data/gemfiles/rails5_postgres_sidekiq.gemfile +10 -0
- data/lib/ddtrace.rb +73 -0
- data/lib/ddtrace/buffer.rb +52 -0
- data/lib/ddtrace/context.rb +145 -0
- data/lib/ddtrace/contrib/active_record/patcher.rb +94 -0
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +108 -0
- data/lib/ddtrace/contrib/elasticsearch/quantize.rb +22 -0
- data/lib/ddtrace/contrib/grape/endpoint.rb +164 -0
- data/lib/ddtrace/contrib/grape/patcher.rb +73 -0
- data/lib/ddtrace/contrib/http/patcher.rb +156 -0
- data/lib/ddtrace/contrib/rack/middlewares.rb +150 -0
- data/lib/ddtrace/contrib/rails/action_controller.rb +81 -0
- data/lib/ddtrace/contrib/rails/action_view.rb +110 -0
- data/lib/ddtrace/contrib/rails/active_record.rb +56 -0
- data/lib/ddtrace/contrib/rails/active_support.rb +113 -0
- data/lib/ddtrace/contrib/rails/core_extensions.rb +137 -0
- data/lib/ddtrace/contrib/rails/framework.rb +171 -0
- data/lib/ddtrace/contrib/rails/middlewares.rb +32 -0
- data/lib/ddtrace/contrib/rails/utils.rb +43 -0
- data/lib/ddtrace/contrib/redis/patcher.rb +118 -0
- data/lib/ddtrace/contrib/redis/quantize.rb +30 -0
- data/lib/ddtrace/contrib/redis/tags.rb +19 -0
- data/lib/ddtrace/contrib/sidekiq/tracer.rb +103 -0
- data/lib/ddtrace/contrib/sinatra/tracer.rb +169 -0
- data/lib/ddtrace/distributed.rb +38 -0
- data/lib/ddtrace/encoding.rb +65 -0
- data/lib/ddtrace/error.rb +37 -0
- data/lib/ddtrace/ext/app_types.rb +10 -0
- data/lib/ddtrace/ext/cache.rb +7 -0
- data/lib/ddtrace/ext/distributed.rb +10 -0
- data/lib/ddtrace/ext/errors.rb +10 -0
- data/lib/ddtrace/ext/http.rb +11 -0
- data/lib/ddtrace/ext/net.rb +8 -0
- data/lib/ddtrace/ext/redis.rb +11 -0
- data/lib/ddtrace/ext/sql.rb +8 -0
- data/lib/ddtrace/logger.rb +39 -0
- data/lib/ddtrace/monkey.rb +84 -0
- data/lib/ddtrace/pin.rb +63 -0
- data/lib/ddtrace/provider.rb +21 -0
- data/lib/ddtrace/sampler.rb +49 -0
- data/lib/ddtrace/span.rb +222 -0
- data/lib/ddtrace/tracer.rb +310 -0
- data/lib/ddtrace/transport.rb +162 -0
- data/lib/ddtrace/utils.rb +16 -0
- data/lib/ddtrace/version.rb +9 -0
- data/lib/ddtrace/workers.rb +108 -0
- data/lib/ddtrace/writer.rb +118 -0
- metadata +208 -0
data/lib/ddtrace/span.rb
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'thread'
|
|
3
|
+
|
|
4
|
+
require 'ddtrace/utils'
|
|
5
|
+
require 'ddtrace/ext/errors'
|
|
6
|
+
|
|
7
|
+
module Datadog
|
|
8
|
+
# Represents a logical unit of work in the system. Each trace consists of one or more spans.
|
|
9
|
+
# Each span consists of a start time and a duration. For example, a span can describe the time
|
|
10
|
+
# spent on a distributed call on a separate machine, or the time spent in a small component
|
|
11
|
+
# within a larger operation. Spans can be nested within each other, and in those instances
|
|
12
|
+
# will have a parent-child relationship.
|
|
13
|
+
class Span
|
|
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
|
|
20
|
+
|
|
21
|
+
attr_accessor :name, :service, :resource, :span_type,
|
|
22
|
+
:start_time, :end_time,
|
|
23
|
+
:span_id, :trace_id, :parent_id,
|
|
24
|
+
:status, :sampled,
|
|
25
|
+
:tracer, :context
|
|
26
|
+
|
|
27
|
+
attr_reader :parent
|
|
28
|
+
|
|
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.
|
|
31
|
+
#
|
|
32
|
+
# * +service+: the service name for this span
|
|
33
|
+
# * +resource+: the resource this span refers, or +name+ if it's missing
|
|
34
|
+
# * +span_type+: the type of the span (such as +http+, +db+ and so on)
|
|
35
|
+
# * +parent_id+: the identifier of the parent span
|
|
36
|
+
# * +trace_id+: the identifier of the root span for this trace
|
|
37
|
+
# * +context+: the context of the span
|
|
38
|
+
def initialize(tracer, name, options = {})
|
|
39
|
+
@tracer = tracer
|
|
40
|
+
|
|
41
|
+
@name = name
|
|
42
|
+
@service = options.fetch(:service, nil)
|
|
43
|
+
@resource = options.fetch(:resource, name)
|
|
44
|
+
@span_type = options.fetch(:span_type, nil)
|
|
45
|
+
|
|
46
|
+
@span_id = Datadog::Utils.next_id
|
|
47
|
+
@parent_id = options.fetch(:parent_id, 0)
|
|
48
|
+
@trace_id = options.fetch(:trace_id, Datadog::Utils.next_id)
|
|
49
|
+
|
|
50
|
+
@context = options.fetch(:context, nil)
|
|
51
|
+
|
|
52
|
+
@meta = {}
|
|
53
|
+
@metrics = {}
|
|
54
|
+
@status = 0
|
|
55
|
+
|
|
56
|
+
@parent = nil
|
|
57
|
+
@sampled = true
|
|
58
|
+
|
|
59
|
+
@start_time = nil # set by Tracer.start_span
|
|
60
|
+
@end_time = nil # set by Span.finish
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Set the given key / value tag pair on the span. Keys and values
|
|
64
|
+
# must be strings. A valid example is:
|
|
65
|
+
#
|
|
66
|
+
# span.set_tag('http.method', request.method)
|
|
67
|
+
def set_tag(key, value)
|
|
68
|
+
@meta[key] = value.to_s
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
Datadog::Tracer.log.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Return the tag with the given key, nil if it doesn't exist.
|
|
74
|
+
def get_tag(key)
|
|
75
|
+
@meta[key]
|
|
76
|
+
end
|
|
77
|
+
|
|
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.
|
|
80
|
+
def set_metric(key, value)
|
|
81
|
+
# enforce that the value is a floating point number
|
|
82
|
+
value = Float(value)
|
|
83
|
+
@metrics[key] = value
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
Datadog::Tracer.log.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Return the metric with the given key, nil if it doesn't exist.
|
|
89
|
+
def get_metric(key)
|
|
90
|
+
@metrics[key]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Mark the span with the given error.
|
|
94
|
+
def set_error(e)
|
|
95
|
+
e = Error.build_from(e)
|
|
96
|
+
|
|
97
|
+
@status = Ext::Errors::STATUS
|
|
98
|
+
set_tag(Ext::Errors::TYPE, e.type) unless e.type.empty?
|
|
99
|
+
set_tag(Ext::Errors::MSG, e.message) unless e.message.empty?
|
|
100
|
+
set_tag(Ext::Errors::STACK, e.backtrace) unless e.backtrace.empty?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Mark the span finished at the current time and submit it.
|
|
104
|
+
def finish(finish_time = nil)
|
|
105
|
+
# A span should not be finished twice. Note that this is not thread-safe,
|
|
106
|
+
# finish is called from multiple threads, a given span might be finished
|
|
107
|
+
# several times. Again, one should not do this, so this test is more a
|
|
108
|
+
# fallback to avoid very bad things and protect you in most common cases.
|
|
109
|
+
return if finished?
|
|
110
|
+
|
|
111
|
+
# Provide a default start_time if unset, but this should have been set by start_span.
|
|
112
|
+
# Using now here causes 0-duration spans, still, this is expected, as we never
|
|
113
|
+
# explicitely say when it started.
|
|
114
|
+
@start_time ||= Time.now.utc
|
|
115
|
+
|
|
116
|
+
@end_time = finish_time.nil? ? Time.now.utc : finish_time # finish this
|
|
117
|
+
|
|
118
|
+
# Finish does not really do anything if the span is not bound to a tracer and a context.
|
|
119
|
+
return self if @tracer.nil? || @context.nil?
|
|
120
|
+
|
|
121
|
+
# spans without a service would be dropped, so here we provide a default.
|
|
122
|
+
# This should really never happen with integrations in contrib, as a default
|
|
123
|
+
# service is always set. It's only for custom instrumentation.
|
|
124
|
+
@service ||= @tracer.default_service unless @tracer.nil?
|
|
125
|
+
|
|
126
|
+
begin
|
|
127
|
+
@context.close_span(self)
|
|
128
|
+
@tracer.record(self)
|
|
129
|
+
rescue StandardError => e
|
|
130
|
+
Datadog::Tracer.log.debug("error recording finished trace: #{e}")
|
|
131
|
+
end
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Return whether the span is finished or not.
|
|
136
|
+
def finished?
|
|
137
|
+
!@end_time.nil?
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Return a string representation of the span.
|
|
141
|
+
def to_s
|
|
142
|
+
"Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# DEPRECATED: remove this function in the next release, replaced by ``parent=``
|
|
146
|
+
def set_parent(parent)
|
|
147
|
+
self.parent = parent
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Set this span's parent, inheriting any properties not explicitly set.
|
|
151
|
+
# If the parent is nil, set the span zero values.
|
|
152
|
+
def parent=(parent)
|
|
153
|
+
@parent = parent
|
|
154
|
+
|
|
155
|
+
if parent.nil?
|
|
156
|
+
@trace_id = @span_id
|
|
157
|
+
@parent_id = 0
|
|
158
|
+
else
|
|
159
|
+
@trace_id = parent.trace_id
|
|
160
|
+
@parent_id = parent.span_id
|
|
161
|
+
@service ||= parent.service
|
|
162
|
+
@sampled = parent.sampled
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Return the hash representation of the current span.
|
|
167
|
+
def to_hash
|
|
168
|
+
h = {
|
|
169
|
+
span_id: @span_id,
|
|
170
|
+
parent_id: @parent_id,
|
|
171
|
+
trace_id: @trace_id,
|
|
172
|
+
name: @name,
|
|
173
|
+
service: @service,
|
|
174
|
+
resource: @resource,
|
|
175
|
+
type: @span_type,
|
|
176
|
+
meta: @meta,
|
|
177
|
+
metrics: @metrics,
|
|
178
|
+
error: @status
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if !@start_time.nil? && !@end_time.nil?
|
|
182
|
+
h[:start] = (@start_time.to_f * 1e9).to_i
|
|
183
|
+
h[:duration] = ((@end_time - @start_time) * 1e9).to_i
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
h
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Return a human readable version of the span
|
|
190
|
+
def pretty_print(q)
|
|
191
|
+
start_time = (@start_time.to_f * 1e9).to_i rescue '-'
|
|
192
|
+
end_time = (@end_time.to_f * 1e9).to_i rescue '-'
|
|
193
|
+
duration = ((@end_time - @start_time) * 1e9).to_i rescue 0
|
|
194
|
+
q.group 0 do
|
|
195
|
+
q.breakable
|
|
196
|
+
q.text "Name: #{@name}\n"
|
|
197
|
+
q.text "Span ID: #{@span_id}\n"
|
|
198
|
+
q.text "Parent ID: #{@parent_id}\n"
|
|
199
|
+
q.text "Trace ID: #{@trace_id}\n"
|
|
200
|
+
q.text "Type: #{@span_type}\n"
|
|
201
|
+
q.text "Service: #{@service}\n"
|
|
202
|
+
q.text "Resource: #{@resource}\n"
|
|
203
|
+
q.text "Error: #{@status}\n"
|
|
204
|
+
q.text "Start: #{start_time}\n"
|
|
205
|
+
q.text "End: #{end_time}\n"
|
|
206
|
+
q.text "Duration: #{duration}\n"
|
|
207
|
+
q.group(2, 'Tags: [', "]\n") do
|
|
208
|
+
q.breakable
|
|
209
|
+
q.seplist @meta.each do |key, value|
|
|
210
|
+
q.text "#{key} => #{value}"
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
q.group(2, 'Metrics: [', ']') do
|
|
214
|
+
q.breakable
|
|
215
|
+
q.seplist @metrics.each do |key, value|
|
|
216
|
+
q.text "#{key} => #{value}"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
require 'thread'
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
|
|
6
|
+
require 'ddtrace/span'
|
|
7
|
+
require 'ddtrace/context'
|
|
8
|
+
require 'ddtrace/provider'
|
|
9
|
+
require 'ddtrace/logger'
|
|
10
|
+
require 'ddtrace/writer'
|
|
11
|
+
require 'ddtrace/sampler'
|
|
12
|
+
|
|
13
|
+
# \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
|
|
14
|
+
module Datadog
|
|
15
|
+
# A \Tracer keeps track of the time spent by an application processing a single operation. For
|
|
16
|
+
# example, a trace can be used to track the entire time spent processing a complicated web request.
|
|
17
|
+
# Even though the request may require multiple resources and machines to handle the request, all
|
|
18
|
+
# of these function calls and sub-requests would be encapsulated within a single trace.
|
|
19
|
+
# rubocop:disable Metrics/ClassLength
|
|
20
|
+
class Tracer
|
|
21
|
+
attr_reader :writer, :sampler, :services, :tags, :provider
|
|
22
|
+
attr_accessor :enabled
|
|
23
|
+
attr_writer :default_service
|
|
24
|
+
|
|
25
|
+
# Global, memoized, lazy initialized instance of a logger that is used within the the Datadog
|
|
26
|
+
# namespace. This logger outputs to +STDOUT+ by default, and is considered thread-safe.
|
|
27
|
+
def self.log
|
|
28
|
+
unless defined? @logger
|
|
29
|
+
@logger = Datadog::Logger.new(STDOUT)
|
|
30
|
+
@logger.level = Logger::WARN
|
|
31
|
+
end
|
|
32
|
+
@logger
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Override the default logger with a custom one.
|
|
36
|
+
def self.log=(logger)
|
|
37
|
+
return unless logger
|
|
38
|
+
return unless logger.respond_to? :methods
|
|
39
|
+
return unless logger.respond_to? :error
|
|
40
|
+
if logger.respond_to? :methods
|
|
41
|
+
unimplemented = Logger.new(STDOUT).methods - logger.methods
|
|
42
|
+
unless unimplemented.empty?
|
|
43
|
+
logger.error("logger #{logger} does not implement #{unimplemented}")
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
@logger = logger
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Activate the debug mode providing more information related to tracer usage
|
|
51
|
+
def self.debug_logging=(value)
|
|
52
|
+
log.level = value ? Logger::DEBUG : Logger::WARN
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return if the debug mode is activated or not
|
|
56
|
+
def self.debug_logging
|
|
57
|
+
log.level == Logger::DEBUG
|
|
58
|
+
end
|
|
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
|
+
|
|
70
|
+
# Initialize a new \Tracer used to create, sample and submit spans that measure the
|
|
71
|
+
# time of sections of code. Available +options+ are:
|
|
72
|
+
#
|
|
73
|
+
# * +enabled+: set if the tracer submits or not spans to the local agent. It's enabled
|
|
74
|
+
# by default.
|
|
75
|
+
def initialize(options = {})
|
|
76
|
+
@enabled = options.fetch(:enabled, true)
|
|
77
|
+
@writer = options.fetch(:writer, Datadog::Writer.new)
|
|
78
|
+
@sampler = options.fetch(:sampler, Datadog::AllSampler.new)
|
|
79
|
+
|
|
80
|
+
@provider = options.fetch(:context_provider, Datadog::DefaultContextProvider.new)
|
|
81
|
+
@provider ||= Datadog::DefaultContextProvider.new # @provider should never be nil
|
|
82
|
+
|
|
83
|
+
@mutex = Mutex.new
|
|
84
|
+
@services = {}
|
|
85
|
+
@tags = {}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Updates the current \Tracer instance, so that the tracer can be configured after the
|
|
89
|
+
# initialization. Available +options+ are:
|
|
90
|
+
#
|
|
91
|
+
# * +enabled+: set if the tracer submits or not spans to the trace agent
|
|
92
|
+
# * +hostname+: change the location of the trace agent
|
|
93
|
+
# * +port+: change the port of the trace agent
|
|
94
|
+
#
|
|
95
|
+
# For instance, if the trace agent runs in a different location, just:
|
|
96
|
+
#
|
|
97
|
+
# tracer.configure(hostname: 'agent.service.consul', port: '8777')
|
|
98
|
+
#
|
|
99
|
+
def configure(options = {})
|
|
100
|
+
enabled = options.fetch(:enabled, nil)
|
|
101
|
+
hostname = options.fetch(:hostname, nil)
|
|
102
|
+
port = options.fetch(:port, nil)
|
|
103
|
+
sampler = options.fetch(:sampler, nil)
|
|
104
|
+
|
|
105
|
+
@enabled = enabled unless enabled.nil?
|
|
106
|
+
@writer.transport.hostname = hostname unless hostname.nil?
|
|
107
|
+
@writer.transport.port = port unless port.nil?
|
|
108
|
+
@sampler = sampler unless sampler.nil?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Set the information about the given service. A valid example is:
|
|
112
|
+
#
|
|
113
|
+
# tracer.set_service_info('web-application', 'rails', 'web')
|
|
114
|
+
def set_service_info(service, app, app_type)
|
|
115
|
+
@services[service] = {
|
|
116
|
+
'app' => app,
|
|
117
|
+
'app_type' => app_type
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return unless Datadog::Tracer.debug_logging
|
|
121
|
+
Datadog::Tracer.log.debug("set_service_info: service: #{service} app: #{app} type: #{app_type}")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# A default value for service. One should really override this one
|
|
125
|
+
# for non-root spans which have a parent. However, root spans without
|
|
126
|
+
# a service would be invalid and rejected.
|
|
127
|
+
def default_service
|
|
128
|
+
return @default_service if instance_variable_defined?(:@default_service) && @default_service
|
|
129
|
+
begin
|
|
130
|
+
@default_service = File.basename($PROGRAM_NAME, '.*')
|
|
131
|
+
rescue StandardError => e
|
|
132
|
+
Datadog::Tracer.log.error("unable to guess default service: #{e}")
|
|
133
|
+
@default_service = 'ruby'.freeze
|
|
134
|
+
end
|
|
135
|
+
@default_service
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Set the given key / value tag pair at the tracer level. These tags will be
|
|
139
|
+
# appended to each span created by the tracer. Keys and values must be strings.
|
|
140
|
+
# A valid example is:
|
|
141
|
+
#
|
|
142
|
+
# tracer.set_tags('env' => 'prod', 'component' => 'core')
|
|
143
|
+
def set_tags(tags)
|
|
144
|
+
@tags.update(tags)
|
|
145
|
+
end
|
|
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
|
+
|
|
210
|
+
# Return a +span+ that will trace an operation called +name+. You could trace your code
|
|
211
|
+
# using a <tt>do-block</tt> like:
|
|
212
|
+
#
|
|
213
|
+
# tracer.trace('web.request') do |span|
|
|
214
|
+
# span.service = 'my-web-site'
|
|
215
|
+
# span.resource = '/'
|
|
216
|
+
# span.set_tag('http.method', request.request_method)
|
|
217
|
+
# do_something()
|
|
218
|
+
# end
|
|
219
|
+
#
|
|
220
|
+
# The <tt>tracer.trace()</tt> method can also be used without a block in this way:
|
|
221
|
+
#
|
|
222
|
+
# span = tracer.trace('web.request', service: 'my-web-site')
|
|
223
|
+
# do_something()
|
|
224
|
+
# span.finish()
|
|
225
|
+
#
|
|
226
|
+
# Remember that in this case, calling <tt>span.finish()</tt> is mandatory.
|
|
227
|
+
#
|
|
228
|
+
# When a Trace is started, <tt>trace()</tt> will store the created span; subsequent spans will
|
|
229
|
+
# become it's children and will inherit some properties:
|
|
230
|
+
#
|
|
231
|
+
# parent = tracer.trace('parent') # has no parent span
|
|
232
|
+
# child = tracer.trace('child') # is a child of 'parent'
|
|
233
|
+
# child.finish()
|
|
234
|
+
# parent.finish()
|
|
235
|
+
# parent2 = tracer.trace('parent2') # has no parent span
|
|
236
|
+
# parent2.finish()
|
|
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.
|
|
244
|
+
def trace(name, options = {})
|
|
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)
|
|
249
|
+
end
|
|
250
|
+
opts[:child_of] = call_context
|
|
251
|
+
span = start_span(name, opts)
|
|
252
|
+
|
|
253
|
+
# call the finish only if a block is given; this ensures
|
|
254
|
+
# that a call to tracer.trace() without a block, returns
|
|
255
|
+
# a span that should be manually finished.
|
|
256
|
+
if block_given?
|
|
257
|
+
begin
|
|
258
|
+
yield(span)
|
|
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
|
|
266
|
+
span.set_error(e)
|
|
267
|
+
raise e
|
|
268
|
+
ensure
|
|
269
|
+
span.finish()
|
|
270
|
+
end
|
|
271
|
+
else
|
|
272
|
+
span
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
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
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Return the current active span or +nil+.
|
|
289
|
+
def active_span
|
|
290
|
+
call_context.current_span
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Send the trace to the writer to enqueue the spans list in the agent
|
|
294
|
+
# sending queue.
|
|
295
|
+
def write(trace)
|
|
296
|
+
return if @writer.nil? || !@enabled
|
|
297
|
+
|
|
298
|
+
if Datadog::Tracer.debug_logging
|
|
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)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
@writer.write(trace, @services)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
private :write, :guess_context_and_parent
|
|
309
|
+
end
|
|
310
|
+
end
|