datadog 2.8.0 → 2.9.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +219 -122
  10. data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
  11. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  12. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
  14. data/ext/datadog_profiling_native_extension/profiling.c +10 -8
  15. data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
  16. data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -54
  17. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
  18. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
  19. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  20. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  21. data/ext/libdatadog_api/crashtracker.c +3 -0
  22. data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
  23. data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
  24. data/lib/datadog/appsec/context.rb +54 -0
  25. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +7 -7
  26. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  27. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  28. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
  29. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
  30. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  31. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
  32. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
  33. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
  34. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
  35. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  36. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
  37. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
  38. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  39. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
  40. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
  41. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
  42. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
  43. data/lib/datadog/appsec/event.rb +6 -6
  44. data/lib/datadog/appsec/ext.rb +3 -1
  45. data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
  46. data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
  47. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  48. data/lib/datadog/appsec.rb +3 -3
  49. data/lib/datadog/auto_instrument.rb +3 -0
  50. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  51. data/lib/datadog/core/configuration/components.rb +4 -2
  52. data/lib/datadog/core/configuration.rb +1 -1
  53. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  54. data/lib/datadog/core/crashtracking/component.rb +1 -3
  55. data/lib/datadog/core/telemetry/event.rb +87 -3
  56. data/lib/datadog/core/telemetry/logging.rb +2 -2
  57. data/lib/datadog/core/telemetry/metric.rb +22 -0
  58. data/lib/datadog/core/telemetry/worker.rb +33 -0
  59. data/lib/datadog/di/base.rb +115 -0
  60. data/lib/datadog/di/code_tracker.rb +7 -4
  61. data/lib/datadog/di/component.rb +17 -11
  62. data/lib/datadog/di/configuration/settings.rb +11 -1
  63. data/lib/datadog/di/contrib/railtie.rb +15 -0
  64. data/lib/datadog/di/contrib.rb +26 -0
  65. data/lib/datadog/di/error.rb +5 -0
  66. data/lib/datadog/di/instrumenter.rb +39 -18
  67. data/lib/datadog/di/{init.rb → preload.rb} +2 -4
  68. data/lib/datadog/di/probe_manager.rb +4 -4
  69. data/lib/datadog/di/probe_notification_builder.rb +16 -2
  70. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  71. data/lib/datadog/di/remote.rb +4 -4
  72. data/lib/datadog/di/transport.rb +2 -4
  73. data/lib/datadog/di.rb +5 -108
  74. data/lib/datadog/kit/appsec/events.rb +3 -3
  75. data/lib/datadog/kit/identity.rb +4 -4
  76. data/lib/datadog/profiling/component.rb +55 -53
  77. data/lib/datadog/profiling/http_transport.rb +1 -26
  78. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  79. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  80. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  81. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  82. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  83. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  84. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  85. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  86. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  87. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  88. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  89. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  90. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  91. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  92. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  93. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  94. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  95. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  96. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  97. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  98. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  99. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  100. data/lib/datadog/tracing/span.rb +12 -4
  101. data/lib/datadog/tracing/span_event.rb +123 -3
  102. data/lib/datadog/tracing/span_operation.rb +6 -0
  103. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  104. data/lib/datadog/version.rb +1 -1
  105. metadata +19 -10
  106. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  107. data/lib/datadog/appsec/scope.rb +0 -58
  108. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../../instrumentation/gateway'
4
- require_relative '../../../reactive/operation'
4
+ require_relative '../../../reactive/engine'
5
5
  require_relative '../../rack/reactive/request_body'
6
6
  require_relative '../reactive/routed'
7
7
  require_relative '../../../event'
@@ -23,85 +23,63 @@ module Datadog
23
23
 
24
24
  def watch_request_dispatch(gateway = Instrumentation.gateway)
25
25
  gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
26
- block = false
27
-
28
26
  event = nil
