ddtrace 0.36.0 → 0.41.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 (197) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +59 -1
  3. data/.gitignore +2 -0
  4. data/.gitlab-ci.yml +27 -0
  5. data/.simplecov +38 -0
  6. data/Appraisals +131 -6
  7. data/CHANGELOG.md +198 -1
  8. data/Rakefile +504 -467
  9. data/ddtrace.gemspec +7 -0
  10. data/docker-compose.yml +2 -2
  11. data/docs/DevelopmentGuide.md +16 -0
  12. data/docs/GettingStarted.md +192 -111
  13. data/lib/ddtrace.rb +4 -0
  14. data/lib/ddtrace/buffer.rb +154 -43
  15. data/lib/ddtrace/configuration.rb +39 -5
  16. data/lib/ddtrace/configuration/components.rb +4 -7
  17. data/lib/ddtrace/configuration/options.rb +3 -1
  18. data/lib/ddtrace/configuration/pin_setup.rb +3 -2
  19. data/lib/ddtrace/configuration/settings.rb +32 -4
  20. data/lib/ddtrace/contrib/action_cable/configuration/settings.rb +7 -2
  21. data/lib/ddtrace/contrib/action_cable/ext.rb +5 -2
  22. data/lib/ddtrace/contrib/action_pack/configuration/settings.rb +7 -2
  23. data/lib/ddtrace/contrib/action_pack/ext.rb +5 -2
  24. data/lib/ddtrace/contrib/action_view/configuration/settings.rb +7 -2
  25. data/lib/ddtrace/contrib/action_view/ext.rb +5 -2
  26. data/lib/ddtrace/contrib/active_model_serializers/configuration/settings.rb +7 -2
  27. data/lib/ddtrace/contrib/active_model_serializers/ext.rb +5 -2
  28. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +7 -2
  29. data/lib/ddtrace/contrib/active_record/events/sql.rb +4 -0
  30. data/lib/ddtrace/contrib/active_record/ext.rb +5 -2
  31. data/lib/ddtrace/contrib/active_support/cache/redis.rb +1 -1
  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/event.rb +3 -1
  35. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +3 -3
  36. data/lib/ddtrace/contrib/aws/configuration/settings.rb +7 -2
  37. data/lib/ddtrace/contrib/aws/ext.rb +5 -2
  38. data/lib/ddtrace/contrib/aws/instrumentation.rb +4 -0
  39. data/lib/ddtrace/contrib/concurrent_ruby/configuration/settings.rb +5 -0
  40. data/lib/ddtrace/contrib/concurrent_ruby/context_composite_executor_service.rb +9 -3
  41. data/lib/ddtrace/contrib/concurrent_ruby/ext.rb +1 -0
  42. data/lib/ddtrace/contrib/configuration/settings.rb +19 -0
  43. data/lib/ddtrace/contrib/dalli/configuration/settings.rb +7 -2
  44. data/lib/ddtrace/contrib/dalli/ext.rb +5 -2
  45. data/lib/ddtrace/contrib/dalli/instrumentation.rb +4 -0
  46. data/lib/ddtrace/contrib/dalli/patcher.rb +1 -5
  47. data/lib/ddtrace/contrib/delayed_job/configuration/settings.rb +7 -2
  48. data/lib/ddtrace/contrib/delayed_job/ext.rb +5 -2
  49. data/lib/ddtrace/contrib/elasticsearch/configuration/settings.rb +7 -2
  50. data/lib/ddtrace/contrib/elasticsearch/ext.rb +5 -2
  51. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +5 -2
  52. data/lib/ddtrace/contrib/ethon/configuration/settings.rb +7 -2
  53. data/lib/ddtrace/contrib/ethon/easy_patch.rb +4 -2
  54. data/lib/ddtrace/contrib/ethon/ext.rb +5 -2
  55. data/lib/ddtrace/contrib/ethon/multi_patch.rb +4 -0
  56. data/lib/ddtrace/contrib/excon/configuration/settings.rb +7 -2
  57. data/lib/ddtrace/contrib/excon/ext.rb +5 -2
  58. data/lib/ddtrace/contrib/excon/middleware.rb +4 -0
  59. data/lib/ddtrace/contrib/extensions.rb +11 -1
  60. data/lib/ddtrace/contrib/faraday/configuration/settings.rb +7 -2
  61. data/lib/ddtrace/contrib/faraday/ext.rb +5 -2
  62. data/lib/ddtrace/contrib/faraday/middleware.rb +9 -3
  63. data/lib/ddtrace/contrib/faraday/patcher.rb +13 -5
  64. data/lib/ddtrace/contrib/grape/configuration/settings.rb +7 -3
  65. data/lib/ddtrace/contrib/grape/endpoint.rb +6 -4
  66. data/lib/ddtrace/contrib/grape/ext.rb +5 -2
  67. data/lib/ddtrace/contrib/grape/patcher.rb +1 -1
  68. data/lib/ddtrace/contrib/graphql/configuration/settings.rb +7 -2
  69. data/lib/ddtrace/contrib/graphql/ext.rb +5 -2
  70. data/lib/ddtrace/contrib/graphql/patcher.rb +6 -3
  71. data/lib/ddtrace/contrib/grpc/configuration/settings.rb +7 -2
  72. data/lib/ddtrace/contrib/grpc/datadog_interceptor.rb +1 -1
  73. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +5 -3
  74. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +4 -0
  75. data/lib/ddtrace/contrib/grpc/ext.rb +5 -2
  76. data/lib/ddtrace/contrib/grpc/patcher.rb +1 -5
  77. data/lib/ddtrace/contrib/http/configuration/settings.rb +7 -2
  78. data/lib/ddtrace/contrib/http/ext.rb +5 -2
  79. data/lib/ddtrace/contrib/http/instrumentation.rb +16 -7
  80. data/lib/ddtrace/contrib/httprb/configuration/settings.rb +32 -0
  81. data/lib/ddtrace/contrib/httprb/ext.rb +17 -0
  82. data/lib/ddtrace/contrib/httprb/instrumentation.rb +163 -0
  83. data/lib/ddtrace/contrib/httprb/integration.rb +43 -0
  84. data/lib/ddtrace/contrib/httprb/patcher.rb +35 -0
  85. data/lib/ddtrace/contrib/kafka/configuration/settings.rb +30 -0
  86. data/lib/ddtrace/contrib/kafka/consumer_event.rb +14 -0
  87. data/lib/ddtrace/contrib/kafka/consumer_group_event.rb +14 -0
  88. data/lib/ddtrace/contrib/kafka/event.rb +51 -0
  89. data/lib/ddtrace/contrib/kafka/events.rb +44 -0
  90. data/lib/ddtrace/contrib/kafka/events/connection/request.rb +34 -0
  91. data/lib/ddtrace/contrib/kafka/events/consumer/process_batch.rb +41 -0
  92. data/lib/ddtrace/contrib/kafka/events/consumer/process_message.rb +39 -0
  93. data/lib/ddtrace/contrib/kafka/events/consumer_group/heartbeat.rb +39 -0
  94. data/lib/ddtrace/contrib/kafka/events/consumer_group/join_group.rb +29 -0
  95. data/lib/ddtrace/contrib/kafka/events/consumer_group/leave_group.rb +29 -0
  96. data/lib/ddtrace/contrib/kafka/events/consumer_group/sync_group.rb +29 -0
  97. data/lib/ddtrace/contrib/kafka/events/produce_operation/send_messages.rb +32 -0
  98. data/lib/ddtrace/contrib/kafka/events/producer/deliver_messages.rb +35 -0
  99. data/lib/ddtrace/contrib/kafka/ext.rb +41 -0
  100. data/lib/ddtrace/contrib/kafka/integration.rb +39 -0
  101. data/lib/ddtrace/contrib/kafka/patcher.rb +26 -0
  102. data/lib/ddtrace/contrib/mongodb/configuration/settings.rb +7 -2
  103. data/lib/ddtrace/contrib/mongodb/ext.rb +5 -2
  104. data/lib/ddtrace/contrib/mongodb/instrumentation.rb +1 -2
  105. data/lib/ddtrace/contrib/mongodb/subscribers.rb +4 -0
  106. data/lib/ddtrace/contrib/mysql2/configuration/settings.rb +7 -2
  107. data/lib/ddtrace/contrib/mysql2/ext.rb +5 -2
  108. data/lib/ddtrace/contrib/mysql2/instrumentation.rb +5 -1
  109. data/lib/ddtrace/contrib/patcher.rb +14 -8
  110. data/lib/ddtrace/contrib/presto/configuration/settings.rb +7 -2
  111. data/lib/ddtrace/contrib/presto/ext.rb +5 -2
  112. data/lib/ddtrace/contrib/presto/instrumentation.rb +3 -0
  113. data/lib/ddtrace/contrib/que/configuration/settings.rb +42 -0
  114. data/lib/ddtrace/contrib/que/ext.rb +30 -0
  115. data/lib/ddtrace/contrib/que/integration.rb +42 -0
  116. data/lib/ddtrace/contrib/que/patcher.rb +24 -0
  117. data/lib/ddtrace/contrib/que/tracer.rb +56 -0
  118. data/lib/ddtrace/contrib/racecar/configuration/settings.rb +7 -2
  119. data/lib/ddtrace/contrib/racecar/event.rb +4 -0
  120. data/lib/ddtrace/contrib/racecar/events.rb +2 -0
  121. data/lib/ddtrace/contrib/racecar/events/consume.rb +27 -0
  122. data/lib/ddtrace/contrib/racecar/ext.rb +6 -2
  123. data/lib/ddtrace/contrib/rack/configuration/settings.rb +7 -2
  124. data/lib/ddtrace/contrib/rack/ext.rb +5 -2
  125. data/lib/ddtrace/contrib/rack/middlewares.rb +17 -12
  126. data/lib/ddtrace/contrib/rails/configuration/settings.rb +10 -12
  127. data/lib/ddtrace/contrib/rails/ext.rb +6 -2
  128. data/lib/ddtrace/contrib/rails/framework.rb +14 -21
  129. data/lib/ddtrace/contrib/rails/log_injection.rb +81 -0
  130. data/lib/ddtrace/contrib/rails/middlewares.rb +7 -2
  131. data/lib/ddtrace/contrib/rails/patcher.rb +15 -0
  132. data/lib/ddtrace/contrib/rake/configuration/settings.rb +7 -3
  133. data/lib/ddtrace/contrib/rake/ext.rb +5 -2
  134. data/lib/ddtrace/contrib/redis/configuration/settings.rb +7 -2
  135. data/lib/ddtrace/contrib/redis/ext.rb +5 -2
  136. data/lib/ddtrace/contrib/redis/patcher.rb +1 -1
  137. data/lib/ddtrace/contrib/redis/tags.rb +4 -0
  138. data/lib/ddtrace/contrib/resque/configuration/settings.rb +7 -2
  139. data/lib/ddtrace/contrib/resque/ext.rb +5 -2
  140. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +7 -2
  141. data/lib/ddtrace/contrib/rest_client/ext.rb +5 -2
  142. data/lib/ddtrace/contrib/rest_client/request_patch.rb +6 -2
  143. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +7 -2
  144. data/lib/ddtrace/contrib/sequel/database.rb +4 -2
  145. data/lib/ddtrace/contrib/sequel/dataset.rb +3 -2
  146. data/lib/ddtrace/contrib/sequel/ext.rb +6 -2
  147. data/lib/ddtrace/contrib/sequel/utils.rb +35 -6
  148. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +7 -2
  149. data/lib/ddtrace/contrib/shoryuken/ext.rb +5 -2
  150. data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +7 -2
  151. data/lib/ddtrace/contrib/sidekiq/ext.rb +6 -2
  152. data/lib/ddtrace/contrib/sidekiq/patcher.rb +8 -1
  153. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +1 -0
  154. data/lib/ddtrace/contrib/sinatra/configuration/settings.rb +7 -2
  155. data/lib/ddtrace/contrib/sinatra/env.rb +5 -4
  156. data/lib/ddtrace/contrib/sinatra/ext.rb +5 -2
  157. data/lib/ddtrace/contrib/sinatra/tracer.rb +21 -42
  158. data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +50 -23
  159. data/lib/ddtrace/contrib/sneakers/configuration/settings.rb +32 -0
  160. data/lib/ddtrace/contrib/sneakers/ext.rb +22 -0
  161. data/lib/ddtrace/contrib/sneakers/integration.rb +41 -0
  162. data/lib/ddtrace/contrib/sneakers/patcher.rb +24 -0
  163. data/lib/ddtrace/contrib/sneakers/tracer.rb +58 -0
  164. data/lib/ddtrace/contrib/sucker_punch/configuration/settings.rb +7 -2
  165. data/lib/ddtrace/contrib/sucker_punch/ext.rb +5 -2
  166. data/lib/ddtrace/contrib/sucker_punch/patcher.rb +1 -1
  167. data/lib/ddtrace/diagnostics/environment_logger.rb +278 -0
  168. data/lib/ddtrace/environment.rb +17 -3
  169. data/lib/ddtrace/ext/diagnostics.rb +3 -0
  170. data/lib/ddtrace/ext/environment.rb +2 -0
  171. data/lib/ddtrace/ext/integration.rb +8 -0
  172. data/lib/ddtrace/ext/runtime.rb +1 -0
  173. data/lib/ddtrace/ext/transport.rb +1 -0
  174. data/lib/ddtrace/logger.rb +1 -1
  175. data/lib/ddtrace/opentracer/distributed_headers.rb +1 -1
  176. data/lib/ddtrace/pin.rb +25 -2
  177. data/lib/ddtrace/pipeline/span_filter.rb +15 -15
  178. data/lib/ddtrace/propagation/grpc_propagator.rb +2 -2
  179. data/lib/ddtrace/runtime/metrics.rb +24 -6
  180. data/lib/ddtrace/sampler.rb +4 -2
  181. data/lib/ddtrace/span.rb +162 -27
  182. data/lib/ddtrace/tracer.rb +18 -12
  183. data/lib/ddtrace/transport/http.rb +15 -0
  184. data/lib/ddtrace/transport/http/adapters/net.rb +16 -2
  185. data/lib/ddtrace/transport/http/adapters/test.rb +6 -0
  186. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +4 -0
  187. data/lib/ddtrace/transport/http/statistics.rb +14 -1
  188. data/lib/ddtrace/transport/response.rb +11 -0
  189. data/lib/ddtrace/transport/traces.rb +7 -2
  190. data/lib/ddtrace/utils.rb +7 -3
  191. data/lib/ddtrace/version.rb +1 -1
  192. data/lib/ddtrace/workers/async.rb +2 -2
  193. data/lib/ddtrace/workers/loop.rb +1 -1
  194. data/lib/ddtrace/workers/polling.rb +1 -1
  195. data/lib/ddtrace/workers/trace_writer.rb +3 -0
  196. data/lib/ddtrace/writer.rb +33 -12
  197. metadata +138 -2
