ddtrace 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,8 +16,10 @@ module Datadog
16
16
  module Watcher
17
17
  # rubocop:disable Metrics/AbcSize
18
18
  # rubocop:disable Metrics/MethodLength
19
+ # rubocop:disable Metrics/CyclomaticComplexity
20
+ # rubocop:disable Metrics/PerceivedComplexity
19
21
  def self.watch
20
- Instrumentation.gateway.watch('rack.request') do |stack, request|
22
+ Instrumentation.gateway.watch('rack.request', :appsec) do |stack, request|
21
23
  block = false
22
24
  event = nil
23
25
  waf_context = request.env['datadog.waf.context']
@@ -26,23 +28,24 @@ module Datadog
26
28
  trace = active_trace
27
29
  span = active_span
28
30
 
29
- Rack::Reactive::Request.subscribe(op, waf_context) do |action, result, _block|
30
- record = [:block, :monitor].include?(action)
31
- if record
31
+ Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
32
+ if result.status == :match
32
33
  # TODO: should this hash be an Event instance instead?
33
34
  event = {
34
35
  waf_result: result,
35
36
  trace: trace,
36
37
  span: span,
37
38
  request: request,
38
- action: action
39
+ actions: result.actions
39
40
  }
40
41
 
42
+ span.set_tag('appsec.event', 'true') if span
43
+
41
44
  waf_context.events << event
42
45
  end
43
46
  end
44
47
 
45
- _action, _result, block = Rack::Reactive::Request.publish(op, request)
48
+ _result, block = Rack::Reactive::Request.publish(op, request)
46
49
  end
47
50
 
48
51
  next [nil, [[:block, event]]] if block
@@ -57,7 +60,7 @@ module Datadog
57
60
  [ret, res]
58
61
  end
59
62
 
60
- Instrumentation.gateway.watch('rack.response') do |stack, response|
63
+ Instrumentation.gateway.watch('rack.response', :appsec) do |stack, response|
61
64
  block = false
62
65
  event = nil
63
66
  waf_context = response.instance_eval { @waf_context }
@@ -66,23 +69,24 @@ module Datadog
66
69
  trace = active_trace
67
70
  span = active_span
68
71
 
69
- Rack::Reactive::Response.subscribe(op, waf_context) do |action, result, _block|
70
- record = [:block, :monitor].include?(action)
71
- if record
72
+ Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
73
+ if result.status == :match
72
74
  # TODO: should this hash be an Event instance instead?
73
75
  event = {
74
76
  waf_result: result,
75
77
  trace: trace,
76
78
  span: span,
77
79
  response: response,
78
- action: action
80
+ actions: result.actions
79
81
  }
80
82
 
83
+ span.set_tag('appsec.event', 'true') if span
84
+
81
85
  waf_context.events << event
82
86
  end
83
87
  end
84
88
 
85
- _action, _result, block = Rack::Reactive::Response.publish(op, response)
89
+ _result, block = Rack::Reactive::Response.publish(op, response)
86
90
  end
87
91
 
88
92
  next [nil, [[:block, event]]] if block
@@ -97,7 +101,7 @@ module Datadog
97
101
  [ret, res]
98
102
  end
99
103
 
100
- Instrumentation.gateway.watch('rack.request.body') do |stack, request|
104
+ Instrumentation.gateway.watch('rack.request.body', :appsec) do |stack, request|
101
105
  block = false
102
106
  event = nil
103
107
  waf_context = request.env['datadog.waf.context']
@@ -106,23 +110,24 @@ module Datadog
106
110
  trace = active_trace
107
111
  span = active_span
108
112
 
109
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |action, result, _block|
110
- record = [:block, :monitor].include?(action)
111
- if record
113
+ Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
114
+ if result.status == :match
112
115
  # TODO: should this hash be an Event instance instead?
113
116
  event = {
114
117
  waf_result: result,
115
118
  trace: trace,
116
119
  span: span,
117
120
  request: request,
118
- action: action
121
+ actions: result.actions
119
122
  }
120
123
 
124
+ span.set_tag('appsec.event', 'true') if span
125
+
121
126
  waf_context.events << event
122
127
  end
123
128
  end
124
129
 
125
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
130
+ _result, block = Rack::Reactive::RequestBody.publish(op, request)
126
131
  end