29
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
30
-
31
- AppSec::Reactive::Operation.new('sinatra.request.dispatch') do |op|
32
- Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result|
33
- if result.status == :match
34
- # TODO: should this hash be an Event instance instead?
35
- event = {
36
- waf_result: result,
37
- trace: scope.trace,
38
- span: scope.service_entry_span,
39
- request: gateway_request,
40
- actions: result.actions
41
- }
42
-
43
- # We want to keep the trace in case of security event
44
- scope.trace.keep! if scope.trace
45
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
46
- scope.processor_context.events << event
47
- end
27
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
28
+ engine = AppSec::Reactive::Engine.new
29
+
30
+ Rack::Reactive::RequestBody.subscribe(engine, context) do |result|
31
+ if result.status == :match
32
+ # TODO: should this hash be an Event instance instead?
33
+ event = {
34
+ waf_result: result,
35
+ trace: context.trace,
36
+ span: context.span,
37
+ request: gateway_request,
38
+ actions: result.actions
39
+ }
40
+
41
+ # We want to keep the trace in case of security event
42
+ context.trace.keep! if context.trace
43
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
44
+ context.waf_runner.events << event
48
45
  end
49
-
50
- block = Rack::Reactive::RequestBody.publish(op, gateway_request)
51
46
  end
52
47
 
48
+ block = Rack::Reactive::RequestBody.publish(engine, gateway_request)
53
49
  next [nil, [[:block, event]]] if block
54
50
 
55
- ret, res = stack.call(gateway_request.request)
56
-
57
- if event
58
- res ||= []
59
- res << [:monitor, event]
60
- end
61
-
62
- [ret, res]
51
+ stack.call(gateway_request.request)
63
52
  end
64
53
  end
65
54
 
66
55
  def watch_request_routed(gateway = Instrumentation.gateway)
67
56
  gateway.watch('sinatra.request.routed', :appsec) do |stack, (gateway_request, gateway_route_params)|
68
- block = false
69
-
70
57
  event = nil
71
- scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
72
-
73
- AppSec::Reactive::Operation.new('sinatra.request.routed') do |op|
74
- Sinatra::Reactive::Routed.subscribe(op, scope.processor_context) do |result|
75
- if result.status == :match
76
- # TODO: should this hash be an Event instance instead?
77
- event = {
78
- waf_result: result,
79
- trace: scope.trace,
80
- span: scope.service_entry_span,
81
- request: gateway_request,
82
- actions: result.actions
83
- }
84
-
85
- # We want to keep the trace in case of security event
86
- scope.trace.keep! if scope.trace
87
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
88
- scope.processor_context.events << event
89
- end
58
+ context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
59
+ engine = AppSec::Reactive::Engine.new
60
+
61
+ Sinatra::Reactive::Routed.subscribe(engine, context) do |result|
62
+ if result.status == :match
63
+ # TODO: should this hash be an Event instance instead?
64
+ event = {
65
+ waf_result: result,
66
+ trace: context.trace,
67
+ span: context.span,
68
+ request: gateway_request,
69
+ actions: result.actions
70
+ }
71
+
72
+ # We want to keep the trace in case of security event
73
+ context.trace.keep! if context.trace
74
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
75
+ context.waf_runner.events << event
90
76
  end
91
-
92
- block = Sinatra::Reactive::Routed.publish(op, [gateway_request, gateway_route_params])
93
77
  end
94
78
 
79
+ block = Sinatra::Reactive::Routed.publish(engine, [gateway_request, gateway_route_params])
95
80
  next [nil, [[:block, event]]] if block
96
81
 
97
- ret, res = stack.call(gateway_request.request)
98
-
99
- if event
100
- res ||= []
101
- res << [:monitor, event]
102
- end
103
-
104
- [ret, res]
82
+ stack.call(gateway_request.request)
105
83
  end
106
84
  end
107
85
  end
@@ -54,7 +54,7 @@ module Datadog
54
54
  def dispatch!
55
55
  env = @request.env
56
56
 
57
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
57
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
58
58
 
59
59
  return super unless context
60
60
 
@@ -86,7 +86,7 @@ module Datadog
86
86
  def process_route(*)
87
87
  env = @request.env
88
88
 
89
- context = env[Datadog::AppSec::Ext::SCOPE_KEY]
89
+ context = env[Datadog::AppSec::Ext::CONTEXT_KEY]
90
90
 
91
91
  return super unless context
92
92
 
@@ -12,18 +12,18 @@ module Datadog
12
12
  ].freeze
13
13
  private_constant :ADDRESSES
14
14
 
15
- def self.publish(op, data)
15
+ def self.publish(engine, data)
16
16
  _request, route_params = data
17
17
 
18
18
  catch(:block) do
19
- op.publish('sinatra.request.route_params', route_params.params)
19
+ engine.publish('sinatra.request.route_params', route_params.params)
20
20
 
