ddtrace 1.4.2 → 1.5.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -2
  3. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +0 -2
  4. data/lib/datadog/appsec/configuration/settings.rb +0 -2
  5. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +0 -2
  6. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  7. data/lib/datadog/ci/ext/environment.rb +16 -4
  8. data/lib/datadog/core/configuration/agent_settings_resolver.rb +0 -3
  9. data/lib/datadog/core/configuration/components.rb +8 -2
  10. data/lib/datadog/core/configuration/settings.rb +69 -2
  11. data/lib/datadog/core/configuration.rb +1 -1
  12. data/lib/datadog/core/header_collection.rb +41 -0
  13. data/lib/datadog/core/telemetry/collector.rb +0 -2
  14. data/lib/datadog/core/workers/async.rb +0 -2
  15. data/lib/datadog/profiling/collectors/old_stack.rb +1 -1
  16. data/lib/datadog/profiling.rb +1 -1
  17. data/lib/datadog/tracing/client_ip.rb +153 -0
  18. data/lib/datadog/tracing/configuration/ext.rb +12 -0
  19. data/lib/datadog/tracing/contrib/aws/services.rb +0 -2
  20. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +0 -2
  21. data/lib/datadog/tracing/contrib/ext.rb +19 -0
  22. data/lib/datadog/tracing/contrib/faraday/middleware.rb +1 -2
  23. data/lib/datadog/tracing/contrib/grape/endpoint.rb +0 -2
  24. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -1
  25. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -4
  26. data/lib/datadog/tracing/contrib/rack/header_collection.rb +35 -0
  27. data/lib/datadog/tracing/contrib/rack/middlewares.rb +92 -38
  28. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +83 -9
  29. data/lib/datadog/tracing/flush.rb +57 -35
  30. data/lib/datadog/tracing/metadata/ext.rb +3 -9
  31. data/lib/datadog/tracing/metadata/tagging.rb +9 -0
  32. data/lib/datadog/tracing/sampling/rate_limiter.rb +3 -0
  33. data/lib/datadog/tracing/sampling/rate_sampler.rb +10 -0
  34. data/lib/datadog/tracing/sampling/span/ext.rb +29 -0
  35. data/lib/datadog/tracing/sampling/span/matcher.rb +9 -0
  36. data/lib/datadog/tracing/sampling/span/rule.rb +82 -0
  37. data/lib/datadog/tracing/sampling/span/rule_parser.rb +104 -0
  38. data/lib/datadog/tracing/sampling/span/sampler.rb +64 -0
  39. data/lib/datadog/tracing/span_operation.rb +0 -2
  40. data/lib/datadog/tracing/trace_operation.rb +22 -3
  41. data/lib/datadog/tracing/trace_segment.rb +1 -2
  42. data/lib/datadog/tracing/tracer.rb +31 -5
  43. data/lib/ddtrace/version.rb +2 -2
  44. metadata +19 -5
@@ -3,7 +3,6 @@
3
3
  module Datadog
4
4
  module Tracing
5
5
  module Contrib
6
- # rubocop:disable Metrics/ModuleLength:
7
6
  module Aws
8
7
  SERVICES = %w[
9
8
  ACM
@@ -117,7 +116,6 @@ module Datadog
117
116
  XRay
118
117
  ].freeze
119
118
  end
120
- # rubocop:enable Metrics/ModuleLength:
121
119
  end
122
120
  end
123
121
  end
@@ -18,7 +18,6 @@ module Datadog
18
18
  end
19
19
 
20
20
  # InstanceMethods - implementing instrumentation
21
- # rubocop:disable Metrics/ModuleLength
22
21
  module InstanceMethods
23
22
  include Contrib::HttpAnnotationHelper
24
23
 
@@ -168,7 +167,6 @@ module Datadog
168
167
  datadog_configuration[:analytics_sample_rate]
169
168
  end
170
169
  end
171
- # rubocop:enable Metrics/ModuleLength
172
170
  end
173
171
  end