@@ -1,8 +1,10 @@
1
1
  module Datadog
2
2
  module Ext
3
3
  module Environment
4
+ ENV_API_KEY = 'DD_API_KEY'.freeze
4
5
  ENV_ENVIRONMENT = 'DD_ENV'.freeze
5
6
  ENV_SERVICE = 'DD_SERVICE'.freeze
7
+ ENV_SITE = 'DD_SITE'.freeze
6
8
  ENV_TAGS = 'DD_TAGS'.freeze
7
9
  ENV_VERSION = 'DD_VERSION'.freeze
8
10
 
@@ -0,0 +1,8 @@
1
+ module Datadog
2
+ module Ext
3
+ module Integration
4
+ # Name of external service that performed the work
5
+ TAG_PEER_SERVICE = 'peer.service'.freeze
6
+ end
7
+ end
8
+ end
@@ -7,6 +7,7 @@ module Datadog
7
7
  LANG = 'ruby'.freeze
8
8
  LANG_INTERPRETER = (RUBY_ENGINE + '-' + RUBY_PLATFORM).freeze
9
9
  LANG_VERSION = RUBY_VERSION
10
+ RUBY_ENGINE = ::RUBY_ENGINE # e.g. 'ruby', 'jruby', 'truffleruby'
10
11
  TRACER_VERSION = Datadog::VERSION::STRING
