datadog 2.8.0 → 2.9.0

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