ddtrace 0.37.0 → 0.42.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.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +56 -0
  3. data/.gitignore +2 -0
  4. data/.gitlab-ci.yml +1 -0
  5. data/.simplecov +38 -0
  6. data/Appraisals +186 -11
  7. data/CHANGELOG.md +188 -1
  8. data/CONTRIBUTING.md +1 -1
  9. data/Rakefile +518 -482
  10. data/ddtrace.gemspec +3 -0
  11. data/docker-compose.yml +2 -2
  12. data/docs/DevelopmentGuide.md +26 -0
  13. data/docs/GettingStarted.md +188 -78
  14. data/lib/ddtrace.rb +4 -0
  15. data/lib/ddtrace/buffer.rb +259 -52
  16. data/lib/ddtrace/configuration.rb +39 -5
  17. data/lib/ddtrace/configuration/components.rb +4 -7
  18. data/lib/ddtrace/configuration/options.rb +3 -1
  19. data/lib/ddtrace/configuration/settings.rb +32 -4
  20. data/lib/ddtrace/context_provider.rb +6 -5
  21. data/lib/ddtrace/contrib/action_cable/configuration/settings.rb +7 -2
  22. data/lib/ddtrace/contrib/action_cable/ext.rb +5 -2
  23. data/lib/ddtrace/contrib/action_pack/configuration/settings.rb +7 -2
  24. data/lib/ddtrace/contrib/action_pack/ext.rb +5 -2
  25. data/lib/ddtrace/contrib/action_view/configuration/settings.rb +7 -2
  26. data/lib/ddtrace/contrib/action_view/ext.rb +5 -2
  27. data/lib/ddtrace/contrib/active_model_serializers/configuration/settings.rb +7 -2
  28. data/lib/ddtrace/contrib/active_model_serializers/ext.rb +5 -2
  29. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +7 -2
  30. data/lib/ddtrace/contrib/active_record/events/sql.rb +4 -0
  31. data/lib/ddtrace/contrib/active_record/ext.rb +5 -2
  32. data/lib/ddtrace/contrib/active_support/configuration/settings.rb +7 -2
  33. data/lib/ddtrace/contrib/active_support/ext.rb +5 -2
  34. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +3 -3
  35. data/lib/ddtrace/contrib/aws/configuration/settings.rb +7 -2
  36. data/lib/ddtrace/contrib/aws/ext.rb +5 -2
  37. data/lib/ddtrace/contrib/aws/instrumentation.rb +4 -0
  38. data/lib/ddtrace/contrib/concurrent_ruby/configuration/settings.rb +5 -0
  39. data/lib/ddtrace/contrib/concurrent_ruby/ext.rb +1 -0
  40. data/lib/ddtrace/contrib/configuration/settings.rb +1 -0
  41. data/lib/ddtrace/contrib/dalli/configuration/settings.rb +7 -2
  42. data/lib/ddtrace/contrib/dalli/ext.rb +5 -2
  43. data/lib/ddtrace/contrib/dalli/instrumentation.rb +4 -0
  44. data/lib/ddtrace/contrib/delayed_job/configuration/settings.rb +8 -2
  45. data/lib/ddtrace/contrib/delayed_job/ext.rb +7 -2
  46. data/lib/ddtrace/contrib/delayed_job/plugin.rb +37 -15
  47. data/lib/ddtrace/contrib/elasticsearch/configuration/settings.rb +7 -2
  48. data/lib/ddtrace/contrib/elasticsearch/ext.rb +5 -2
  49. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +4 -0
  50. data/lib/ddtrace/contrib/ethon/configuration/settings.rb +7 -2
  51. data/lib/ddtrace/contrib/ethon/easy_patch.rb +4 -2
  52. data/lib/ddtrace/contrib/ethon/ext.rb +5 -2
  53. data/lib/ddtrace/contrib/ethon/multi_patch.rb +4 -0
  54. data/lib/ddtrace/contrib/excon/configuration/settings.rb +7 -2
  55. data/lib/ddtrace/contrib/excon/ext.rb +5 -2
  56. data/lib/ddtrace/contrib/excon/middleware.rb +4 -0
  57. data/lib/ddtrace/contrib/extensions.rb +11 -1
  58. data/lib/ddtrace/contrib/faraday/configuration/settings.rb +7 -2
  59. data/lib/ddtrace/contrib/faraday/ext.rb +5 -2
  60. data/lib/ddtrace/contrib/faraday/middleware.rb +9 -3
  61. data/lib/ddtrace/contrib/faraday/patcher.rb +12 -0
  62. data/lib/ddtrace/contrib/grape/configuration/settings.rb +7 -3
  63. data/lib/ddtrace/contrib/grape/endpoint.rb +6 -4
  64. data/lib/ddtrace/contrib/grape/ext.rb +5 -2
  65. data/lib/ddtrace/contrib/graphql/configuration/settings.rb +7 -2
  66. data/lib/ddtrace/contrib/graphql/ext.rb +5 -2
  67. data/lib/ddtrace/contrib/grpc/configuration/settings.rb +7 -2
  68. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +6 -4
  69. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +4 -0
  70. data/lib/ddtrace/contrib/grpc/ext.rb +5 -2
  71. data/lib/ddtrace/contrib/http/configuration/settings.rb +7 -2
  72. data/lib/ddtrace/contrib/http/ext.rb +5 -2
  73. data/lib/ddtrace/contrib/http/instrumentation.rb +4 -0
  74. data/lib/ddtrace/contrib/httprb/configuration/settings.rb +32 -0
  75. data/lib/ddtrace/contrib/httprb/ext.rb +17 -0
  76. data/lib/ddtrace/contrib/httprb/instrumentation.rb +163 -0
  77. data/lib/ddtrace/contrib/httprb/integration.rb +43 -0
  78. data/lib/ddtrace/contrib/httprb/patcher.rb +35 -0
  79. data/lib/ddtrace/contrib/kafka/configuration/settings.rb +30 -0
  80. data/lib/ddtrace/contrib/kafka/consumer_event.rb +14 -0
  81. data/lib/ddtrace/contrib/kafka/consumer_group_event.rb +14 -0
  82. data/lib/ddtrace/contrib/kafka/event.rb +51 -0
  83. data/lib/ddtrace/contrib/kafka/events.rb +44 -0
  84. data/lib/ddtrace/contrib/kafka/events/connection/request.rb +34 -0
  85. data/lib/ddtrace/contrib/kafka/events/consumer/process_batch.rb +41 -0
  86. data/lib/ddtrace/contrib/kafka/events/consumer/process_message.rb +39 -0
  87. data/lib/ddtrace/contrib/kafka/events/consumer_group/heartbeat.rb +39 -0
  88. data/lib/ddtrace/contrib/kafka/events/consumer_group/join_group.rb +29 -0
  89. data/lib/ddtrace/contrib/kafka/events/consumer_group/leave_group.rb +29 -0
  90. data/lib/ddtrace/contrib/kafka/events/consumer_group/sync_group.rb +29 -0
  91. data/lib/ddtrace/contrib/kafka/events/produce_operation/send_messages.rb +32 -0
  92. data/lib/ddtrace/contrib/kafka/events/producer/deliver_messages.rb +35 -0
  93. data/lib/ddtrace/contrib/kafka/ext.rb +41 -0
  94. data/lib/ddtrace/contrib/kafka/integration.rb +39 -0
  95. data/lib/ddtrace/contrib/kafka/patcher.rb +26 -0
  96. data/lib/ddtrace/contrib/mongodb/configuration/settings.rb +7 -2
  97. data/lib/ddtrace/contrib/mongodb/ext.rb +5 -2
  98. data/lib/ddtrace/contrib/mongodb/subscribers.rb +4 -0
  99. data/lib/ddtrace/contrib/mysql2/configuration/settings.rb +7 -2
  100. data/lib/ddtrace/contrib/mysql2/ext.rb +5 -2
  101. data/lib/ddtrace/contrib/mysql2/instrumentation.rb +4 -0
  102. data/lib/ddtrace/contrib/presto/configuration/settings.rb +7 -2
  103. data/lib/ddtrace/contrib/presto/ext.rb +5 -2
  104. data/lib/ddtrace/contrib/presto/instrumentation.rb +3 -0
  105. data/lib/ddtrace/contrib/que/configuration/settings.rb +42 -0
  106. data/lib/ddtrace/contrib/que/ext.rb +30 -0
  107. data/lib/ddtrace/contrib/que/integration.rb +42 -0
  108. data/lib/ddtrace/contrib/que/patcher.rb +24 -0
  109. data/lib/ddtrace/contrib/que/tracer.rb +56 -0
  110. data/lib/ddtrace/contrib/racecar/configuration/settings.rb +7 -2
  111. data/lib/ddtrace/contrib/racecar/event.rb +4 -0
  112. data/lib/ddtrace/contrib/racecar/events.rb +2 -0
  113. data/lib/ddtrace/contrib/racecar/events/consume.rb +27 -0
  114. data/lib/ddtrace/contrib/racecar/ext.rb +6 -2
  115. data/lib/ddtrace/contrib/rack/configuration/settings.rb +7 -2
  116. data/lib/ddtrace/contrib/rack/ext.rb +5 -2
  117. data/lib/ddtrace/contrib/rack/middlewares.rb +17 -12
  118. data/lib/ddtrace/contrib/rails/configuration/settings.rb +12 -2
  119. data/lib/ddtrace/contrib/rails/ext.rb +6 -2
  120. data/lib/ddtrace/contrib/rails/log_injection.rb +81 -0
  121. data/lib/ddtrace/contrib/rails/middlewares.rb +7 -2
  122. data/lib/ddtrace/contrib/rails/patcher.rb +26 -0
  123. data/lib/ddtrace/contrib/rake/configuration/settings.rb +7 -3
  124. data/lib/ddtrace/contrib/rake/ext.rb +5 -2
  125. data/lib/ddtrace/contrib/redis/configuration/settings.rb +7 -2
  126. data/lib/ddtrace/contrib/redis/ext.rb +5 -2
  127. data/lib/ddtrace/contrib/redis/tags.rb +4 -0
  128. data/lib/ddtrace/contrib/resque/configuration/settings.rb +7 -2
  129. data/lib/ddtrace/contrib/resque/ext.rb +5 -2
  130. data/lib/ddtrace/contrib/resque/integration.rb +1 -1
  131. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +7 -2
  132. data/lib/ddtrace/contrib/rest_client/ext.rb +5 -2
  133. data/lib/ddtrace/contrib/rest_client/request_patch.rb +6 -2
  134. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +7 -2
  135. data/lib/ddtrace/contrib/sequel/database.rb +3 -1
  136. data/lib/ddtrace/contrib/sequel/dataset.rb +3 -2
  137. data/lib/ddtrace/contrib/sequel/ext.rb +6 -2
  138. data/lib/ddtrace/contrib/sequel/utils.rb +35 -6
  139. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +7 -2
  140. data/lib/ddtrace/contrib/shoryuken/ext.rb +5 -2
  141. data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +7 -2
  142. data/lib/ddtrace/contrib/sidekiq/ext.rb +6 -2
  143. data/lib/ddtrace/contrib/sidekiq/patcher.rb +8 -1
  144. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +1 -0
  145. data/lib/ddtrace/contrib/sinatra/configuration/settings.rb +7 -2
  146. data/lib/ddtrace/contrib/sinatra/env.rb +5 -4
  147. data/lib/ddtrace/contrib/sinatra/ext.rb +5 -2
  148. data/lib/ddtrace/contrib/sinatra/tracer.rb +21 -42
  149. data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +50 -23
  150. data/lib/ddtrace/contrib/sneakers/configuration/settings.rb +32 -0
  151. data/lib/ddtrace/contrib/sneakers/ext.rb +22 -0
  152. data/lib/ddtrace/contrib/sneakers/integration.rb +41 -0
  153. data/lib/ddtrace/contrib/sneakers/patcher.rb +24 -0
  154. data/lib/ddtrace/contrib/sneakers/tracer.rb +58 -0
  155. data/lib/ddtrace/contrib/sucker_punch/configuration/settings.rb +7 -2
  156. data/lib/ddtrace/contrib/sucker_punch/ext.rb +5 -2
  157. data/lib/ddtrace/diagnostics/environment_logger.rb +278 -0
  158. data/lib/ddtrace/environment.rb +17 -3
  159. data/lib/ddtrace/ext/diagnostics.rb +3 -0
  160. data/lib/ddtrace/ext/environment.rb +2 -0
  161. data/lib/ddtrace/ext/integration.rb +8 -0
  162. data/lib/ddtrace/ext/runtime.rb +1 -0
  163. data/lib/ddtrace/ext/transport.rb +1 -0
  164. data/lib/ddtrace/logger.rb +1 -1
  165. data/lib/ddtrace/opentracer/distributed_headers.rb +1 -1
  166. data/lib/ddtrace/pipeline/span_filter.rb +15 -15
  167. data/lib/ddtrace/propagation/grpc_propagator.rb +18 -6
  168. data/lib/ddtrace/runtime/metrics.rb +24 -6
  169. data/lib/ddtrace/sampler.rb +4 -2
  170. data/lib/ddtrace/span.rb +162 -27
  171. data/lib/ddtrace/tracer.rb +24 -18
  172. data/lib/ddtrace/transport/http.rb +15 -0
  173. data/lib/ddtrace/transport/http/adapters/net.rb +16 -2
  174. data/lib/ddtrace/transport/http/adapters/test.rb +6 -0
  175. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +4 -0
  176. data/lib/ddtrace/transport/http/statistics.rb +14 -1
  177. data/lib/ddtrace/transport/response.rb +11 -0
  178. data/lib/ddtrace/transport/traces.rb +7 -2
  179. data/lib/ddtrace/utils.rb +7 -3
  180. data/lib/ddtrace/version.rb +1 -1
  181. data/lib/ddtrace/workers/async.rb +2 -2
  182. data/lib/ddtrace/workers/loop.rb +1 -1
  183. data/lib/ddtrace/workers/polling.rb +1 -1
  184. data/lib/ddtrace/workers/trace_writer.rb +3 -0
  185. data/lib/ddtrace/writer.rb +33 -12
  186. metadata +81 -2
