ddtrace 0.38.0 → 0.43.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +56 -0
  3. data/.github/workflows/add-milestone-to-pull-requests.yml +42 -0
  4. data/.github/workflows/create-next-milestone.yml +20 -0
  5. data/.gitlab-ci.yml +1 -0
  6. data/.simplecov +38 -0
  7. data/Appraisals +293 -105
  8. data/CHANGELOG.md +192 -1
  9. data/CONTRIBUTING.md +2 -2
  10. data/Rakefile +554 -480
  11. data/ddtrace.gemspec +3 -0
  12. data/docs/DevelopmentGuide.md +28 -2
  13. data/docs/GettingStarted.md +207 -82
  14. data/lib/ddtrace.rb +4 -0
  15. data/lib/ddtrace/buffer.rb +259 -52
  16. data/lib/ddtrace/configuration.rb +55 -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 +18 -6
  20. data/lib/ddtrace/context.rb +18 -0
  21. data/lib/ddtrace/context_provider.rb +17 -5
  22. data/lib/ddtrace/contrib/action_cable/configuration/settings.rb +7 -2
  23. data/lib/ddtrace/contrib/action_cable/ext.rb +5 -2
  24. data/lib/ddtrace/contrib/action_pack/configuration/settings.rb +7 -2
  25. data/lib/ddtrace/contrib/action_pack/ext.rb +5 -2
  26. data/lib/ddtrace/contrib/action_view/configuration/settings.rb +7 -2
  27. data/lib/ddtrace/contrib/action_view/ext.rb +5 -2
  28. data/lib/ddtrace/contrib/active_model_serializers/configuration/settings.rb +7 -2
  29. data/lib/ddtrace/contrib/active_model_serializers/ext.rb +5 -2
  30. data/lib/ddtrace/contrib/active_record/configuration/settings.rb +7 -2
  31. data/lib/ddtrace/contrib/active_record/events/sql.rb +4 -0
  32. data/lib/ddtrace/contrib/active_record/ext.rb +5 -2
  33. data/lib/ddtrace/contrib/active_support/cache/instrumentation.rb +104 -3
  34. data/lib/ddtrace/contrib/active_support/cache/patcher.rb +21 -0
  35. data/lib/ddtrace/contrib/active_support/configuration/settings.rb +7 -2
  36. data/lib/ddtrace/contrib/active_support/ext.rb +8 -2
  37. data/lib/ddtrace/contrib/active_support/notifications/event.rb +10 -0
  38. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -2
  39. data/lib/ddtrace/contrib/aws/configuration/settings.rb +7 -2
  40. data/lib/ddtrace/contrib/aws/ext.rb +5 -2
  41. data/lib/ddtrace/contrib/aws/instrumentation.rb +6 -1
  42. data/lib/ddtrace/contrib/aws/patcher.rb +0 -1
  43. data/lib/ddtrace/contrib/concurrent_ruby/configuration/settings.rb +5 -0
  44. data/lib/ddtrace/contrib/concurrent_ruby/ext.rb +1 -0
  45. data/lib/ddtrace/contrib/configurable.rb +2 -0
  46. data/lib/ddtrace/contrib/configuration/resolvers/pattern_resolver.rb +4 -5
  47. data/lib/ddtrace/contrib/configuration/settings.rb +1 -0
  48. data/lib/ddtrace/contrib/cucumber/configuration/settings.rb +38 -0
  49. data/lib/ddtrace/contrib/cucumber/ext.rb +19 -0
  50. data/lib/ddtrace/contrib/cucumber/formatter.rb +104 -0
  51. data/lib/ddtrace/contrib/cucumber/instrumentation.rb +24 -0
  52. data/lib/ddtrace/contrib/cucumber/integration.rb +40 -0
  53. data/lib/ddtrace/contrib/cucumber/patcher.rb +23 -0
  54. data/lib/ddtrace/contrib/dalli/configuration/settings.rb +7 -2
  55. data/lib/ddtrace/contrib/dalli/ext.rb +5 -2
  56. data/lib/ddtrace/contrib/dalli/instrumentation.rb +4 -0
  57. data/lib/ddtrace/contrib/delayed_job/configuration/settings.rb +9 -2
  58. data/lib/ddtrace/contrib/delayed_job/ext.rb +7 -2
  59. data/lib/ddtrace/contrib/delayed_job/plugin.rb +39 -15
  60. data/lib/ddtrace/contrib/elasticsearch/configuration/settings.rb +7 -2
  61. data/lib/ddtrace/contrib/elasticsearch/ext.rb +5 -2
  62. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +4 -0
  63. data/lib/ddtrace/contrib/ethon/configuration/settings.rb +7 -2
  64. data/lib/ddtrace/contrib/ethon/easy_patch.rb +4 -2
  65. data/lib/ddtrace/contrib/ethon/ext.rb +5 -2
  66. data/lib/ddtrace/contrib/ethon/multi_patch.rb +4 -0
  67. data/lib/ddtrace/contrib/excon/configuration/settings.rb +7 -2
  68. data/lib/ddtrace/contrib/excon/ext.rb +5 -2
  69. data/lib/ddtrace/contrib/excon/middleware.rb +11 -1
  70. data/lib/ddtrace/contrib/extensions.rb +1 -1
  71. data/lib/ddtrace/contrib/faraday/configuration/settings.rb +7 -2
  72. data/lib/ddtrace/contrib/faraday/ext.rb +5 -2
  73. data/lib/ddtrace/contrib/faraday/middleware.rb +4 -0
  74. data/lib/ddtrace/contrib/faraday/patcher.rb +13 -4
  75. data/lib/ddtrace/contrib/grape/configuration/settings.rb +14 -3
  76. data/lib/ddtrace/contrib/grape/endpoint.rb +24 -7
  77. data/lib/ddtrace/contrib/grape/ext.rb +5 -2
  78. data/lib/ddtrace/contrib/graphql/configuration/settings.rb +7 -2
  79. data/lib/ddtrace/contrib/graphql/ext.rb +5 -2
  80. data/lib/ddtrace/contrib/grpc/configuration/settings.rb +7 -2
  81. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +5 -1
  82. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +4 -0
  83. data/lib/ddtrace/contrib/grpc/ext.rb +5 -2
  84. data/lib/ddtrace/contrib/http/configuration/settings.rb +7 -2
  85. data/lib/ddtrace/contrib/http/ext.rb +5 -2
  86. data/lib/ddtrace/contrib/http/instrumentation.rb +6 -2
  87. data/lib/ddtrace/contrib/httprb/configuration/settings.rb +7 -2
  88. data/lib/ddtrace/contrib/httprb/ext.rb +5 -2
  89. data/lib/ddtrace/contrib/httprb/instrumentation.rb +8 -8
  90. data/lib/ddtrace/contrib/kafka/configuration/settings.rb +7 -2
  91. data/lib/ddtrace/contrib/kafka/event.rb +1 -1
  92. data/lib/ddtrace/contrib/kafka/ext.rb +5 -2
  93. data/lib/ddtrace/contrib/mongodb/configuration/settings.rb +7 -2
  94. data/lib/ddtrace/contrib/mongodb/ext.rb +5 -2
  95. data/lib/ddtrace/contrib/mongodb/subscribers.rb +4 -0
  96. data/lib/ddtrace/contrib/mysql2/configuration/settings.rb +7 -2
  97. data/lib/ddtrace/contrib/mysql2/ext.rb +5 -2
  98. data/lib/ddtrace/contrib/mysql2/instrumentation.rb +4 -0
  99. data/lib/ddtrace/contrib/presto/configuration/settings.rb +7 -2
  100. data/lib/ddtrace/contrib/presto/ext.rb +5 -2
  101. data/lib/ddtrace/contrib/presto/instrumentation.rb +3 -0
  102. data/lib/ddtrace/contrib/que/configuration/settings.rb +43 -0
  103. data/lib/ddtrace/contrib/que/ext.rb +30 -0
  104. data/lib/ddtrace/contrib/que/integration.rb +42 -0
  105. data/lib/ddtrace/contrib/que/patcher.rb +24 -0
  106. data/lib/ddtrace/contrib/que/tracer.rb +57 -0
  107. data/lib/ddtrace/contrib/racecar/configuration/settings.rb +7 -2
  108. data/lib/ddtrace/contrib/racecar/event.rb +4 -0
  109. data/lib/ddtrace/contrib/racecar/events.rb +2 -0
  110. data/lib/ddtrace/contrib/racecar/events/consume.rb +27 -0
  111. data/lib/ddtrace/contrib/racecar/ext.rb +6 -2
  112. data/lib/ddtrace/contrib/rack/configuration/settings.rb +7 -2
  113. data/lib/ddtrace/contrib/rack/ext.rb +5 -2
  114. data/lib/ddtrace/contrib/rack/middlewares.rb +2 -0
  115. data/lib/ddtrace/contrib/rails/configuration/settings.rb +12 -2
  116. data/lib/ddtrace/contrib/rails/ext.rb +6 -2
  117. data/lib/ddtrace/contrib/rails/log_injection.rb +81 -0
  118. data/lib/ddtrace/contrib/rails/middlewares.rb +7 -2
  119. data/lib/ddtrace/contrib/rails/patcher.rb +29 -0
  120. data/lib/ddtrace/contrib/rake/configuration/settings.rb +7 -3
  121. data/lib/ddtrace/contrib/rake/ext.rb +5 -2
  122. data/lib/ddtrace/contrib/redis/configuration/settings.rb +7 -2
  123. data/lib/ddtrace/contrib/redis/ext.rb +5 -2
  124. data/lib/ddtrace/contrib/redis/tags.rb +4 -0
  125. data/lib/ddtrace/contrib/resque/configuration/settings.rb +8 -2
  126. data/lib/ddtrace/contrib/resque/ext.rb +5 -2
  127. data/lib/ddtrace/contrib/resque/integration.rb +1 -1
  128. data/lib/ddtrace/contrib/resque/resque_job.rb +1 -1
  129. data/lib/ddtrace/contrib/rest_client/configuration/settings.rb +7 -2
  130. data/lib/ddtrace/contrib/rest_client/ext.rb +5 -2
  131. data/lib/ddtrace/contrib/rest_client/request_patch.rb +4 -0
  132. data/lib/ddtrace/contrib/rspec/configuration/settings.rb +38 -0
  133. data/lib/ddtrace/contrib/rspec/example.rb +61 -0
  134. data/lib/ddtrace/contrib/rspec/example_group.rb +61 -0
  135. data/lib/ddtrace/contrib/rspec/ext.rb +19 -0
  136. data/lib/ddtrace/contrib/rspec/integration.rb +41 -0
  137. data/lib/ddtrace/contrib/rspec/patcher.rb +25 -0
  138. data/lib/ddtrace/contrib/sequel/configuration/settings.rb +7 -2
  139. data/lib/ddtrace/contrib/sequel/database.rb +3 -1
  140. data/lib/ddtrace/contrib/sequel/dataset.rb +3 -2
  141. data/lib/ddtrace/contrib/sequel/ext.rb +6 -2
  142. data/lib/ddtrace/contrib/sequel/utils.rb +35 -6
  143. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +8 -2
  144. data/lib/ddtrace/contrib/shoryuken/ext.rb +5 -2
  145. data/lib/ddtrace/contrib/shoryuken/tracer.rb +4 -1
  146. data/lib/ddtrace/contrib/sidekiq/configuration/settings.rb +8 -2
  147. data/lib/ddtrace/contrib/sidekiq/ext.rb +5 -2
  148. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +4 -1
  149. data/lib/ddtrace/contrib/sinatra/configuration/settings.rb +7 -2
  150. data/lib/ddtrace/contrib/sinatra/env.rb +5 -4
  151. data/lib/ddtrace/contrib/sinatra/ext.rb +5 -2
  152. data/lib/ddtrace/contrib/sinatra/tracer.rb +21 -42
  153. data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +50 -23
  154. data/lib/ddtrace/contrib/sneakers/configuration/settings.rb +33 -0
  155. data/lib/ddtrace/contrib/sneakers/ext.rb +22 -0
  156. data/lib/ddtrace/contrib/sneakers/integration.rb +41 -0
  157. data/lib/ddtrace/contrib/sneakers/patcher.rb +24 -0
  158. data/lib/ddtrace/contrib/sneakers/tracer.rb +55 -0
  159. data/lib/ddtrace/contrib/status_code_matcher.rb +67 -0
  160. data/lib/ddtrace/contrib/sucker_punch/configuration/settings.rb +7 -2
  161. data/lib/ddtrace/contrib/sucker_punch/ext.rb +5 -2
  162. data/lib/ddtrace/diagnostics/environment_logger.rb +1 -1
  163. data/lib/ddtrace/environment.rb +14 -4
  164. data/lib/ddtrace/ext/app_types.rb +1 -0
  165. data/lib/ddtrace/ext/ci.rb +265 -0
  166. data/lib/ddtrace/ext/diagnostics.rb +2 -1
  167. data/lib/ddtrace/ext/distributed.rb +8 -2
  168. data/lib/ddtrace/ext/git.rb +12 -0
  169. data/lib/ddtrace/ext/integration.rb +8 -0
  170. data/lib/ddtrace/ext/runtime.rb +2 -0
  171. data/lib/ddtrace/ext/test.rb +24 -0
  172. data/lib/ddtrace/ext/transport.rb +1 -0
  173. data/lib/ddtrace/logger.rb +1 -1
  174. data/lib/ddtrace/opentracer/distributed_headers.rb +1 -1
  175. data/lib/ddtrace/propagation/grpc_propagator.rb +18 -6
  176. data/lib/ddtrace/runtime/identity.rb +4 -5
  177. data/lib/ddtrace/runtime/metrics.rb +24 -6
  178. data/lib/ddtrace/sampler.rb +2 -2
  179. data/lib/ddtrace/sampling/rate_limiter.rb +65 -16
  180. data/lib/ddtrace/span.rb +152 -27
  181. data/lib/ddtrace/tracer.rb +25 -13
  182. data/lib/ddtrace/transport/http.rb +15 -0
  183. data/lib/ddtrace/transport/http/adapters/net.rb +8 -2
  184. data/lib/ddtrace/transport/http/adapters/test.rb +2 -0
  185. data/lib/ddtrace/transport/http/statistics.rb +14 -1
  186. data/lib/ddtrace/transport/traces.rb +7 -2
  187. data/lib/ddtrace/utils.rb +16 -13
  188. data/lib/ddtrace/utils/forking.rb +52 -0
  189. data/lib/ddtrace/version.rb +1 -1
  190. data/lib/ddtrace/workers/async.rb +2 -2
  191. data/lib/ddtrace/workers/loop.rb +1 -1
  192. data/lib/ddtrace/workers/polling.rb +1 -1
  193. data/lib/ddtrace/writer.rb +19 -1
  194. metadata +53 -6