174
172
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ # Contrib specific constants
7
+ module Ext
8
+ # @public_api
9
+ module DB
10
+ TAG_INSTANCE = 'db.instance'
11
+ TAG_USER = 'db.user'
12
+ TAG_SYSTEM = 'db.system'
13
+ TAG_STATEMENT = 'db.statement'
14
+ TAG_ROW_COUNT = 'db.row_count'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -39,8 +39,7 @@ module Datadog
39
39
 
40
40
  def annotate!(span, env, options)
41
41
  span.resource = resource_name(env)
42
- service_name(env[:url].host, options)
43
- span.service = options[:split_by_domain] ? env[:url].host : options[:service_name]
42
+ span.service = service_name(env[:url].host, options)
44
43
  span.span_type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
45
44
 
46
45
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -9,7 +9,6 @@ module Datadog
9
9
  module Tracing
10
10
  module Contrib
11
11
  module Grape
12
- # rubocop:disable Metrics/ModuleLength
13
12
  # Endpoint module includes a list of subscribers to create
14
13
  # traces when a Grape endpoint is hit
15
14
  module Endpoint
@@ -245,7 +244,6 @@ module Datadog
245
244
  end
246
245
  end
247
246
  end
248
- # rubocop:enable Metrics/ModuleLength
249
247
  end
250
248
  end
251
249
  end
@@ -27,7 +27,7 @@ module Datadog
27
27
  end
28
28
 
29
29
  option :schemas
30
- option :service_nam
30
+ option :service_name
31
31
  end
32
32
  end
33
33
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../metadata/ext'
4
4
  require_relative '../analytics'
5
+ require_relative '../ext'
5
6
  require_relative 'ext'
6
7
 
7
8
  module Datadog
@@ -95,9 +96,9 @@ module Datadog
95
96
  span.set_tag(Tracing::Metadata::Ext::TAG_PEER_SERVICE, service)
96
97
  span.set_tag(Tracing::Metadata::Ext::TAG_PEER_HOSTNAME, host)
97
98
 
98
- span.set_tag(Tracing::Metadata::Ext::DB::TAG_INSTANCE, db)
99
- span.set_tag(Tracing::Metadata::Ext::DB::TAG_USER, user)
100
- span.set_tag(Tracing::Metadata::Ext::DB::TAG_SYSTEM, Ext::SPAN_SYSTEM)
99
+ span.set_tag(Contrib::Ext::DB::TAG_INSTANCE, db)
100
+ span.set_tag(Contrib::Ext::DB::TAG_USER, user)
101
+ span.set_tag(Contrib::Ext::DB::TAG_SYSTEM, Ext::SPAN_SYSTEM)
101
102
 
102
103
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, host)
103
104
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, port)
@@ -106,7 +107,7 @@ module Datadog
106
107
  end
107
108
 
108
109
  def annotate_span_with_result!(span, result)
109
- span.set_tag(Tracing::Metadata::Ext::DB::TAG_ROW_COUNT, result.ntuples)
110
+ span.set_tag(Contrib::Ext::DB::TAG_ROW_COUNT, result.ntuples)
110
111
  end
111
112
 
112
113
  def datadog_configuration
@@ -0,0 +1,35 @@
1
+ require_relative '../../../core/header_collection'
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Rack
7
+ # Classes and utilities for handling headers in Rack.
8
+ module Header
9
+ # An implementation of a header collection that looks up headers from a Rack environment.
10
+ class RequestHeaderCollection < Datadog::Core::HeaderCollection
11
+ # Creates a header collection from a rack environment.
12
+ def initialize(env)
13
+ super()
14
+ @env = env
15
+ end
16
+
17
+ # Gets the value of the header with the given name.
18
+ def get(header_name)
19
+ @env[Header.to_rack_header(header_name)]
20
+ end
21
+
22
+ # Tests whether a header with the given name exists in the environment.
23
+ def key?(header_name)
24
+ @env.key?(Header.to_rack_header(header_name))
25
+ end
26
+ end
27
+
28
+ def self.to_rack_header(name)
29
+ "HTTP_#{name.to_s.upcase.gsub(/[-\s]/, '_')}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -3,12 +3,14 @@
3
3
  require 'date'
4
4
 
5
5
  require_relative '../../../core/environment/variable_helpers'
6
+ require_relative '../../client_ip'
6
7
  require_relative '../../metadata/ext'