@@ -1,3 +1,4 @@
1
+ require 'ddtrace/ext/integration'
1
2
  require 'ddtrace/ext/runtime'
2
3
 
3
4
  require 'ddtrace/metrics'
@@ -25,8 +26,11 @@ module Datadog
25
26
  # Register service as associated with metrics
26
27
  register_service(span.service) unless span.service.nil?
27
28
 
28
- # Tag span with language and runtime ID for association with metrics
29
- span.set_tag(Ext::Runtime::TAG_LANG, Runtime::Identity.lang)
29
+ # Tag span with language and runtime ID for association with metrics.
30
+ # We only tag spans that performed internal application work.
31
+ unless span.get_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE)
32
+ span.set_tag(Ext::Runtime::TAG_LANG, Runtime::Identity.lang)
33
+ end
30
34
  end
31
35
 
32
36
  # Associate service with runtime metrics
@@ -55,10 +59,8 @@ module Datadog
55
59
 
56
60
  def gc_metrics
57
61
  Hash[
58
- GC.stat.map do |k, v|
59
- next if v.is_a?(Hash) # TODO: JRuby supports additional nested metrics
60
-
61
- ["#{Ext::Runtime::Metrics::METRIC_GC_PREFIX}.#{k}", v]
62
+ GC.stat.flat_map do |k, v|
63
+ nested_gc_metric(Ext::Runtime::Metrics::METRIC_GC_PREFIX, k, v)
62
64
  end
63
65
  ]
