ddtrace 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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