@@ -2,7 +2,8 @@ module Datadog
2
2
  module Ext
3
3
  module Diagnostics
4
4
  DD_TRACE_STARTUP_LOGS = 'DD_TRACE_STARTUP_LOGS'.freeze
5
-
5
+ DD_TRACE_DEBUG = 'DD_TRACE_DEBUG'.freeze
6
+ DD_TRACE_ENABLED = 'DD_TRACE_ENABLED'.freeze
6
7
  # Health
7
8
  module Health
8
9
  # Metrics
@@ -20,8 +20,14 @@ module Datadog
20
20
  PROPAGATION_STYLE_DATADOG = 'Datadog'.freeze
21
21
  PROPAGATION_STYLE_B3 = 'B3'.freeze
22
22
  PROPAGATION_STYLE_B3_SINGLE_HEADER = 'B3 single header'.freeze
23
- PROPAGATION_INJECT_STYLE_ENV = 'DD_PROPAGATION_INJECT_STYLE'.freeze
24
- PROPAGATION_EXTRACT_STYLE_ENV = 'DD_PROPAGATION_EXTRACT_STYLE'.freeze
23
+ PROPAGATION_STYLE_INJECT_ENV = 'DD_PROPAGATION_STYLE_INJECT'.freeze
24
+ PROPAGATION_STYLE_EXTRACT_ENV = 'DD_PROPAGATION_STYLE_EXTRACT'.freeze
25
+ # Note: the below inject/extract values are deprecated and were defined erronously
26
+ # they were never part of the datadog language client standard or documentation
27
+ # some users may already be relying on them, but we should look to remove these in the future
28
+ # or before 1.0.
29
+ PROPAGATION_INJECT_STYLE_ENV_OLD = 'DD_PROPAGATION_INJECT_STYLE'.freeze
30
+ PROPAGATION_EXTRACT_STYLE_ENV_OLD = 'DD_PROPAGATION_EXTRACT_STYLE'.freeze
25
31
 