127
132
 
128
133
  next [nil, [[:block, event]]] if block
@@ -137,6 +142,8 @@ module Datadog
137
142
  [ret, res]
138
143
  end
139
144
  end
145
+ # rubocop:enable Metrics/CyclomaticComplexity
146
+ # rubocop:enable Metrics/PerceivedComplexity
140
147
  # rubocop:enable Metrics/MethodLength
141
148
  # rubocop:enable Metrics/AbcSize
142
149
 
@@ -54,27 +54,27 @@ module Datadog
54
54
  }
55
55
 
56
56
  waf_timeout = Datadog::AppSec.settings.waf_timeout
57
- action, result = waf_context.run(waf_args, waf_timeout)
57
+ result = waf_context.run(waf_args, waf_timeout)
58
58
 
59
59
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
60
60
 
61
- # TODO: encapsulate return array in a type
62
- case action
63
- when :monitor
61
+ case result.status
62
+ when :match
64
63
  Datadog.logger.debug { "WAF: #{result.inspect}" }
65
- yield [action, result, false]
66
- when :block
67
- Datadog.logger.debug { "WAF: #{result.inspect}" }
68
- yield [action, result, true]
69
- throw(:block, [action, result, true])
70
- when :good
64
+
65
+ block = result.actions.include?('block')
66
+
67
+ yield [result, block]
68
+
69
+ throw(:block, [result, true]) if block
70
+ when :ok
71
71
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
72
72
  when :invalid_call
73
73
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
74
74
  when :invalid_rule, :invalid_flow, :no_rule
75
75
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
76
76
  else
77
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
77
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
78
78
  end
79
79
  end
80
80
  end
@@ -32,27 +32,27 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog::AppSec.settings.waf_timeout
35
- action, result = waf_context.run(waf_args, waf_timeout)
35
+ result = waf_context.run(waf_args, waf_timeout)
36
36
 
37
37
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
38
38
 
39
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
42
+
43
+ block = result.actions.include?('block')
44
+
45
+ yield [result, block]
46
+
47
+ throw(:block, [result, true]) if block
48
+ when :ok
49
49
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
50
50
  when :invalid_call
51
51
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
52
52
  when :invalid_rule, :invalid_flow, :no_rule
53
53
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
54
54
  else
55
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
55
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
56
56
  end
57
57
  end
58
58
  end
@@ -32,27 +32,27 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog::AppSec.settings.waf_timeout
35
- action, result = waf_context.run(waf_args, waf_timeout)
35
+ result = waf_context.run(waf_args, waf_timeout)
36
36
 
37
37
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
38
38
 
39
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
42
+
43
+ block = result.actions.include?('block')
44
+
45
+ yield [result, block]
46
+
47
+ throw(:block, [result, true]) if block
48
+ when :ok
49
49
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
50
50
  when :invalid_call
51
51
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
52
52
  when :invalid_rule, :invalid_flow, :no_rule
53
53
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
54
54
  else
55
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
55
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
56
56
  end
57
57
  end
58
58
  end
@@ -47,6 +47,9 @@ module Datadog
47
47
  end
48
48
 
49
49
  def self.form_hash(request)
50
+ # force form data processing
51
+ request.POST if request.form_data?
52
+
50
53
  # usually Hash<String,String> but can be a more complex
51
54
  # Hash<String,String||Array||Hash> when e.g coming from JSON
52
55
  request.env['rack.request.form_hash']
@@ -21,7 +21,7 @@ module Datadog
21
21
  end
22
22
 
23
23
  def call(env)
24
- return @app.call(env) unless @processor.ready?
24
+ return @app.call(env) unless Datadog.configuration.appsec.enabled && @processor.ready?
25
25
 
26
26
  # TODO: handle exceptions, except for @app.call
27
27
 
@@ -57,6 +57,7 @@ module Datadog
57
57
  request_return
58
58
  ensure
59
59
  add_waf_runtime_tags(context) if context
60
+ context.finalize if context
60
61
  end
61
62
 
62
63
  private
@@ -13,7 +13,7 @@ module Datadog
13
13
  # Watcher for Rails gateway events
14
14
  module Watcher
15
15
  def self.watch