11
12
 
12
13
  TAG_LANG = 'language'.freeze
@@ -6,6 +6,7 @@ module Datadog
6
6
  DEFAULT_PORT = 8126
7
7
  ENV_DEFAULT_HOST = 'DD_AGENT_HOST'.freeze
8
8
  ENV_DEFAULT_PORT = 'DD_TRACE_AGENT_PORT'.freeze
9
+ ENV_DEFAULT_URL = 'DD_TRACE_AGENT_URL'.freeze
9
10
  HEADER_CONTAINER_ID = 'Datadog-Container-ID'.freeze
10
11
  HEADER_META_LANG = 'Datadog-Meta-Lang'.freeze
11
12
  HEADER_META_LANG_VERSION = 'Datadog-Meta-Lang-Version'.freeze
@@ -10,7 +10,7 @@ module Datadog
10
10
  def initialize(*args, &block)
11
11
  super
12
12
  self.progname = PREFIX
13
- self.level = ::Logger::WARN
13
+ self.level = ::Logger::INFO
14
14
  end
15
15
 
16
16
  def add(severity, message = nil, progname = nil, &block)
@@ -44,7 +44,7 @@ module Datadog
44
44
 
45
45
  def id(header)
46
46
  value = @carrier[header].to_i
47
- return if value.zero? || value >= Datadog::Span::MAX_ID
47
+ return if value.zero? || value >= Datadog::Span::EXTERNAL_MAX_ID
48
48
  value < 0 ? value + 0x1_0000_0000_0000_0000 : value