7
8
  require_relative '../../propagation/http'
8
9
  require_relative '../analytics'
10
+ require_relative '../utils/quantization/http'
9
11
  require_relative 'ext'
12
+ require_relative 'header_collection'
10
13
  require_relative 'request_queue'
11
- require_relative '../utils/quantization/http'
12
14
 
13
15
  module Datadog
14
16
  module Tracing
@@ -121,20 +123,13 @@ module Datadog
121
123
  # rubocop:disable Metrics/PerceivedComplexity
122
124
  # rubocop:disable Metrics/MethodLength
123
125
  def set_request_tags!(trace, request_span, env, status, headers, response, original_env)
124
- # http://www.rubydoc.info/github/rack/rack/file/SPEC
125
- # The source of truth in Rack is the PATH_INFO key that holds the
126
- # URL for the current request; but some frameworks may override that
127
- # value, especially during exception handling.
128
- #
129
- # Because of this, we prefer to use REQUEST_URI, if available, which is the
130
- # relative path + query string, and doesn't mutate.
131
- #
132
- # REQUEST_URI is only available depending on what web server is running though.
133
- # So when its not available, we want the original, unmutated PATH_INFO, which
134
- # is just the relative path without query strings.
135
- url = env['REQUEST_URI'] || original_env['PATH_INFO']
136
- request_headers = parse_request_headers(env)
137
- response_headers = parse_response_headers(headers || {})
126
+ request_header_collection = Header::RequestHeaderCollection.new(env)
127
+ request_headers_tags = parse_request_headers(request_header_collection)
128
+ response_headers_tags = parse_response_headers(headers || {})
129
+
130
+ # request_headers is subject to filtering and configuration so we
131
+ # get the user agent separately
132
+ user_agent = parse_user_agent_header(request_header_collection)
138
133
 
139
134
  # The priority
140
135
  # 1. User overrides span.resource
@@ -169,8 +164,14 @@ module Datadog
169
164
  request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, env['REQUEST_METHOD'])
170
165
  end
171
166
 
167
+ url = parse_url(env, original_env)
168
+
172
169
  if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_URL).nil?
173
- options = configuration[:quantize]
170
+ options = configuration[:quantize] || {}
171
+
172
+ # Quantization::HTTP.url base defaults to :show, but we are transitioning
173
+ options[:base] ||= :exclude
174
+
174
175
  request_span.set_tag(
175
176
  Tracing::Metadata::Ext::HTTP::TAG_URL,
176
177
  Contrib::Utils::Quantization::HTTP.url(url, options)
@@ -178,29 +179,43 @@ module Datadog
178
179
  end
179
180
 
180
181
  if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_BASE_URL).nil?
181
- request_obj = ::Rack::Request.new(env)
182
+ options = configuration[:quantize]
182
183
 
183
- base_url = if request_obj.respond_to?(:base_url)
184
- request_obj.base_url
185
- else
186
- # Compatibility for older Rack versions
187
- request_obj.url.chomp(request_obj.fullpath)
188
- end
184
+ unless options[:base] == :show
185
+ base_url = Contrib::Utils::Quantization::HTTP.base_url(url)
189
186
 
190
- request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_BASE_URL, base_url)
187
+ unless base_url.empty?
188
+ request_span.set_tag(
189
+ Tracing::Metadata::Ext::HTTP::TAG_BASE_URL,
190
+ base_url
191
+ )
192
+ end
193
+ end
194
+ end
195
+
196
+ if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
197
+ Tracing::ClientIp.set_client_ip_tag(
198
+ request_span,
199
+ headers: request_header_collection,
200
+ remote_ip: env['REMOTE_ADDR']
201
+ )
191
202
  end
192
203
 
193
204
  if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE).nil? && status
194
205
  request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, status)
195
206
  end
196
207
 
208
+ if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT).nil? && user_agent
209
+ request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT, user_agent)
210
+ end
211
+
197
212
  # Request headers
198
- request_headers.each do |name, value|
213
+ request_headers_tags.each do |name, value|
199
214
  request_span.set_tag(name, value) if request_span.get_tag(name).nil?
200
215
  end