26
32
  # gRPC metadata keys for distributed tracing. https://github.com/grpc/grpc-go/blob/v1.10.x/Documentation/grpc-metadata.md
27
33
  GRPC_METADATA_TRACE_ID = 'x-datadog-trace-id'.freeze
@@ -0,0 +1,12 @@
1
+ module Datadog
2
+ module Ext
3
+ # Defines constants for Git tags
4
+ module Git
5
+ TAG_BRANCH = 'git.branch'.freeze
6
+ TAG_COMMIT_SHA = 'git.commit.sha'.freeze
7
+ TAG_DEPRECATED_COMMIT_SHA = 'git.commit_sha'.freeze
8
+ TAG_REPOSITORY_URL = 'git.repository_url'.freeze
9
+ TAG_TAG = 'git.tag'.freeze
10
+ end
11
+ end
12
+ end
@@ -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,8 +7,10 @@ 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
 
13
+ TAG_ID = 'runtime-id'.freeze
12
14
  TAG_LANG = 'language'.freeze
13
15
 
14
16
  # Metrics
@@ -0,0 +1,24 @@
1
+ module Datadog
2
+ module Ext
3
+ # Defines constants for test tags
4
+ module Test
5
+ TAG_ARGUMENTS = 'test.arguments'.freeze
6
+ TAG_FRAMEWORK = 'test.framework'.freeze
7
+ TAG_NAME = 'test.name'.freeze
8
+ TAG_SKIP_REASON = 'test.skip_reason'.freeze
9
+ TAG_STATUS = 'test.status'.freeze
10
+ TAG_SUITE = 'test.suite'.freeze
11
+ TAG_TRAITS = 'test.traits'.freeze
12
+ TAG_TYPE = 'test.type'.freeze
13
+
14
+ # TODO: is there a better place for SPAN_KIND?
15
+ TAG_SPAN_KIND = 'span.kind'.freeze
16
+
17
+ module Status
18
+ PASS = 'pass'.freeze
19
+ FAIL = 'fail'.freeze
20
+ SKIP = 'skip'.freeze
21
+ end
22
+ end
23
+ end
24
+ end
@@ -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
@@ -38,24 +38,36 @@ module Datadog
38
38
  end