16
- Instrumentation.gateway.watch('rails.request.action') do |stack, request|
16
+ Instrumentation.gateway.watch('rails.request.action', :appsec) do |stack, request|
17
17
  block = false
18
18
  event = nil
19
19
  waf_context = request.env['datadog.waf.context']
@@ -22,23 +22,24 @@ module Datadog
22
22
  trace = active_trace
23
23
  span = active_span
24
24
 
25
- Rails::Reactive::Action.subscribe(op, waf_context) do |action, result, _block|
26
- record = [:block, :monitor].include?(action)
27
- if record
25
+ Rails::Reactive::Action.subscribe(op, waf_context) do |result, _block|
26
+ if result.status == :match
28
27
  # TODO: should this hash be an Event instance instead?
29
28
  event = {
30
29
  waf_result: result,
31
30
  trace: trace,
32
31
  span: span,
33
32
  request: request,
34
- action: action
33
+ actions: result.actions
35
34
  }
36
35
 
36
+ span.set_tag('appsec.event', 'true') if span
37
+
37
38
  waf_context.events << event
38
39
  end
39
40
  end
40
41
 
41
- _action, _result, block = Rails::Reactive::Action.publish(op, request)
42
+ _result, block = Rails::Reactive::Action.publish(op, request)
42
43
  end
43
44
 
44
45
  next [nil, [[:block, event]]] if block
@@ -36,27 +36,27 @@ module Datadog
36
36
  }
37
37
 
38
38
  waf_timeout = Datadog::AppSec.settings.waf_timeout
39
- action, result = waf_context.run(waf_args, waf_timeout)
39
+ result = waf_context.run(waf_args, waf_timeout)
40
40
 
41
41
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
42
42
 
43
- # TODO: encapsulate return array in a type
44
- case action
45
- when :monitor
43
+ case result.status
44
+ when :match
46
45
  Datadog.logger.debug { "WAF: #{result.inspect}" }
47
- yield [action, result, false]
48
- when :block
49
- Datadog.logger.debug { "WAF: #{result.inspect}" }
50
- yield [action, result, true]
51
- throw(:block, [action, result, true])
52
- when :good
46
+
47
+ block = result.actions.include?('block')
48
+
49
+ yield [result, block]
50
+
51
+ throw(:block, [result, true]) if block
52
+ when :ok
53
53
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
54
54
  when :invalid_call
55
55
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
56
56
  when :invalid_rule, :invalid_flow, :no_rule
57
57
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
58
58
  else
59
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
59
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
60
60
  end
61
61
  end
62
62
  end
@@ -7,6 +7,9 @@ module Datadog
7
7
  # Normalized extration of data from ActionDispatch::Request
8
8
  module Request
9
9
  def self.parsed_body(request)
10
+ # force body parameter parsing, which is done lazily by Rails
11
+ request.parameters
12
+
10
13
  # usually Hash<String,String> but can be a more complex
11
14
  # Hash<String,String||Array||Hash> when e.g coming from JSON or
12
15
  # with Rails advanced param square bracket parsing
@@ -15,7 +15,7 @@ module Datadog
15
15
  module Watcher
16
16
  # rubocop:disable Metrics/MethodLength
17
17
  def self.watch
18
- Instrumentation.gateway.watch('sinatra.request.dispatch') do |stack, request|
18
+ Instrumentation.gateway.watch('sinatra.request.dispatch', :appsec) do |stack, request|
19
19
  block = false
20
20
  event = nil
21
21
  waf_context = request.env['datadog.waf.context']
@@ -24,23 +24,24 @@ module Datadog
24
24
  trace = active_trace
25
25
  span = active_span
26
26
 
27
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |action, result, _block|
28
- record = [:block, :monitor].include?(action)
29
- if record
27
+ Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
28
+ if result.status == :match
30
29
  # TODO: should this hash be an Event instance instead?
31
30
  event = {
32
31
  waf_result: result,
33
32
  trace: trace,
34
33
  span: span,
35
34
  request: request,
36
- action: action
35
+ actions: result.actions
37
36
  }
38
37
 
38
+ span.set_tag('appsec.event', 'true') if span
39
+
39
40
  waf_context.events << event
40
41
  end
41
42
  end
42
43
 
43
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
44
+ _result, block = Rack::Reactive::RequestBody.publish(op, request)
44
45
  end
45
46
 