21
21
  nil
22
22
  end
23
23
  end
24
24
 
25
- def self.subscribe(op, waf_context)
26
- op.subscribe(*ADDRESSES) do |*values|
25
+ def self.subscribe(engine, context)
26
+ engine.subscribe(*ADDRESSES) do |*values|
27
27
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
28
28
  path_params = values[0]
29
29
 
@@ -32,7 +32,7 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog.configuration.appsec.waf_timeout
35
- result = waf_context.run(persistent_data, {}, waf_timeout)
35
+ result = context.run_waf(persistent_data, {}, waf_timeout)
36
36
 
37
37
  next if result.status != :match
38
38
 
@@ -137,16 +137,16 @@ module Datadog
137
137
  end
138
138
  # rubocop:enable Metrics/MethodLength
139
139
 
140
- def tag_and_keep!(scope, waf_result)
140
+ def tag_and_keep!(context, waf_result)
141
141
  # We want to keep the trace in case of security event
142
- scope.trace.keep! if scope.trace
142
+ context.trace.keep! if context.trace
143
143
 
144
- if scope.service_entry_span
145
- scope.service_entry_span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
146
- scope.service_entry_span.set_tag('appsec.event', 'true')
144
+ if context.span
145
+ context.span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
146
+ context.span.set_tag('appsec.event', 'true')
147
147
  end
148
148
 
149
- add_distributed_tags(scope.trace)
149
+ add_distributed_tags(context.trace)
150
150
  end
151
151
 
152
152
  private
@@ -3,8 +3,10 @@
3
3
  module Datadog
4
4
  module AppSec
5
5
  module Ext
6
+ RASP_SQLI = :sql_injection
6
7
  INTERRUPT = :datadog_appsec_interrupt
7
- SCOPE_KEY = 'datadog.appsec.scope'
8
+ CONTEXT_KEY = 'datadog.appsec.context'
9
+ ACTIVE_CONTEXT_KEY = :datadog_appsec_active_context
8
10
 
9
11
  TAG_APPSEC_ENABLED = '_dd.appsec.enabled'
10
12
  TAG_APM_ENABLED = '_dd.apm.enabled'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../instrumentation/gateway'
4
- require_relative '../../reactive/operation'
4
+ require_relative '../../reactive/engine'
5
5
  require_relative '../reactive/set_user'
6
6
 
7
7
  module Datadog
@@ -19,42 +19,32 @@ module Datadog
19
19
 
20
20
  def watch_user_id(gateway = Instrumentation.gateway)
21
21
  gateway.watch('identity.set_user', :appsec) do |stack, user|
22
- block = false
23
22
  event = nil
24
- scope = Datadog::AppSec.active_scope
25
-
26
- AppSec::Reactive::Operation.new('identity.set_user') do |op|
27
- Monitor::Reactive::SetUser.subscribe(op, scope.processor_context) do |result|
28
- if result.status == :match
29
- # TODO: should this hash be an Event instance instead?
30
- event = {
31
- waf_result: result,
32
- trace: scope.trace,
33
- span: scope.service_entry_span,
34
- user: user,
35
- actions: result.actions
36
- }
37
-
38
- # We want to keep the trace in case of security event
39
- scope.trace.keep! if scope.trace
40
- Datadog::AppSec::Event.tag_and_keep!(scope, result)
41
- scope.processor_context.events << event
42
- end
23
+ context = Datadog::AppSec.active_context
24
+ engine = AppSec::Reactive::Engine.new
25
+
26
+ Monitor::Reactive::SetUser.subscribe(engine, context) do |result|
27
+ if result.status == :match
28
+ # TODO: should this hash be an Event instance instead?
29
+ event = {
30
+ waf_result: result,
31
+ trace: context.trace,
32
+ span: context.span,
33
+ user: user,
34
+ actions: result.actions
35
+ }
36
+
37
+ # We want to keep the trace in case of security event
38
+ context.trace.keep! if context.trace
39
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
40
+ context.waf_runner.events << event
43
41
  end
44
-
45
- block = Monitor::Reactive::SetUser.publish(op, user)
46
42
  end
47
43
 