39
39
 
40
40
  def trace_id
41
- value = @metadata[GRPC_METADATA_TRACE_ID].to_i
42
- value if (1..Span::MAX_ID).cover? value
41
+ value = metadata_for_key(GRPC_METADATA_TRACE_ID).to_i
42
+ value if (1..Span::EXTERNAL_MAX_ID).cover? value
43
43
  end
44
44
 
45
45
  def parent_id
46
- value = @metadata[GRPC_METADATA_PARENT_ID].to_i
47
- value if (1..Span::MAX_ID).cover? value
46
+ value = metadata_for_key(GRPC_METADATA_PARENT_ID).to_i
47
+ value if (1..Span::EXTERNAL_MAX_ID).cover? value
48
48
  end
49
49
 
50
50
  def sampling_priority
51
- value = @metadata[GRPC_METADATA_SAMPLING_PRIORITY]
51
+ value = metadata_for_key(GRPC_METADATA_SAMPLING_PRIORITY)
52
52
  value && value.to_i
53
53
  end
54
54
 
55
55
  def origin
56
- value = @metadata[GRPC_METADATA_ORIGIN]
56
+ value = metadata_for_key(GRPC_METADATA_ORIGIN)
57
57
  value if value != ''
58
58
  end
59
+
60
+ private
61
+
62
+ def metadata_for_key(key)
63
+ # metadata values can be arrays (multiple headers with the same key)
64
+ value = @metadata[key]
65
+ if value.is_a?(Array)
66
+ value.first
67
+ else
68
+ value
69
+ end
70
+ end
59
71
  end