46
47
  next [nil, [[:block, event]]] if block
@@ -55,7 +56,7 @@ module Datadog
55
56
  [ret, res]
56
57
  end
57
58
 
58
- Instrumentation.gateway.watch('sinatra.request.routed') do |stack, (request, route_params)|
59
+ Instrumentation.gateway.watch('sinatra.request.routed', :appsec) do |stack, (request, route_params)|
59
60
  block = false
60
61
  event = nil
61
62
  waf_context = request.env['datadog.waf.context']
@@ -64,23 +65,24 @@ module Datadog
64
65
  trace = active_trace
65
66
  span = active_span
66
67
 
67
- Sinatra::Reactive::Routed.subscribe(op, waf_context) do |action, result, _block|
68
- record = [:block, :monitor].include?(action)
69
- if record
68
+ Sinatra::Reactive::Routed.subscribe(op, waf_context) do |result, _block|
69
+ if result.status == :match
70
70
  # TODO: should this hash be an Event instance instead?
71
71
  event = {
72
72
  waf_result: result,
73
73
  trace: trace,
74
74
  span: span,
75
75
  request: request,
76
- action: action
76
+ actions: result.actions
77
77
  }
78
78
 
79
+ span.set_tag('appsec.event', 'true') if span
80
+
79
81
  waf_context.events << event
80
82
  end
81
83
  end
82
84
 
83
- _action, _result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
85
+ _result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
84
86
  end
85
87
 
86
88
  next [nil, [[:block, event]]] if block
@@ -31,27 +31,27 @@ module Datadog
31
31
  }
32
32
 
33
33
  waf_timeout = Datadog::AppSec.settings.waf_timeout
34
- action, result = waf_context.run(waf_args, waf_timeout)
34
+ result = waf_context.run(waf_args, waf_timeout)
35
35
 
36
36
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
37
37
 
38
- # TODO: encapsulate return array in a type
39
- case action
40
- when :monitor
38
+ case result.status
39
+ when :match
41
40
  Datadog.logger.debug { "WAF: #{result.inspect}" }
42
- yield [action, result, false]
43
- when :block
44
- Datadog.logger.debug { "WAF: #{result.inspect}" }
45
- yield [action, result, true]
46
- throw(:block, [action, result, true])
47
- when :good
41
+
42
+ block = result.actions.include?('block')
43
+
44
+ yield [result, block]
45
+
46
+ throw(:block, [result, true]) if block
47
+ when :ok
48
48
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
49
49
  when :invalid_call
50
50
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
51
51
  when :invalid_rule, :invalid_flow, :no_rule
52
52
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
53
53
  else
54
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
54
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
55
55
  end
56
56
  end
57
57
  end
@@ -51,8 +51,6 @@ module Datadog
51
51
  end
52
52
  end
53
53
 
54
- # rubocop:disable Metrics/AbcSize
55
- # rubocop:disable Metrics/MethodLength
56
54
  def self.record_via_span(*events)
57
55
  events.group_by { |e| e[:trace] }.each do |trace, event_group|
58
56
  unless trace
@@ -64,10 +62,6 @@ module Datadog
64
62
 
65
63
  # prepare and gather tags to apply
66
64
  trace_tags = event_group.each_with_object({}) do |event, tags|
67
- span = event[:span]
68
-
69
- span.set_tag('appsec.event', 'true') if span
70
-
71
65
  # TODO: assume HTTP request context for now
72
66
 
73
67
  if (request = event[:request])
@@ -114,8 +108,6 @@ module Datadog
114
108
  end
115
109
  end
116
110
  end
117
- # rubocop:enable Metrics/MethodLength
118
- # rubocop:enable Metrics/AbcSize
119
111
  end
120
112
  end
121
113
  end
@@ -6,6 +6,20 @@ module Datadog
6
6
  module Instrumentation
7
7
  # Instrumentation gateway implementation
8
8
  class Gateway
9
+ # Instrumentation gateway middleware
10
+ class Middleware
11
+ attr_reader :key, :block
12
+
13
+ def initialize(key, &block)
14
+ @key = key
15
+ @block = block
16
+ end
17
+
18
+ def call(*args, **kwargs, &block)
19
+ @block.call(*args, **kwargs, &block)
20
+ end
21
+ end
22
+
9
23
  def initialize