48
- throw(Datadog::AppSec::Ext::INTERRUPT, [nil, [[:block, event]]]) if block
49
-
50
- ret, res = stack.call(user)
51
-
52
- if event
53
- res ||= []
54
- res << [:monitor, event]
55
- end
44
+ block = Monitor::Reactive::SetUser.publish(engine, user)
45
+ throw(Datadog::AppSec::Ext::INTERRUPT, event[:actions]) if block
56
46
 
57
- [ret, res]
47
+ stack.call(user)
58
48
  end
59
49
  end
60
50
  end
@@ -11,16 +11,16 @@ module Datadog
11
11
  ].freeze
12
12
  private_constant :ADDRESSES
13
13
 
14
- def self.publish(op, user)
14
+ def self.publish(engine, user)
15
15
  catch(:block) do
16
- op.publish('usr.id', user.id)
16
+ engine.publish('usr.id', user.id)
17
17
 
18
18
  nil
19
19
  end
20
20
  end
21
21
 
22
- def self.subscribe(op, waf_context)
23
- op.subscribe(*ADDRESSES) do |*values|
22
+ def self.subscribe(engine, context)
23
+ engine.subscribe(*ADDRESSES) do |*values|
24
24
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
25
25
 
26
26
  user_id = values[0]
@@ -30,7 +30,7 @@ module Datadog
30
30
  }
31
31
 
32
32
  waf_timeout = Datadog.configuration.appsec.waf_timeout
33
- result = waf_context.run(persistent_data, {}, waf_timeout)
33
+ result = context.run_waf(persistent_data, {}, waf_timeout)
34
34
 
35
35
  next if result.status != :match
36
36
 
@@ -74,9 +74,6 @@ module Datadog
74
74
  when Hash
75
75
  pass = ip_passlist[:pass]
76
76
  monitor = ip_passlist[:monitor]
77
- else
78
- pass = []
79
- monitor = []
80
77
  end
81
78
 
82
79
  exclusions = []
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'appsec/configuration'
4
4
  require_relative 'appsec/extensions'
5
- require_relative 'appsec/scope'
5
+ require_relative 'appsec/context'
6
6
  require_relative 'appsec/ext'
7
7
  require_relative 'appsec/utils'
8
8
 
@@ -14,8 +14,8 @@ module Datadog
14
14
  Datadog.configuration.appsec.enabled
15
15
  end
16
16
 
17
- def active_scope
18
- Datadog::AppSec::Scope.active_scope
17
+ def active_context
18
+ Datadog::AppSec::Context.active
19
19
  end
20
20
 
21
21
  def processor
@@ -6,6 +6,9 @@
6
6
  require_relative '../datadog'
7
7
  require_relative 'tracing/contrib/auto_instrument'
8
8
 
9
+ # DI is not loaded on Ruby 2.5 and JRuby
10
+ Datadog::DI::Contrib.load_now_or_later if defined?(Datadog::DI::Contrib)
11
+
9
12
  Datadog::Profiling.start_if_enabled
10
13
 
11
14
  module Datadog
@@ -19,21 +19,49 @@ module Datadog
19
19
  # Whenever there is a conflict (different configurations are provided in different orders), it MUST warn the users
20
20
  # about it and pick a value based on the following priority: code > environment variable > defaults.
21
21
  class AgentSettingsResolver
22
- AgentSettings = Struct.new(
23
- :adapter,
24
- :ssl,
25
- :hostname,
26
- :port,
27
- :uds_path,
28
- :timeout_seconds,
29
- keyword_init: true
30
- ) do
31
- def initialize(*)
32
- super
22
+ # Immutable container for the resulting settings
23
+ class AgentSettings
24
+ attr_reader :adapter, :ssl, :hostname, :port, :uds_path, :timeout_seconds
25
+
26
+ def initialize(adapter: nil, ssl: nil, hostname: nil, port: nil, uds_path: nil, timeout_seconds: nil)
27
+ @adapter = adapter
28
+ @ssl = ssl
29
+ @hostname = hostname
30
+ @port = port
31
+ @uds_path = uds_path
32
+ @timeout_seconds = timeout_seconds
33
33
  freeze
34
34
  end