60
72
  end
61
73
  end
@@ -1,22 +1,21 @@
1
1
  require 'securerandom'
2
2
  require 'ddtrace/ext/runtime'
3
+ require 'ddtrace/utils/forking'
3
4
 
4
5
  module Datadog
5
6
  module Runtime
6
7
  # For runtime identity
7
8
  module Identity
9
+ extend Datadog::Utils::Forking
10
+
8
11
  module_function
9
12
 
10
13
  # Retrieves number of classes from runtime
11
14
  def id
12
- @pid ||= Process.pid
13
15
  @id ||= SecureRandom.uuid
14
16
 
15
17
  # Check if runtime has changed, e.g. forked.
16
- if Process.pid != @pid
17
- @pid = Process.pid
18
- @id = SecureRandom.uuid
19
- end
18
+ after_fork! { @id = SecureRandom.uuid }
20
19
 
21
20
  @id
22
21
  end
@@ -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)
@@ -38,6 +38,10 @@ module Datadog
38
38
  @tokens = max_tokens
39
39
  @total_messages = 0
40
40
  @conforming_messages = 0
41
+ @prev_conforming_messages = nil
42
+ @prev_total_messages = nil
43
+ @current_window = nil
44
+
41
45
  @last_refill = Utils::Time.get_time
42
46
  end