64
66
  end
@@ -91,6 +93,22 @@ module Datadog
91
93
  "#{Ext::Runtime::Metrics::TAG_SERVICE}:#{service}".freeze
92
94
  end
93
95
  end
96
+
97
+ def nested_gc_metric(prefix, k, v)
98
+ path = "#{prefix}.#{k}"
99
+
100
+ if v.is_a?(Hash)
101
+ v.flat_map do |key, value|
102
+ nested_gc_metric(path, key, value)
103
+ end
104
+ else
105
+ [[to_metric_name(path), v]]
106
+ end
107
+ end
108
+
109
+ def to_metric_name(str)
110
+ str.downcase.gsub(/[-\s]/, '_')
111
+ end
94
112
  end
95
113
  end
96
114
  end
@@ -61,11 +61,11 @@ module Datadog
61
61
 
62
62
  def sample_rate=(sample_rate)
63
63
  @sample_rate = sample_rate
64
- @sampling_id_threshold = sample_rate * Span::MAX_ID
64
+ @sampling_id_threshold = sample_rate * Span::EXTERNAL_MAX_ID
65
65
  end
66
66
 
67
67
  def sample?(span)
68
- ((span.trace_id * KNUTH_FACTOR) % Datadog::Span::MAX_ID) <= @sampling_id_threshold
68
+ ((span.trace_id * KNUTH_FACTOR) % Datadog::Span::EXTERNAL_MAX_ID) <= @sampling_id_threshold
69
69
  end