49
49
  end
50
50
  end
@@ -18,13 +18,15 @@ module Datadog
18
18
  attr_accessor :name
19
19
  attr_accessor :service_name
20
20
  attr_accessor :tags
21
- attr_writer :tracer
21
+ attr_reader :tracer
22
22
  attr_accessor :writer
23
23
 
24
24
  alias service= service_name=
25
25
  alias service service_name
26
26
 
27
27
  def initialize(service_name, options = {})
28
+ deprecation_warning unless options[:tracer].is_a?(Proc) || options[:tracer].nil?
29
+
28
30
  @app = options[:app]
29
31
  @app_type = options[:app_type]
30
32
  @config = options[:config]
@@ -35,7 +37,7 @@ module Datadog
35
37
  end
36
38
 
37
39
  def tracer
38
- @tracer || Datadog.tracer
40
+ @tracer.is_a?(Proc) ? @tracer.call : (@tracer || Datadog.tracer)
39
41
  end
40
42
 
41
43
  def enabled?
@@ -67,6 +69,27 @@ module Datadog
67
69
  def to_s
68
70
  "Pin(service:#{service},app:#{app},app_type:#{app_type},name:#{name})"
69
71
  end
72
+
73
+ private
74
+
75
+ DEPRECATION_WARNING = %(
76
+ Explicitly providing a tracer instance is DEPRECATED.
77
+ It's recommended to not provide an explicit tracer instance
78
+ and let Datadog::Pin resolve the correct tracer internally.
79
+ ).freeze
80
+
81
+ def deprecation_warning
82
+ log_deprecation_warning('Datadog::Pin.new')
83
+ end
84
+
85
+ include Datadog::Patcher
86
+
87
+ def log_deprecation_warning(method_name)
88
+ # Only log each deprecation warning once (safeguard against log spam)
89
+ do_once(method_name) do
90
+ Datadog.logger.warn("#{method_name}:#{DEPRECATION_WARNING}")
91
+ end
92
+ end
70
93
  end