201
216
 
202
217
  # Response headers
203
- response_headers.each do |name, value|
218
+ response_headers_tags.each do |name, value|
204
219
  request_span.set_tag(name, value) if request_span.get_tag(name).nil?
205
220
  end
206
221
 
@@ -219,14 +234,57 @@ module Datadog
219
234
  Datadog.configuration.tracing[:rack]
220
235
  end
221
236
 
222
- def parse_request_headers(env)
223
- {}.tap do |result|
224
- whitelist = configuration[:headers][:request] || []
225
- whitelist.each do |header|
226
- rack_header = header_to_rack_header(header)
227
- if env.key?(rack_header)
228
- result[Tracing::Metadata::Ext::HTTP::RequestHeaders.to_tag(header)] = env[rack_header]
229
- end
237
+ def parse_url(env, original_env)
238
+ request_obj = ::Rack::Request.new(env)
239
+
240
+ # scheme, host, and port
241
+ base_url = if request_obj.respond_to?(:base_url)
242
+ request_obj.base_url
243
+ else
244
+ # Compatibility for older Rack versions
245
+ request_obj.url.chomp(request_obj.fullpath)
246
+ end
247
+
248
+ # https://github.com/rack/rack/blob/main/SPEC.rdoc
249
+ #
250
+ # The source of truth in Rack is the PATH_INFO key that holds the
251
+ # URL for the current request; but some frameworks may override that
252
+ # value, especially during exception handling.
253
+ #
254
+ # Because of this, we prefer to use REQUEST_URI, if available, which is the
255
+ # relative path + query string, and doesn't mutate.
256
+ #
257
+ # REQUEST_URI is only available depending on what web server is running though.
258
+ # So when its not available, we want the original, unmutated PATH_INFO, which
259
+ # is just the relative path without query strings.
260
+ #
261
+ # SCRIPT_NAME is the first part of the request URL path, so that
262
+ # the application can know its virtual location. It should be
263
+ # prepended to PATH_INFO to reflect the correct user visible path.
264
+ request_uri = env['REQUEST_URI'].to_s
265
+ fullpath = if request_uri.empty?
266
+ query_string = original_env['QUERY_STRING'].to_s
267
+ path = original_env['SCRIPT_NAME'].to_s + original_env['PATH_INFO'].to_s
268
+
269
+ query_string.empty? ? path : "#{path}?#{query_string}"
270
+ else
271
+ request_uri
272
+ end
273
+
274
+ base_url + fullpath
275
+ end
276
+
277
+ def parse_user_agent_header(headers)
278
+ headers.get(Tracing::Metadata::Ext::HTTP::HEADER_USER_AGENT)
279
+ end
280
+
281
+ def parse_request_headers(headers)
282
+ whitelist = configuration[:headers][:request] || []
283
+ whitelist.each_with_object({}) do |header, result|
284
+ header_value = headers.get(header)
285
+ unless header_value.nil?
286
+ header_tag = Tracing::Metadata::Ext::HTTP::RequestHeaders.to_tag(header)
287
+ result[header_tag] = header_value
230
288
  end
231
289
  end
232
290
  end
@@ -248,10 +306,6 @@ module Datadog
248
306
  end
249
307
  end
250
308
  end
251
-
252
- def header_to_rack_header(name)
253
- "HTTP_#{name.to_s.upcase.gsub(/[-\s]/, '_')}"
254
- end
255
309
  end
256
310
  end
257
311
  end
@@ -22,6 +22,14 @@ module Datadog
22
22
  options[:placeholder] || PLACEHOLDER
23
23
  end
24
24
 
25
+ def base_url(url, options = {})
26
+ URI.parse(url).tap do |uri|
27
+ uri.path = ''
28
+ uri.query = nil
29
+ uri.fragment = nil
30
+ end.to_s
31
+ end
32
+
25
33
  def url!(url, options = {})
26
34
  options ||= {}
27
35
 
@@ -32,8 +40,14 @@ module Datadog
32
40
  uri.query = (!query.nil? && query.empty? ? nil : query)
33
41
  end
34
42
 
35
- # Remove any URI framents
43
+ # Remove any URI fragments
36
44
  uri.fragment = nil unless options[:fragment] == :show