70
70
 
71
71
  def sample!(span)
@@ -193,6 +193,8 @@ module Datadog
193
193
  class PrioritySampler
194
194
  extend Forwardable
195
195
 
196
+ attr_reader :pre_sampler, :priority_sampler
197
+
196
198
  SAMPLE_RATE_METRIC_KEY = '_sample_rate'.freeze
197
199
 
198
200
  def initialize(opts = {})
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'time'
2
4
  require 'thread'
3
5
 
@@ -8,6 +10,7 @@ require 'ddtrace/environment'
8
10
  require 'ddtrace/analytics'
9
11
  require 'ddtrace/forced_tracing'
10
12
  require 'ddtrace/diagnostics/health'
13
+ require 'ddtrace/utils/time'
11
14
 
12
15
  module Datadog
13
16
  # Represents a logical unit of work in the system. Each trace consists of one or more spans.
@@ -24,24 +27,26 @@ module Datadog
24
27
  # The max value for a \Span identifier.
25
28
  # Span and trace identifiers should be strictly positive and strictly inferior to this limit.
26
29
  #
27
- # Limited to 63-bit positive integers, as some other languages might be limited to this,
28
- # and IDs need to be easy to port across various languages and platforms.
29
- MAX_ID = 2**63
30
+ # Limited to +2<<62-1+ positive integers, as Ruby is able to represent such numbers "inline",
31
+ # inside a +VALUE+ scalar, thus not requiring memory allocation.
32
+ #
33
+ # The range of IDs also has to consider portability across different languages and platforms.
34
+ RUBY_MAX_ID = (1 << 62) - 1
30
35
 