71
94
 
72
95
  # Modification to Pin which logs deprecation warnings if accessed.
@@ -10,12 +10,22 @@ module Datadog
10
10
  @criteria = filter || block
11
11
  end
12
12
 
13
+ # Note: this SpanFilter implementation only handles traces in which child spans appear
14
+ # after parent spans in the trace array. If in the future child spans can be before
15
+ # parent spans, then the code below will need to be updated.
13
16
  def call(trace)
14
- black_list = trace.select(&method(:drop_it?))
15
-
16
- clean_trace(black_list, trace) while black_list.any?
17
-
18
- trace
17
+ deleted = Set.new
18
+
19
+ trace.delete_if do |span|
20
+ if deleted.include?(span.parent)
21
+ deleted << span
22
+ true
23
+ else
24
+ drop = drop_it?(span)
25
+ deleted << span if drop
26
+ drop
27
+ end
28
+ end
19
29
  end
20
30
 
21
31
  private
@@ -23,16 +33,6 @@ module Datadog
23
33
  def drop_it?(span)
24
34
  @criteria.call(span) rescue false
25
35
  end
26
-
27
- def clean_trace(black_list, trace)
28
- current = black_list.shift
29
-
30
- trace.delete(current)
31
-
32
- trace.each do |span|
33
- black_list << span if span.parent == current
34
- end
35
- end
36
36
  end
37
37
  end
38
38
  end
@@ -39,12 +39,12 @@ module Datadog
39
39
 
40
40
  def trace_id
41
41
  value = @metadata[GRPC_METADATA_TRACE_ID].to_i
42
- value if (1..Span::MAX_ID).cover? value
42
+ value if (1..Span::EXTERNAL_MAX_ID).cover? value
43
43
  end
44
44
 
45
45
  def parent_id
46
46
  value = @metadata[GRPC_METADATA_PARENT_ID].to_i
47
- value if (1..Span::MAX_ID).cover? value
47
+ value if (1..Span::EXTERNAL_MAX_ID).cover? value
48
48
  end
49
49
 
50
50
  def sampling_priority
@@ -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