45
+
46
+ if options[:base] == :exclude
47
+ uri.host = nil
48
+ uri.port = nil
49
+ uri.scheme = nil
50
+ end
37
51
  end.to_s
38
52
  end
39
53
 
@@ -45,22 +59,26 @@ module Datadog
45
59
 
46
60
  def query!(query, options = {})
47
61
  options ||= {}
48
- options[:show] = options[:show] || []
62
+ options[:obfuscate] = {} if options[:obfuscate] == :internal
63
+ options[:show] = options[:show] || (options[:obfuscate] ? :all : [])
49
64
  options[:exclude] = options[:exclude] || []
50
65
 
51
66
  # Short circuit if query string is meant to exclude everything
52
67
  # or if the query string is meant to include everything
53
68
  return '' if options[:exclude] == :all
54
- return query if options[:show] == :all
55
69
 
56
- collect_query(query, uniq: true) do |key, value|
57
- if options[:exclude].include?(key)
58
- [nil, nil]
59
- else
60
- value = options[:show].include?(key) ? value : nil
61
- [key, value]
70
+ unless options[:show] == :all && !(options[:obfuscate] && options[:exclude])
71
+ query = collect_query(query, uniq: true) do |key, value|
72
+ if options[:exclude].include?(key)
73
+ [nil, nil]
74
+ else
75
+ value = options[:show] == :all || options[:show].include?(key) ? value : nil
76
+ [key, value]
77
+ end
62
78
  end
63
79
  end
80
+
81
+ options[:obfuscate] ? obfuscate_query(query, options[:obfuscate]) : query
64
82
  end
65
83
 
66
84
  # Iterate over each key value pair, yielding to the block given.
@@ -91,6 +109,62 @@ module Datadog
91
109
  end
92
110
 
93
111
  private_class_method :collect_query