31
36
  # While we only generate 63-bit integers due to limitations in other languages, we support
32
37
  # parsing 64-bit integers for distributed tracing since an upstream system may generate one
33
- EXTERNAL_MAX_ID = 2**64
38
+ EXTERNAL_MAX_ID = 1 << 64
34
39
 
35
40
  # This limit is for numeric tags because uint64 could end up rounded.
36
- NUMERIC_TAG_SIZE_RANGE = (-2**53..2**53)
41
+ NUMERIC_TAG_SIZE_RANGE = (-1 << 53..1 << 53)
37
42
 
38
43
  attr_accessor :name, :service, :resource, :span_type,
39
- :start_time, :end_time,
40
44
  :span_id, :trace_id, :parent_id,
41
45
  :status, :sampled,
42
- :tracer, :context
46
+ :tracer, :context, :duration, :start_time, :end_time
43
47
 
44
48
  attr_reader :parent
49
+
45
50
  # Create a new span linked to the given tracer. Call the \Tracer method <tt>start_span()</tt>
46
51
  # and then <tt>finish()</tt> once the tracer operation is over.
47
52
  #
@@ -72,11 +77,20 @@ module Datadog
72
77
  @parent = nil
73
78
  @sampled = true
74
79
 
75
- @start_time = nil # set by Tracer.start_span
76
- @end_time = nil # set by Span.finish
77
-
78
80
  @allocation_count_start = now_allocations
79
81
  @allocation_count_finish = @allocation_count_start
82
+
83
+ # start_time and end_time track wall clock. In Ruby, wall clock
84
+ # has less accuracy than monotonic clock, so if possible we look to only use wall clock
85
+ # to measure duration when a time is supplied by the user, or if monotonic clock
86
+ # is unsupported.
87
+ @start_time = nil
88
+ @end_time = nil
89
+
90
+ # duration_start and duration_end track monotonic clock, and may remain nil in cases where it
91
+ # is known that we have to use wall clock to measure duration.
92
+ @duration_start = nil
93
+ @duration_end = nil
80
94
  end
81
95
 
82
96
  # Set the given key / value tag pair on the span. Keys and values