43
47
 
@@ -47,28 +51,17 @@ module Datadog
47
51
  # If it does, return +true+ and remove +size+
48
52
  # tokens from the bucket.
49
53
  # If it does not, return +false+ without affecting
50
- # the tokens form the bucket.
54
+ # the tokens from the bucket.
51
55
  #
52
56
  # @return [Boolean] +true+ if message conforms with current bucket limit
53
57
  def allow?(size)
54
- return false if @rate.zero?
55
- return true if @rate < 0
56
-
57
- refill_since_last_message
58
-
59
- increment_total_count
60
-
61
- return false if @tokens < size
62
-
63
- increment_conforming_count
64
-
65
- @tokens -= size
66
-
67
- true
58
+ allowed = should_allow?(size)
59
+ update_rate_counts(allowed)
60
+ allowed
68
61
  end
69
62
 
70
63
  # Ratio of 'conformance' per 'total messages' checked
71
- # on this bucket.
64
+ # averaged for the past 2 buckets
72
65
  #
73
66
  # Returns +1.0+ when no messages have been checked yet.
74
67
  #
@@ -77,6 +70,20 @@ module Datadog
77
70
  return 0.0 if @rate.zero?
78
71
  return 1.0 if @rate < 0 || @total_messages.zero?
79
72
 
73
+ return current_window_rate if @prev_conforming_messages.nil? || @prev_total_messages.nil?
74
+
75
+ (@conforming_messages.to_f + @prev_conforming_messages.to_f) / (@total_messages + @prev_total_messages)
76
+ end
77
+
78
+ # Ratio of 'conformance' per 'total messages' checked
79
+ # on this bucket
80
+ #
81
+ # Returns +1.0+ when no messages have been checked yet.
82
+ #
83
+ # @return [Float] Conformance ratio, between +[0,1]+
84
+ def current_window_rate
85
+ return 1.0 if @total_messages.zero?
86
+
80
87
  @conforming_messages.to_f / @total_messages
81
88
  end
82
89
 
@@ -91,6 +98,8 @@ module Datadog
91
98
  now = Utils::Time.get_time
92
99
  elapsed = now - @last_refill
93
100
 
101
+ # Update the number of available tokens, but ensure we do not exceed the max
102
+ # we return the min of tokens + rate*elapsed, or max tokens
94
103
  refill_tokens(@rate * elapsed)
95
104
 
96
105
  @last_refill = now
@@ -108,6 +117,46 @@ module Datadog
108
117
  def increment_conforming_count
109
118
  @conforming_messages += 1
110
119
  end