10
24
  @middlewares = Hash.new { |h, k| h[k] = [] }
11
25
  end
@@ -31,8 +45,8 @@ module Datadog
31
45
  stack.call(env)
32
46
  end
33
47
 
34
- def watch(name, &block)
35
- @middlewares[name] << block
48
+ def watch(name, key, &block)
49
+ @middlewares[name] << Middleware.new(key, &block) unless @middlewares[name].any? { |m| m.key == key }
36
50
  end
37
51
  end
38
52
 
@@ -31,7 +31,7 @@ module Datadog
31
31
  def run(*args)
32
32
  start_ns = Core::Utils::Time.get_time(:nanosecond)
33
33
 
34
- ret, res = @context.run(*args)
34
+ _code, res = @context.run(*args)
35
35
 
36
36
  stop_ns = Core::Utils::Time.get_time(:nanosecond)
37
37
 
@@ -39,7 +39,11 @@ module Datadog
39
39
  @time_ext_ns += (stop_ns - start_ns)
40
40
  @timeouts += 1 if res.timeout
41
41
 
42
- [ret, res]
42
+ res
43
+ end
44
+
45
+ def finalize
46
+ @context.finalize
43
47
  end
44
48
  end
45
49
 
@@ -64,6 +68,18 @@ module Datadog
64
68
  Context.new(self)
65
69
  end
66
70
 
71
+ def update_rule_data(data)
72
+ @handle.update_rule_data(data)
73
+ end
74
+
75
+ def toggle_rules(map)
76
+ @handle.toggle_rules(map)
77
+ end
78
+
79
+ def finalize
80
+ @handle.finalize
81
+ end
82
+
67
83
  protected
68
84
 
69
85
  attr_reader :handle
@@ -323,8 +323,8 @@ module Datadog
323
323
 
324
324
  # Parse tags from environment
325
325
  env_to_list(Core::Environment::Ext::ENV_TAGS, comma_separated_only: false).each do |tag|
326
- pair = tag.split(':')
327
- tags[pair.first] = pair.last if pair.length == 2
326
+ key, value = tag.split(':', 2)
327
+ tags[key] = value if value && !value.empty?
328
328
  end
329
329
 
330
330
  # Override tags if defined
@@ -271,7 +271,7 @@ module Datadog
271
271
  request_uri
272
272
  end
273
273
 
274
- base_url + fullpath
274
+ ::URI.join(base_url, fullpath).to_s
275
275
  end
276
276
 
277
277
  def parse_user_agent_header(headers)
@@ -14,20 +14,28 @@ module Datadog
14
14
 
15
15
  PLACEHOLDER = '?'.freeze
16
16
 
17
+ # taken from Ruby https://github.com/ruby/uri/blob/ffbab83de6d8748c9454414e02db5317609166eb/lib/uri/rfc3986_parser.rb
18
+ # but adjusted to parse only <scheme>://<host>:<port>/ components
19
+ # and stop there, since we don't care about the path, query string,
20
+ # and fragment components
21
+ RFC3986_URL_BASE = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))(?::(?<port>\d*))?)))(?:\/|\z)/.freeze # rubocop:disable Style/RegexpLiteral, Layout/LineLength
22
+
17
23
  module_function
18
24
 
19
25
  def url(url, options = {})
20
26
  url!(url, options)
21
27
  rescue StandardError
22
- options[:placeholder] || PLACEHOLDER
28
+ placeholder = options[:placeholder] || PLACEHOLDER
29
+
30
+ options[:base] == :exclude ? placeholder : "#{base_url(url)}/#{placeholder}"
23
31
  end
24
32
 
25
33
  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
34
+ if (m = RFC3986_URL_BASE.match(url))
35
+ m[1]
36
+ else
37
+ ''
38
+ end
31
39
  end
32
40
 
33
41
  def url!(url, options = {})
@@ -101,6 +101,8 @@ module Datadog
101
101
  # Make the trace serializable
102
102
  serializable_trace = SerializableTrace.new(trace)
103
103
 
104
+ Datadog.logger.debug { "Flushing trace: #{JSON.dump(serializable_trace)}" }
105
+
104
106
  # Encode the trace
105
107
  encoder.encode(serializable_trace)
106
108
  end