@@ -107,6 +121,16 @@ module Datadog
107
121
  Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}")
108
122
  end
109
123
 
124
+ # Sets tags from given hash, for each key in hash it sets the tag with that key
125
+ # and associated value from the hash. It is shortcut for `set_tag`. Keys and values
126
+ # of the hash must be strings. Note that nested hashes are not supported.
127
+ # A valid example is:
128
+ #
129
+ # span.set_tags({ "http.method" => "GET", "user.id" => "234" })
130
+ def set_tags(tags)
131
+ tags.each { |k, v| set_tag(k, v) }
132
+ end
133
+
110
134
  # This method removes a tag for the given key.
111
135
  def clear_tag(key)
112
136
  @meta.delete(key)
@@ -150,6 +174,28 @@ module Datadog
150
174
  set_tag(Ext::Errors::STACK, e.backtrace) unless e.backtrace.empty?
151
175
  end
152
176
 
177
+ # Mark the span started at the current time.
178
+ def start(start_time = nil)
179
+ # A span should not be started twice. However, this is existing
180
+ # behavior and so we maintain it for backward compatibility for those
181
+ # who are using async manual instrumentation that may rely on this
182
+
183
+ @start_time = start_time || Time.now.utc
184
+ @duration_start = start_time.nil? ? duration_marker : nil
185
+
186
+ self
187
+ end
188
+
189
+ # for backwards compatibility
190
+ def start_time=(time)
191
+ time.tap { start(time) }
192
+ end
193
+
194
+ # for backwards compatibility
195
+ def end_time=(time)
196
+ time.tap { finish(time) }
197
+ end
198
+
153
199
  # Mark the span finished at the current time and submit it.
154
200
  def finish(finish_time = nil)
155
201
  # A span should not be finished twice. Note that this is not thread-safe,
@@ -160,12 +206,15 @@ module Datadog
160
206
 
161
207
  @allocation_count_finish = now_allocations
162
208
 
163
- # Provide a default start_time if unset, but this should have been set by start_span.
164
- # Using now here causes 0-duration spans, still, this is expected, as we never
165
- # explicitely say when it started.
166
- @start_time ||= Time.now.utc
209
+ now = Time.now.utc
210
+
211
+ # Provide a default start_time if unset.
212
+ # Using `now` here causes duration to be 0; this is expected
213
+ # behavior when start_time is unknown.
214
+ start(finish_time || now) unless started?
167
215
 
168
- @end_time = finish_time.nil? ? Time.now.utc : finish_time # finish this
216
+ @end_time = finish_time || now
217
+ @duration_end = finish_time.nil? ? duration_marker : nil
169
218
 
170
219
  # Finish does not really do anything if the span is not bound to a tracer and a context.
171
220
  return self if @tracer.nil? || @context.nil?
@@ -185,11 +234,6 @@ module Datadog
185
234
  self
186
235
  end
187
236
 
188
- # Return whether the span is finished or not.
189
- def finished?
190
- !@end_time.nil?
191
- end
192
-
193
237
  # Return a string representation of the span.
194
238
  def to_s
195
239
  "Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})"
@@ -236,19 +280,76 @@ module Datadog
236
280
  error: @status
237
281
  }
238
282
 
239
- if !@start_time.nil? && !@end_time.nil?
240
- h[:start] = (@start_time.to_f * 1e9).to_i
241
- h[:duration] = ((@end_time - @start_time) * 1e9).to_i
283
+ if finished?
284
+ h[:start] = start_time_nano
285
+ h[:duration] = duration_nano
242
286
  end
243
287
 
244
288
  h
245
289
  end
246
290
 