35
+
36
+ def url
37
+ case adapter
38
+ when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
39
+ hostname = self.hostname
40
+ hostname = "[#{hostname}]" if hostname =~ IPV6_REGEXP
41
+ "#{ssl ? 'https' : 'http'}://#{hostname}:#{port}/"
42
+ when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
43
+ "unix://#{uds_path}"
44
+ else
45
+ raise ArgumentError, "Unexpected adapter: #{adapter}"
46
+ end
47
+ end
48
+
49
+ def ==(other)
50
+ self.class == other.class &&
51
+ adapter == other.adapter &&
52
+ ssl == other.ssl &&
53
+ hostname == other.hostname &&
54
+ port == other.port &&
55
+ uds_path == other.uds_path &&
56
+ timeout_seconds == other.timeout_seconds
57
+ end
35
58
  end
36
59
 
60
+ # IPv6 regular expression from
61
+ # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
62
+ # Does not match IPv4 addresses.
63
+ IPV6_REGEXP = /\A(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\z)/.freeze # rubocop:disable Layout/LineLength
64
+
37
65
  def self.call(settings, logger: Datadog.logger)
38
66
  new(settings, logger: logger).send(:call)
39
67
  end
@@ -105,14 +105,16 @@ module Datadog
105
105
  @profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
106
106
  settings: settings,
107
107
  agent_settings: agent_settings,
108
- optional_tracer: @tracer
108
+ optional_tracer: @tracer,
109
+ logger: @logger,
109
110
  )
110
111
  @environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
111
112
 
112
113
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings)
113
114
  @health_metrics = self.class.build_health_metrics(settings)
114
115
  @appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
115
- @dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, telemetry: telemetry)
116
+ @dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
117
+ @environment_logger_extra[:dynamic_instrumentation_enabled] = !!@dynamic_instrumentation
116
118
 
117
119
  self.class.configure_tracing(settings)
118
120
  end
@@ -236,7 +236,7 @@ module Datadog
236
236
  rescue ThreadError => e
237
237
  logger_without_components.error(
238
238
  'Detected deadlock during datadog initialization. ' \
239
- 'Please report this at https://github.com/DataDog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug' \
239
+ 'Please report this at https://github.com/datadog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug' \
240
240
  "\n\tSource:\n\t#{Array(e.backtrace).join("\n\t")}"
241
241
  )
242
242
  nil
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../analytics'
4
-
5
3
  module Datadog
6
- module Tracing
4
+ module Core
7
5
  module Contrib
8
6
  module Rails
9
7
  # common utilities for Rails
@@ -3,7 +3,6 @@
3
3
  require 'libdatadog'
4
4
 
5
5
  require_relative 'tag_builder'
6
- require_relative 'agent_base_url'
7
6
  require_relative '../utils/only_once'
8
7
  require_relative '../utils/at_fork_monkey_patch'
9
8
 
@@ -31,8 +30,7 @@ module Datadog
31
30
 
32
31
  def self.build(settings, agent_settings, logger:)
33
32
  tags = TagBuilder.call(settings)
34
- agent_base_url = AgentBaseUrl.resolve(agent_settings)
35
- logger.warn('Missing agent base URL; cannot enable crash tracking') unless agent_base_url
33
+ agent_base_url = agent_settings.url
36
34
 
37
35
  ld_library_path = ::Libdatadog.ld_library_path
38
36
  logger.warn('Missing ld_library_path; cannot enable crash tracking') unless ld_library_path
@@ -29,6 +29,22 @@ module Datadog
29
29
  def payload
30
30
  {}
31
31
  end
32
+
33
+ # Override equality to allow for deduplication
34
+ # The basic implementation is to check if the other object is an instance of the same class.
35
+ # This works for events that have no attributes.
36
+ # For events with attributes, you should override this method to compare the attributes.
37
+ def ==(other)
38
+ other.is_a?(self.class)
39
+ end
40
+
41
+ # @see #==
42
+ alias eql? ==
43
+
44
+ # @see #==
45
+ def hash
46
+ self.class.hash
47
+ end
32
48
  end
33
49
 
34
50
  # Telemetry class for the 'app-started' event
@@ -263,6 +279,8 @@ module Datadog
263
279
 
264
280
  # Telemetry class for the 'app-client-configuration-change' event
265
281
  class AppClientConfigurationChange < Base
282
+ attr_reader :changes, :origin
283
+
266
284
  def type
267
285
  'app-client-configuration-change'
268
286
  end
@@ -301,6 +319,16 @@ module Datadog
301
319
 
302
320
  res
303
321
  end