112
+
113
+ # Scans over the query string and obfuscates sensitive data by
114
+ # replacing matches with an opaque value
115
+ def obfuscate_query(query, options = {})
116
+ options[:regex] = nil if options[:regex] == :internal
117
+ re = options[:regex] || OBFUSCATOR_REGEX
118
+ with = options[:with] || OBFUSCATOR_WITH
119
+
120
+ query.gsub(re, with)
121
+ end
122
+
123
+ private_class_method :obfuscate_query
124
+
125
+ OBFUSCATOR_WITH = '<redacted>'.freeze
126
+
127
+ # rubocop:disable Layout/LineLength
128
+ OBFUSCATOR_REGEX = %r{
129
+ (?: # JSON-ish leading quote
130
+ (?:"|%22)?
131
+ )
132
+ (?: # common keys
133
+ (?:old_?|new_?)?p(?:ass)?w(?:or)?d(?:1|2)? # pw, password variants
134
+ |pass(?:_?phrase)? # pass, passphrase variants
135
+ |secret
136
+ |(?: # key, key_id variants
137
+ api_?
138
+ |private_?
139
+ |public_?
140
+ |access_?
141
+ |secret_?
142
+ )key(?:_?id)?
143
+ |token
144
+ |consumer_?(?:id|key|secret)
145
+ |sign(?:ed|ature)?
146
+ |auth(?:entication|orization)?
147
+ )
148
+ (?:
149
+ # '=' query string separator, plus value til next '&' separator
150
+ (?:\s|%20)*(?:=|%3D)[^&]+
151
+ # JSON-ish '": "somevalue"', key being handled with case above, without the opening '"'
152
+ |(?:"|%22) # closing '"' at end of key
153
+ (?:\s|%20)*(?::|%3A)(?:\s|%20)* # ':' key-value spearator, with surrounding spaces
154
+ (?:"|%22) # opening '"' at start of value
155
+ (?:%2[^2]|%[^2]|[^"%])+ # value
156
+ (?:"|%22) # closing '"' at end of value
157
+ )
158
+ |(?: # other common secret values
159
+ bearer(?:\s|%20)+[a-z0-9._\-]+
160
+ |token(?::|%3A)[a-z0-9]{13}
161
+ |gh[opsu]_[0-9a-zA-Z]{36}
162
+ |ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+/=-]|%3D|%2F|%2B)+)?
163
+ |-{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY-{5}[^\-]+-{5}END(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY(?:-{5})?(?:\n|%0A)?
164
+ |(?:ssh-(?:rsa|dss)|ecdsa-[a-z0-9]+-[a-z0-9]+)(?:\s|%20)*(?:[a-z0-9/.+]|%2F|%5C|%2B){100,}(?:=|%3D)*(?:(?:\s+)[a-z0-9._-]+)?
165
+ )
166
+ }ix.freeze
167
+ # rubocop:enable Layout/LineLength
94
168
  end
95
169
  end
96
170
  end
@@ -3,71 +3,93 @@
3
3
  module Datadog
4
4
  module Tracing
5
5
  module Flush
6
- # Consumes only completed traces (where all spans have finished)
7
- class Finished
8
- # Consumes and returns completed traces (where all spans have finished)
9
- # from the provided \trace_op, if any.
6
+ # Consumes and returns a {TraceSegment} to be flushed, from
7
+ # the provided {TraceSegment}.
8
+ #
9
+ # Only finished spans are consumed. Any spans consumed are
10
+ # removed from +trace_op+ as a side effect. Unfinished spans are
11
+ # unaffected.
12
+ #
13
+ # @abstract
14
+ class Base
15
+ # Consumes and returns a {TraceSegment} to be flushed, from
16
+ # the provided {TraceSegment}.
10
17
  #
11
- # Any traces consumed are removed from +trace_op+ as a side effect.
18
+ # Only finished spans are consumed. Any spans consumed are
19
+ # removed from +trace_op+ as a side effect. Unfinished spans are
20
+ # unaffected.
12
21
  #
22
+ # @param [TraceOperation] trace_op
13
23
  # @return [TraceSegment] trace to be flushed, or +nil+ if the trace is not finished
14
24
  def consume!(trace_op)
15
- return unless full_flush?(trace_op)
25
+ return unless flush?(trace_op)
16
26
 
17
27
  get_trace(trace_op)
18
28
  end
19
29
 
20
- def full_flush?(trace_op)
21
- trace_op && trace_op.sampled? && trace_op.finished?
30
+ # Should we consume spans from the +trace_op+?
31
+ # @abstract
32
+ def flush?(trace_op)
33
+ raise NotImplementedError
22
34
  end
23
35
 
24
36
  protected
25
37
 
38
+ # Consumes all finished spans from trace.
39
+ # @return [TraceSegment]
26
40
  def get_trace(trace_op)
27
- trace_op.flush!
41
+ trace_op.flush! do |spans|
42
+ spans.select! { |span| single_sampled?(span) } unless trace_op.sampled?
43
+
44
+ spans
45
+ end
46
+ end
47
+
48
+ # Single Span Sampling has chosen to keep this span
49
+ # regardless of the trace-level sampling decision
50
+ def single_sampled?(span)
51
+ span.get_metric(Sampling::Span::Ext::TAG_MECHANISM) == Sampling::Span::Ext::MECHANISM_SPAN_SAMPLING_RATE
52
+ end
53
+ end
54
+
55
+ # Consumes and returns completed traces (where all spans have finished),
56
+ # if any, from the provided +trace_op+.
57
+ #
58
+ # Spans consumed are removed from +trace_op+ as a side effect.
59
+ class Finished < Base
60
+ # Are all spans finished?
61
+ def flush?(trace_op)
62
+ trace_op && trace_op.finished?
28
63
  end
29
64
  end
30
65
 
31
- # Performs partial trace flushing to avoid large traces residing in memory for too long
32
- class Partial
66
+ # Consumes and returns completed or partially completed
67
+ # traces from the provided +trace_op+, if any.
68
+ #
69
+ # Partial trace flushing avoids large traces residing in memory for too long.
70
+ #
71
+ # Partially completed traces, where not all spans have finished,
72
+ # will only be returned if there are at least
73
+ # +@min_spans_for_partial+ finished spans.
74
+ #
75
+ # Spans consumed are removed from +trace_op+ as a side effect.
76
+ class Partial < Base
33
77
  # Start flushing partial trace after this many active spans in one trace
34
78
  DEFAULT_MIN_SPANS_FOR_PARTIAL_FLUSH = 500
35
79
 
36
80
  attr_reader :min_spans_for_partial
37
81
 
38
82
  def initialize(options = {})
83
+ super()
39
84
  @min_spans_for_partial = options.fetch(:min_spans_before_partial_flush, DEFAULT_MIN_SPANS_FOR_PARTIAL_FLUSH)
40
85
  end
41
86
 
42
- # Consumes and returns completed or partially completed
43
- # traces from the provided +trace_op+, if any.
44
- #
45
- # Partially completed traces, where not all spans have finished,
46
- # will only be returned if there are at least
47
- # +@min_spans_for_partial+ finished spans.
48
- #
49
- # Any spans consumed are removed from +trace_op+ as a side effect.
50
- #
51
- # @return [TraceSegment] partial or complete trace to be flushed, or +nil+ if no spans are finished
52
- def consume!(trace_op)
53
- return unless partial_flush?(trace_op)
54
-
55
- get_trace(trace_op)
56
- end
57
-
58
- def partial_flush?(trace_op)
59
- return false unless trace_op.sampled?
87
+ def flush?(trace_op)
60
88
  return true if trace_op.finished?
61
89
  return false if trace_op.finished_span_count < @min_spans_for_partial
62
90
 
63
91
  true
64
92
  end
65
-
66
- protected
67
-
68
- def get_trace(trace_op)
69
- trace_op.flush!
70
- end
71
93
  end
72
94
  end
73
95
  end
@@ -63,11 +63,14 @@ module Datadog
63
63
  TAG_BASE_URL = 'http.base_url'
64
64
  TAG_METHOD = 'http.method'
65
65
  TAG_STATUS_CODE = 'http.status_code'
66
+ TAG_USER_AGENT = 'http.useragent'
66
67
  TAG_URL = 'http.url'
67
68
  TYPE_INBOUND = AppTypes::TYPE_WEB.freeze
68
69
  TYPE_OUTBOUND = 'http'
69
70
  TYPE_PROXY = 'proxy'
70
71
  TYPE_TEMPLATE = 'template'
72
+ TAG_CLIENT_IP = 'http.client_ip'
73
+ HEADER_USER_AGENT = 'User-Agent'
71
74
 
72
75
  # General header functionality
73
76
  module Headers
@@ -153,15 +156,6 @@ module Datadog
153
156
  TAG_QUERY = 'sql.query'
154
157
  end
155
158
 
156
- # @public_api
157
- module DB
158
- TAG_INSTANCE = 'db.instance'
159
- TAG_USER = 'db.user'
160
- TAG_SYSTEM = 'db.system'
161
- TAG_STATEMENT = 'db.statement'
162
- TAG_ROW_COUNT = 'db.row_count'
163
- end
164
-
165
159
  # @public_api
166
160
  module SpanKind
167
161
  TAG_SERVER = 'server'
@@ -65,6 +65,15 @@ module Datadog
65
65
  tags.each { |k, v| set_tag(k, v) }
66
66
  end
67
67
 
68
+ # Returns true if the provided `tag` was set to a non-nil value.
69
+ # False otherwise.
70
+ #
71
+ # @param [String] tag the tag or metric to check for presence
72
+ # @return [Boolean] if the tag is present and not nil
73
+ def has_tag?(tag) # rubocop:disable Naming/PredicateName
74
+ !get_tag(tag).nil? # nil is considered not present, thus we can't use `Hash#has_key?`
75
+ end
76
+
68
77
  # This method removes a tag for the given key.
69
78
  def clear_tag(key)
70
79
  meta.delete(key)
@@ -39,6 +39,9 @@ module Datadog
39
39
  def initialize(rate, max_tokens = rate)
40
40
  super()
41
41
 
42
+ raise ArgumentError, "rate must be a number: #{rate}" unless rate.is_a?(Numeric)
43
+ raise ArgumentError, "max_tokens must be a number: #{max_tokens}" unless max_tokens.is_a?(Numeric)
44
+
42
45
  @rate = rate
43
46
  @max_tokens = max_tokens
44
47