291
+ # MessagePack serializer interface. Making this object
292
+ # respond to `#to_msgpack` allows it to be automatically
293
+ # serialized by MessagePack.
294
+ #
295
+ # This is more efficient than doing +MessagePack.pack(span.to_hash)+
296
+ # as we don't have to create an intermediate Hash.
297
+ #
298
+ # @param packer [MessagePack::Packer] serialization buffer, can be +nil+ with JRuby
299
+ def to_msgpack(packer = nil)
300
+ # As of 1.3.3, JRuby implementation doesn't pass an existing packer
301
+ packer ||= MessagePack::Packer.new
302
+
303
+ if finished?
304
+ packer.write_map_header(13) # Set header with how many elements in the map
305
+
306
+ packer.write('start')
307
+ packer.write(start_time_nano)
308
+
309
+ packer.write('duration')
310
+ packer.write(duration_nano)
311
+ else
312
+ packer.write_map_header(11) # Set header with how many elements in the map
313
+ end
314
+
315
+ # DEV: We use strings as keys here, instead of symbols, as
316
+ # DEV: MessagePack will ultimately convert them to strings.
317
+ # DEV: By providing strings directly, we skip this indirection operation.
318
+ packer.write('span_id')
319
+ packer.write(@span_id)
320
+ packer.write('parent_id')
321
+ packer.write(@parent_id)
322
+ packer.write('trace_id')
323
+ packer.write(@trace_id)
324
+ packer.write('name')
325
+ packer.write(@name)
326
+ packer.write('service')
327
+ packer.write(@service)
328
+ packer.write('resource')
329
+ packer.write(@resource)
330
+ packer.write('type')
331
+ packer.write(@span_type)
332
+ packer.write('meta')
333
+ packer.write(@meta)
334
+ packer.write('metrics')
335
+ packer.write(@metrics)
336
+ packer.write('allocations')
337
+ packer.write(allocations)
338
+ packer.write('error')
339
+ packer.write(@status)
340
+ packer
341
+ end
342
+
343
+ # JSON serializer interface.
344
+ # Used by older version of the transport.
345
+ def to_json(*args)
346
+ to_hash.to_json(*args)
347
+ end
348
+
247
349
  # Return a human readable version of the span
248
350
  def pretty_print(q)
249
- start_time = (@start_time.to_f * 1e9).to_i rescue '-'
250
- end_time = (@end_time.to_f * 1e9).to_i rescue '-'
251
- duration = ((@end_time - @start_time) * 1e9).to_i rescue 0
351
+ start_time = (self.start_time.to_f * 1e9).to_i
352
+ end_time = (self.end_time.to_f * 1e9).to_i
252
353
  q.group 0 do
253
354
  q.breakable
254
355
  q.text "Name: #{@name}\n"
@@ -261,7 +362,7 @@ module Datadog
261
362
  q.text "Error: #{@status}\n"
262
363
  q.text "Start: #{start_time}\n"
263
364
  q.text "End: #{end_time}\n"
264
- q.text "Duration: #{duration}\n"
365
+ q.text "Duration: #{duration.to_f if finished?}\n"
265
366
  q.text "Allocations: #{allocations}\n"
266
367
  q.group(2, 'Tags: [', "]\n") do
267
368
  q.breakable
@@ -278,8 +379,30 @@ module Datadog
278
379
  end
279
380
  end
280
381
 
382
+ # Return whether the duration is started or not
383
+ def started?
384
+ !@start_time.nil?
385
+ end
386
+
387
+ # Return whether the duration is finished or not.
388
+ def finished?
389
+ !@end_time.nil?
390
+ end
391
+
392
+ def duration
393
+ if @duration_end.nil? || @duration_start.nil?
394
+ @end_time - @start_time
395
+ else
396
+ @duration_end - @duration_start
397
+ end
398
+ end
399
+
281
400
  private
282
401
 
402
+ def duration_marker
403
+ Utils::Time.get_time
404
+ end
405
+
283
406
  if defined?(JRUBY_VERSION) || Gem::Version.new(RUBY_VERSION) < Gem::Version.new(VERSION::MINIMUM_RUBY_VERSION)
284
407
  def now_allocations
285
408
  0
@@ -293,5 +416,17 @@ module Datadog
293
416
  GC.stat(:total_allocated_objects)
294
417
  end
295
418
  end
419
+
420
+ # Used for serialization
421
+ # @return [Integer] in nanoseconds since Epoch
422
+ def start_time_nano
423
+ @start_time.to_i * 1000000000 + @start_time.nsec
424
+ end
425
+
426
+ # Used for serialization
427
+ # @return [Integer] in nanoseconds since Epoch
428
+ def duration_nano
429
+ (duration * 1e9).to_i
430
+ end
296
431
  end
297
432
  end
@@ -1,4 +1,3 @@
1
- require 'pp'
2
1
  require 'thread'
3
2
  require 'logger'