322
+
323
+ def ==(other)
324
+ other.is_a?(AppClientConfigurationChange) && other.changes == @changes && other.origin == @origin
325
+ end
326
+
327
+ alias eql? ==
328
+
329
+ def hash
330
+ [self.class, @changes, @origin].hash
331
+ end
304
332
  end
305
333
 
306
334
  # Telemetry class for the 'app-heartbeat' event
@@ -319,6 +347,8 @@ module Datadog
319
347
 
320
348
  # Telemetry class for the 'generate-metrics' event
321
349
  class GenerateMetrics < Base
350
+ attr_reader :namespace, :metric_series
351
+
322
352
  def type
323
353
  'generate-metrics'
324
354
  end
@@ -335,24 +365,54 @@ module Datadog
335
365
  series: @metric_series.map(&:to_h)
336
366
  }
337
367
  end
368
+
369
+ def ==(other)
370
+ other.is_a?(GenerateMetrics) && other.namespace == @namespace && other.metric_series == @metric_series
371
+ end
372
+
373
+ alias eql? ==
374
+
375
+ def hash
376
+ [self.class, @namespace, @metric_series].hash
377
+ end
338
378
  end
339
379
 
340
- # Telemetry class for the 'logs' event
380
+ # Telemetry class for the 'logs' event.
381
+ # Logs with the same content are deduplicated at flush time.
341
382
  class Log < Base
342
383
  LEVELS = {
343
384
  error: 'ERROR',
344
385
  warn: 'WARN',
345
386
  }.freeze
346
387
 
388
+ LEVELS_STRING = LEVELS.values.freeze
389
+
390
+ attr_reader :message, :level, :stack_trace, :count
391
+
347
392
  def type
348
393
  'logs'
349
394
  end
350
395
 
351
- def initialize(message:, level:, stack_trace: nil)
396
+ # @param message [String] the log message
397
+ # @param level [Symbol, String] the log level. Either :error, :warn, 'ERROR', or 'WARN'.
398
+ # @param stack_trace [String, nil] the stack trace
399
+ # @param count [Integer] the number of times the log was emitted. Used for deduplication.
400
+ def initialize(message:, level:, stack_trace: nil, count: 1)
352
401
  super()
353
402
  @message = message
354
403
  @stack_trace = stack_trace
355
- @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
404
+
405
+ if level.is_a?(String) && LEVELS_STRING.include?(level)
406
+ # String level is used during object copy for deduplication
407
+ @level = level
408
+ elsif level.is_a?(Symbol)
409
+ # Symbol level is used by the regular log emitter user
410
+ @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
411
+ else
412
+ raise ArgumentError, "Invalid log level #{level}"
413
+ end
414
+
415
+ @count = count
356
416
  end
357
417
 
358
418
  def payload
@@ -362,10 +422,24 @@ module Datadog
362
422
  message: @message,
363
423
  level: @level,
364
424
  stack_trace: @stack_trace,
425
+ count: @count,
365
426
  }.compact
366
427
  ]
367
428
  }
368
429
  end
430
+
431
+ # override equality to allow for deduplication
432
+ def ==(other)
433
+ other.is_a?(Log) &&
434
+ other.message == @message &&
435
+ other.level == @level && other.stack_trace == @stack_trace && other.count == @count
436
+ end
437
+
438
+ alias eql? ==
439
+
440
+ def hash
441
+ [self.class, @message, @level, @stack_trace, @count].hash
442
+ end
369
443
  end
370
444
 
371
445
  # Telemetry class for the 'distributions' event
@@ -395,6 +469,16 @@ module Datadog
395
469
  }
396
470
  end
397
471
  end
472
+
473
+ def ==(other)
474
+ other.is_a?(MessageBatch) && other.events == @events
475
+ end
476
+
477
+ alias eql? ==
478
+
479
+ def hash
480
+ [self.class, @events].hash
481
+ end
398
482
  end
399
483
  end
400
484
  end
@@ -41,7 +41,7 @@ module Datadog
41
41
  else
42
42
  'REDACTED'
43
43
  end
44
- end.join(',')
44
+ end.join("\n")
45
45
  end
46
46
  end
47
47
 
@@ -49,7 +49,7 @@ module Datadog
49
49
  # Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
50
50
  message = +''
51
51
  message << (exception.class.name || exception.class.inspect)
52
- message << ':' << description if description
52
+ message << ': ' << description if description
53
53
 
54
54
  event = Event::Log.new(
55
55
  message: message,