120
+
121
+ def should_allow?(size)
122
+ # rate limit of 0 blocks everything
123
+ return false if @rate.zero?
124
+
125
+ # negative rate limit disables rate limiting
126
+ return true if @rate < 0
127
+
128
+ refill_since_last_message
129
+
130
+ # if tokens < 1 we don't allow?
131
+ return false if @tokens < size
132
+
133
+ @tokens -= size
134
+
135
+ true
136
+ end
137
+
138
+ # Sets and Updates the past two 1 second windows for which
139
+ # the rate limiter must compute it's rate over and updates
140
+ # the total count, and conforming message count if +allowed+
141
+ def update_rate_counts(allowed)
142
+ now = Utils::Time.get_time
143
+
144
+ # No tokens have been seen yet, start a new window
145
+ if @current_window.nil?
146
+ @current_window = now
147
+ # If more than 1 second has past since last window, reset
148
+ elsif now - @current_window >= 1
149
+ @prev_conforming_messages = @conforming_messages
150
+ @prev_total_messages = @total_messages
151
+ @conforming_messages = 0
152
+ @total_messages = 0
153
+ @current_window = now
154
+ end
155
+
156
+ increment_conforming_count if allowed
157
+
158
+ increment_total_count
159
+ end
111
160
  end
112
161
 
113
162
  # \RateLimiter that accepts all resources,
@@ -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
@@ -160,6 +174,28 @@ module Datadog
160
174
  set_tag(Ext::Errors::STACK, e.backtrace) unless e.backtrace.empty?
161
175
  end
162
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
+
163
199
  # Mark the span finished at the current time and submit it.
164
200
  def finish(finish_time = nil)
165
201
  # A span should not be finished twice. Note that this is not thread-safe,
@@ -170,12 +206,15 @@ module Datadog
170
206
 
171
207
  @allocation_count_finish = now_allocations
172
208
 
173
- # Provide a default start_time if unset, but this should have been set by start_span.
174
- # Using now here causes 0-duration spans, still, this is expected, as we never
175
- # explicitely say when it started.
176
- @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?
177
215
 
178
- @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
179
218
 
180
219
  # Finish does not really do anything if the span is not bound to a tracer and a context.
181
220
  return self if @tracer.nil? || @context.nil?
@@ -195,11 +234,6 @@ module Datadog
195
234
  self
196
235
  end
197
236
 
198
- # Return whether the span is finished or not.
199
- def finished?
200
- !@end_time.nil?
201
- end
202
-
203
237
  # Return a string representation of the span.
204
238
  def to_s
205
239
  "Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})"
@@ -246,19 +280,76 @@ module Datadog
246
280
  error: @status
247
281
  }
248
282
 
249
- if !@start_time.nil? && !@end_time.nil?
250
- h[:start] = (@start_time.to_f * 1e9).to_i
251
- h[:duration] = ((@end_time - @start_time) * 1e9).to_i
283
+ if finished?
284
+ h[:start] = start_time_nano
285
+ h[:duration] = duration_nano
252
286
  end
253
287
 
254
288
  h
255
289
  end
256
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
+
257
349
  # Return a human readable version of the span
258
350
  def pretty_print(q)
259
- start_time = (@start_time.to_f * 1e9).to_i rescue '-'
260
- end_time = (@end_time.to_f * 1e9).to_i rescue '-'
261
- 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
262
353
  q.group 0 do
263
354
  q.breakable
264
355
  q.text "Name: #{@name}\n"
@@ -271,7 +362,7 @@ module Datadog
271
362
  q.text "Error: #{@status}\n"
272
363
  q.text "Start: #{start_time}\n"
273
364
  q.text "End: #{end_time}\n"
274
- q.text "Duration: #{duration}\n"
365
+ q.text "Duration: #{duration.to_f if finished?}\n"
275
366
  q.text "Allocations: #{allocations}\n"
276
367
  q.group(2, 'Tags: [', "]\n") do
277
368
  q.breakable
@@ -288,8 +379,30 @@ module Datadog
288
379
  end
289
380
  end
290
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
+
291
400
  private
292
401
 
402
+ def duration_marker
403
+ Utils::Time.get_time
404
+ end
405
+
293
406
  if defined?(JRUBY_VERSION) || Gem::Version.new(RUBY_VERSION) < Gem::Version.new(VERSION::MINIMUM_RUBY_VERSION)
294
407
  def now_allocations
295
408
  0
@@ -303,5 +416,17 @@ module Datadog
303
416
  GC.stat(:total_allocated_objects)
304
417
  end
305
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
306
431
  end
307
432
  end