4
3
  require 'pathname'
@@ -60,8 +59,8 @@ module Datadog
60
59
  #
61
60
  # This method makes use of a \ContextProvider that is automatically set during the tracer
62
61
  # initialization, or while using a library instrumentation.
63
- def call_context
64
- @provider.context
62
+ def call_context(key = nil)
63
+ @provider.context(key)
65
64
  end
66
65
 
67
66
  # Initialize a new \Tracer used to create, sample and submit spans that measure the
@@ -186,8 +185,7 @@ module Datadog
186
185
  # * +start_time+: when the span actually starts (defaults to \now)
187
186
  # * +tags+: extra tags which should be added to the span.
188
187
  def start_span(name, options = {})
189
- start_time = options.fetch(:start_time, Time.now.utc)
190
-
188
+ start_time = options[:start_time]
191
189
  tags = options.fetch(:tags, {})
192
190
 
193
191
  span_options = options.select do |k, _v|
@@ -212,9 +210,10 @@ module Datadog
212
210
  # child span
213
211
  span.parent = parent # sets service, trace_id, parent_id, sampled
214
212
  end
215
- @tags.each { |k, v| span.set_tag(k, v) } unless @tags.empty?
216
- tags.each { |k, v| span.set_tag(k, v) } unless tags.empty?
217
- span.start_time = start_time
213
+
214
+ span.set_tags(@tags) unless @tags.empty?
215
+ span.set_tags(tags) unless tags.empty?
216
+ span.start(start_time)
218
217
 
219
218
  # this could at some point be optional (start_active_span vs start_manual_span)
220
219
  ctx.add_span(span) unless ctx.nil?
@@ -255,9 +254,11 @@ module Datadog
255
254
  # * +service+: the service name for this span
256
255
  # * +resource+: the resource this span refers, or \name if it's missing
257
256
  # * +span_type+: the type of the span (such as \http, \db and so on)
257
+ # * +child_of+: a \Span or a \Context instance representing the parent for this span.
258
+ # If not set, defaults to Tracer.call_context
258
259
  # * +tags+: extra tags which should be added to the span.
259
260
  def trace(name, options = {})
260
- options[:child_of] = call_context
261
+ options[:child_of] ||= call_context
261
262
 
262
263
  # call the finish only if a block is given; this ensures
263
264
  # that a call to tracer.trace() without a block, returns
@@ -269,11 +270,16 @@ module Datadog
269
270
  begin
270
271
  begin
271
272
  span = start_span(name, options)
272
- # rubocop:disable Lint/UselessAssignment
273
273
  rescue StandardError => e
274
- Datadog.logger.debug('Failed to start span: #{e}')
274
+ Datadog.logger.debug("Failed to start span: #{e}")
275
275
  ensure
276
- return_value = yield(span)
276
+ # We should yield to the provided block when possible, as this
277
+ # block is application code that we don't want to hinder. We call:
278
+ # * `yield(span)` during normal execution.
279
+ # * `yield(nil)` if `start_span` fails with a runtime error.
280
+ # * We don't yield during a fatal error, as the application is likely trying to
281
+ # end its execution (either due to a system error or graceful shutdown).
282
+ return_value = yield(span) if span || e.is_a?(StandardError)
277
283
  end
278
284
  # rubocop:disable Lint/RescueException
279
285
  # Here we really want to catch *any* exception, not only StandardError,
@@ -318,18 +324,18 @@ module Datadog
318
324
  end
319
325
 
320
326
  # Return the current active span or +nil+.
321
- def active_span
322
- call_context.current_span
327
+ def active_span(key = nil)
328
+ call_context(key).current_span
323
329
  end
324
330
 
325
331
  # Return the current active root span or +nil+.
326
- def active_root_span
327
- call_context.current_root_span
332
+ def active_root_span(key = nil)
333
+ call_context(key).current_root_span
328
334
  end
329
335
 
330
336
  # Return a CorrelationIdentifier for active span
331
- def active_correlation
332
- Datadog::Correlation.identifier_from_context(call_context)
337
+ def active_correlation(key = nil)
338
+ Datadog::Correlation.identifier_from_context(call_context(key))
333
339
  end
334
340
 
335
341
  # Send the trace to the writer to enqueue the spans list in the agent