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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.env +11 -0
  3. data/.gitignore +59 -0
  4. data/.rubocop.yml +61 -0
  5. data/.yardopts +5 -0
  6. data/Appraisals +136 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +24 -0
  9. data/README.md +156 -0
  10. data/Rakefile +176 -0
  11. data/circle.yml +61 -0
  12. data/ddtrace.gemspec +44 -0
  13. data/docker-compose.yml +42 -0
  14. data/docs/GettingStarted.md +735 -0
  15. data/gemfiles/contrib.gemfile +16 -0
  16. data/gemfiles/contrib_old.gemfile +15 -0
  17. data/gemfiles/rails30_postgres.gemfile +10 -0
  18. data/gemfiles/rails30_postgres_sidekiq.gemfile +11 -0
  19. data/gemfiles/rails32_mysql2.gemfile +11 -0
  20. data/gemfiles/rails32_postgres.gemfile +10 -0
  21. data/gemfiles/rails32_postgres_redis.gemfile +11 -0
  22. data/gemfiles/rails32_postgres_sidekiq.gemfile +11 -0
  23. data/gemfiles/rails4_mysql2.gemfile +9 -0
  24. data/gemfiles/rails4_postgres.gemfile +9 -0
  25. data/gemfiles/rails4_postgres_redis.gemfile +10 -0
  26. data/gemfiles/rails4_postgres_sidekiq.gemfile +11 -0
  27. data/gemfiles/rails5_mysql2.gemfile +8 -0
  28. data/gemfiles/rails5_postgres.gemfile +8 -0
  29. data/gemfiles/rails5_postgres_redis.gemfile +9 -0
  30. data/gemfiles/rails5_postgres_sidekiq.gemfile +10 -0
  31. data/lib/ddtrace.rb +73 -0
  32. data/lib/ddtrace/buffer.rb +52 -0
  33. data/lib/ddtrace/context.rb +145 -0
  34. data/lib/ddtrace/contrib/active_record/patcher.rb +94 -0
  35. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +108 -0
  36. data/lib/ddtrace/contrib/elasticsearch/quantize.rb +22 -0
  37. data/lib/ddtrace/contrib/grape/endpoint.rb +164 -0
  38. data/lib/ddtrace/contrib/grape/patcher.rb +73 -0
  39. data/lib/ddtrace/contrib/http/patcher.rb +156 -0
  40. data/lib/ddtrace/contrib/rack/middlewares.rb +150 -0
  41. data/lib/ddtrace/contrib/rails/action_controller.rb +81 -0
  42. data/lib/ddtrace/contrib/rails/action_view.rb +110 -0
  43. data/lib/ddtrace/contrib/rails/active_record.rb +56 -0
  44. data/lib/ddtrace/contrib/rails/active_support.rb +113 -0
  45. data/lib/ddtrace/contrib/rails/core_extensions.rb +137 -0
  46. data/lib/ddtrace/contrib/rails/framework.rb +171 -0
  47. data/lib/ddtrace/contrib/rails/middlewares.rb +32 -0
  48. data/lib/ddtrace/contrib/rails/utils.rb +43 -0
  49. data/lib/ddtrace/contrib/redis/patcher.rb +118 -0
  50. data/lib/ddtrace/contrib/redis/quantize.rb +30 -0
  51. data/lib/ddtrace/contrib/redis/tags.rb +19 -0
  52. data/lib/ddtrace/contrib/sidekiq/tracer.rb +103 -0
  53. data/lib/ddtrace/contrib/sinatra/tracer.rb +169 -0
  54. data/lib/ddtrace/distributed.rb +38 -0
  55. data/lib/ddtrace/encoding.rb +65 -0
  56. data/lib/ddtrace/error.rb +37 -0
  57. data/lib/ddtrace/ext/app_types.rb +10 -0
  58. data/lib/ddtrace/ext/cache.rb +7 -0
  59. data/lib/ddtrace/ext/distributed.rb +10 -0
  60. data/lib/ddtrace/ext/errors.rb +10 -0
  61. data/lib/ddtrace/ext/http.rb +11 -0
  62. data/lib/ddtrace/ext/net.rb +8 -0
  63. data/lib/ddtrace/ext/redis.rb +11 -0
  64. data/lib/ddtrace/ext/sql.rb +8 -0
  65. data/lib/ddtrace/logger.rb +39 -0
  66. data/lib/ddtrace/monkey.rb +84 -0
  67. data/lib/ddtrace/pin.rb +63 -0
  68. data/lib/ddtrace/provider.rb +21 -0
  69. data/lib/ddtrace/sampler.rb +49 -0
  70. data/lib/ddtrace/span.rb +222 -0
  71. data/lib/ddtrace/tracer.rb +310 -0
  72. data/lib/ddtrace/transport.rb +162 -0
  73. data/lib/ddtrace/utils.rb +16 -0
  74. data/lib/ddtrace/version.rb +9 -0
  75. data/lib/ddtrace/workers.rb +108 -0
  76. data/lib/ddtrace/writer.rb +118 -0
  77. metadata +208 -0
@@ -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