datadog 2.2.0 → 2.4.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +14 -26
  5. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  7. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
  18. data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
  19. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  21. data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
  22. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  23. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  24. data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  27. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
  30. data/ext/datadog_profiling_native_extension/profiling.c +1 -3
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  33. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  38. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
  39. data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
  40. data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
  41. data/ext/libdatadog_api/extconf.rb +108 -0
  42. data/ext/libdatadog_api/macos_development.md +26 -0
  43. data/ext/libdatadog_extconf_helpers.rb +130 -0
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  46. data/lib/datadog/appsec/component.rb +29 -8
  47. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  48. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  49. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  50. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  51. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
  52. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  53. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
  54. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  55. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  56. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  57. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
  58. data/lib/datadog/appsec/event.rb +1 -1
  59. data/lib/datadog/appsec/processor/actions.rb +1 -1
  60. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  61. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  62. data/lib/datadog/appsec/processor.rb +36 -37
  63. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  64. data/lib/datadog/appsec/remote.rb +7 -3
  65. data/lib/datadog/appsec/response.rb +15 -1
  66. data/lib/datadog/appsec.rb +3 -2
  67. data/lib/datadog/core/configuration/components.rb +18 -15
  68. data/lib/datadog/core/configuration/settings.rb +135 -9
  69. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  70. data/lib/datadog/core/crashtracking/component.rb +111 -0
  71. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  72. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  73. data/lib/datadog/core/environment/execution.rb +5 -5
  74. data/lib/datadog/core/metrics/client.rb +7 -0
  75. data/lib/datadog/core/rate_limiter.rb +183 -0
  76. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  77. data/lib/datadog/core/remote/component.rb +4 -2
  78. data/lib/datadog/core/remote/negotiation.rb +4 -4
  79. data/lib/datadog/core/remote/tie.rb +2 -0
  80. data/lib/datadog/core/runtime/metrics.rb +1 -1
  81. data/lib/datadog/core/telemetry/component.rb +51 -2
  82. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  83. data/lib/datadog/core/telemetry/event.rb +37 -1
  84. data/lib/datadog/core/telemetry/ext.rb +1 -0
  85. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  86. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  87. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  88. data/lib/datadog/core/telemetry/logger.rb +51 -0
  89. data/lib/datadog/core/telemetry/logging.rb +71 -0
  90. data/lib/datadog/core/telemetry/request.rb +13 -1
  91. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  92. data/lib/datadog/core/utils/time.rb +12 -0
  93. data/lib/datadog/di/code_tracker.rb +168 -0
  94. data/lib/datadog/di/configuration/settings.rb +163 -0
  95. data/lib/datadog/di/configuration.rb +11 -0
  96. data/lib/datadog/di/error.rb +31 -0
  97. data/lib/datadog/di/extensions.rb +16 -0
  98. data/lib/datadog/di/probe.rb +133 -0
  99. data/lib/datadog/di/probe_builder.rb +41 -0
  100. data/lib/datadog/di/redactor.rb +188 -0
  101. data/lib/datadog/di/serializer.rb +193 -0
  102. data/lib/datadog/di.rb +14 -0
  103. data/lib/datadog/kit/appsec/events.rb +2 -4
  104. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  105. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  106. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  107. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
  109. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  110. data/lib/datadog/profiling/collectors/info.rb +15 -6
  111. data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
  112. data/lib/datadog/profiling/component.rb +89 -95
  113. data/lib/datadog/profiling/exporter.rb +3 -3
  114. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  115. data/lib/datadog/profiling/ext.rb +21 -21
  116. data/lib/datadog/profiling/flush.rb +1 -1
  117. data/lib/datadog/profiling/http_transport.rb +14 -7
  118. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  119. data/lib/datadog/profiling/preload.rb +1 -1
  120. data/lib/datadog/profiling/profiler.rb +5 -8
  121. data/lib/datadog/profiling/scheduler.rb +33 -25
  122. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  123. data/lib/datadog/profiling/tag_builder.rb +2 -2
  124. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  125. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  126. data/lib/datadog/profiling.rb +4 -5
  127. data/lib/datadog/single_step_instrument.rb +12 -0
  128. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  129. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  130. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  132. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  133. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  134. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  135. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
  136. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  137. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  138. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  139. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  141. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  142. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  143. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  144. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  145. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
  146. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  149. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  150. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  151. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  152. data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
  153. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  154. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  155. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  156. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  157. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  158. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  159. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  160. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  161. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  162. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  163. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  164. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  165. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  166. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  167. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  168. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  169. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  170. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  171. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  172. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  173. data/lib/datadog/tracing/metadata/ext.rb +6 -0
  174. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  175. data/lib/datadog/tracing/remote.rb +5 -2
  176. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  177. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  178. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  179. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  180. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  181. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  182. data/lib/datadog/tracing/span.rb +9 -2
  183. data/lib/datadog/tracing/span_event.rb +41 -0
  184. data/lib/datadog/tracing/span_operation.rb +6 -2
  185. data/lib/datadog/tracing/trace_operation.rb +26 -2
  186. data/lib/datadog/tracing/tracer.rb +14 -12
  187. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  188. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  190. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  191. data/lib/datadog/tracing/workers.rb +1 -1
  192. data/lib/datadog/version.rb +1 -1
  193. metadata +46 -11
  194. data/lib/datadog/profiling/crashtracker.rb +0 -91
  195. data/lib/datadog/profiling/ext/forking.rb +0 -98
  196. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -73,13 +73,15 @@ module Datadog
73
73
 
74
74
  attr_reader :diagnostics, :addresses
75
75
 
76
- def initialize(ruleset:)
76
+ def initialize(ruleset:, telemetry:)
77
77
  @diagnostics = nil
78
78
  @addresses = []
79
79
  settings = Datadog.configuration.appsec
80
+ @telemetry = telemetry
80
81
 
81
- unless load_libddwaf && create_waf_handle(settings, ruleset)
82
- Datadog.logger.warn { 'AppSec is disabled, see logged errors above' }
82
+ # TODO: Refactor to make it easier to test
83
+ unless require_libddwaf && libddwaf_provides_waf? && create_waf_handle(settings, ruleset)
84
+ Datadog.logger.warn('AppSec is disabled, see logged errors above')
83
85
  end
84
86
  end
85
87
 
@@ -97,8 +99,27 @@ module Datadog
97
99
 
98
100
  private
99
101
 
100
- def load_libddwaf
101
- Processor.require_libddwaf && Processor.libddwaf_provides_waf?
102
+ # libddwaf raises a LoadError on unsupported platforms; it may at some
103
+ # point succeed in being required yet not provide a specific needed feature.
104
+ def require_libddwaf
105
+ Datadog.logger.debug { "libddwaf platform: #{libddwaf_platform}" }
106
+
107
+ require 'libddwaf'
108
+
109
+ true
110
+ rescue LoadError => e
111
+ Datadog.logger.error do
112
+ 'libddwaf failed to load,' \
113
+ "installed platform: #{libddwaf_platform} ruby platforms: #{ruby_platforms} error: #{e.inspect}"
114
+ end
115
+ @telemetry.report(e, description: 'libddwaf failed to load')
116
+
117
+ false
118
+ end
119
+
120
+ # check whether libddwaf is required *and* able to provide the needed feature
121
+ def libddwaf_provides_waf?
122
+ defined?(Datadog::AppSec::WAF) ? true : false
102
123
  end
103
124
 
104
125
  def create_waf_handle(settings, ruleset)
@@ -119,6 +140,7 @@ module Datadog
119
140
  Datadog.logger.error do
120
141
  "libddwaf failed to initialize, error: #{e.inspect}"
121
142
  end
143
+ @telemetry.report(e, description: 'libddwaf failed to initialize')
122
144
 
123
145
  @diagnostics = e.diagnostics if e.diagnostics
124
146
 
@@ -127,44 +149,21 @@ module Datadog
127
149
  Datadog.logger.error do
128
150
  "libddwaf failed to initialize, error: #{e.inspect}"
129
151
  end
152
+ @telemetry.report(e, description: 'libddwaf failed to initialize')
130
153
 
131
154
  false
132
155
  end
133
156
 
134
- class << self
135
- # check whether libddwaf is required *and* able to provide the needed feature
136
- def libddwaf_provides_waf?
137
- defined?(Datadog::AppSec::WAF) ? true : false
138
- end
139
-
140
- # libddwaf raises a LoadError on unsupported platforms; it may at some
141
- # point succeed in being required yet not provide a specific needed feature.
142
- def require_libddwaf
143
- Datadog.logger.debug { "libddwaf platform: #{libddwaf_platform}" }
144
-
145
- require 'libddwaf'
146
-
147
- true
148
- rescue LoadError => e
149
- Datadog.logger.error do
150
- 'libddwaf failed to load,' \
151
- "installed platform: #{libddwaf_platform} ruby platforms: #{ruby_platforms} error: #{e.inspect}"
152
- end
153
-
154
- false
155
- end
156
-
157
- def libddwaf_spec
158
- Gem.loaded_specs['libddwaf']
159
- end
160
-
161
- def libddwaf_platform
162
- libddwaf_spec ? libddwaf_spec.platform.to_s : 'unknown'
157
+ def libddwaf_platform
158
+ if Gem.loaded_specs['libddwaf']
159
+ Gem.loaded_specs['libddwaf'].platform.to_s
160
+ else
161
+ 'unknown'
163
162
  end
163
+ end
164
164
 
165
- def ruby_platforms
166
- Gem.platforms.map(&:to_s)
167
- end
165
+ def ruby_platforms
166
+ Gem.platforms.map(&:to_s)
168
167
  end
169
168
  end
170
169
  end
@@ -1,60 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../core/rate_limiter'
4
+
3
5
  module Datadog
4
6
  module AppSec
5
- # Simple per-thread rate limiter
6
- # Since AppSec marks sampling to keep on a security event, this limits the flood of egress traces involving AppSec
7
+ # Per-thread rate limiter based on token bucket rate limiter.
8
+ #
9
+ # Since AppSec marks sampling to keep on a security event, this limits
10
+ # the flood of egress traces involving AppSec
7
11
  class RateLimiter
8
- def initialize(rate)
9
- @rate = rate
10
- @timestamps = []
11
- end
12
-
13
- def limit
14
- now = Time.now.to_f
15
-
16
- loop do
17
- oldest = @timestamps.first
18
-
19
- break if oldest.nil? || now - oldest < 1
20
-
21
- @timestamps.shift
22
- end
23
-
24
- @timestamps << now
25
-
26
- if (count = @timestamps.count) <= @rate
27
- yield
28
- else
29
- Datadog.logger.debug { "Rate limit hit: #{count}/#{@rate} AppSec traces/second" }
30
- end
31
- end
12
+ THREAD_KEY = :datadog_security_appsec_rate_limiter
32
13
 
33
14
  class << self
34
- def limit(name, &block)
35
- rate_limiter(name).limit(&block)
15
+ def thread_local
16
+ rate_limiter = Thread.current.thread_variable_get(THREAD_KEY)
17
+ return rate_limiter unless rate_limiter.nil?
18
+
19
+ Thread.current.thread_variable_set(THREAD_KEY, new(trace_rate_limit))
36
20
  end
37
21
 
38
22
  # reset a rate limiter: used for testing
39
- def reset!(name)
40
- Thread.current[:datadog_security_trace_rate_limiter] = nil
23
+ def reset!
24
+ Thread.current.thread_variable_set(THREAD_KEY, nil)
41
25
  end
42
26
 
43
- protected
44
-
45
- def rate_limiter(name)
46
- case name
47
- when :traces
48
- Thread.current[:datadog_security_trace_rate_limiter] ||= RateLimiter.new(trace_rate_limit)
49
- else
50
- raise "unsupported rate limiter: #{name.inspect}"
51
- end
52
- end
27
+ private
53
28
 
54
29
  def trace_rate_limit
55
30
  Datadog.configuration.appsec.trace_rate_limit
56
31
  end
57
32
  end
33
+
34
+ def initialize(rate)
35
+ @rate_limiter = Core::TokenBucket.new(rate)
36
+ end
37
+
38
+ def limit
39
+ return yield if @rate_limiter.allow?
40
+
41
+ Datadog.logger.debug { "Rate limit hit: #{@rate_limiter.current_window_rate} AppSec traces/second" }
42
+ end
58
43
  end
59
44
  end
60
45
  end
@@ -53,7 +53,7 @@ module Datadog
53
53
  end
54
54
 
55
55
  # rubocop:disable Metrics/MethodLength
56
- def receivers
56
+ def receivers(telemetry)
57
57
  return [] unless remote_features_enabled?
58
58
 
59
59
  matcher = Core::Remote::Dispatcher::Matcher::Product.new(ASM_PRODUCTS)
@@ -86,7 +86,10 @@ module Datadog
86
86
  end
87
87
 
88
88
  if rules.empty?
89
- settings_rules = AppSec::Processor::RuleLoader.load_rules(ruleset: Datadog.configuration.appsec.ruleset)
89
+ settings_rules = AppSec::Processor::RuleLoader.load_rules(
90
+ telemetry: telemetry,
91
+ ruleset: Datadog.configuration.appsec.ruleset
92
+ )
90
93
 
91
94
  raise NoRulesError, 'no default rules available' unless settings_rules
92
95
 
@@ -99,9 +102,10 @@ module Datadog
99
102
  overrides: overrides,
100
103
  exclusions: exclusions,
101
104
  custom_rules: custom_rules,
105
+ telemetry: telemetry
102
106
  )
103
107
 
104
- Datadog::AppSec.reconfigure(ruleset: ruleset, actions: actions)
108
+ Datadog::AppSec.reconfigure(ruleset: ruleset, actions: actions, telemetry: telemetry)
105
109
  end
106
110
 
107
111
  [receiver]
@@ -36,7 +36,7 @@ module Datadog
36
36
  # I rather use break to stop the execution
37
37
  next if configured_response
38
38
 
39
- action_configuration = AppSec::Processor::Actions.fecth_configuration(action)
39
+ action_configuration = AppSec::Processor::Actions.fetch_configuration(action)
40
40
  next unless action_configuration
41
41
 
42
42
  configured_response = case action_configuration['type']
@@ -50,6 +50,20 @@ module Datadog
50
50
  configured_response || default_response(env)
51
51
  end
52
52
 
53
+ def graphql_response(gateway_multiplex)
54
+ multiplex_return = []
55
+ gateway_multiplex.queries.each do |query|
56
+ # This method is only called in places where GraphQL-Ruby is already required
57
+ query_result = ::GraphQL::Query::Result.new(
58
+ query: query,
59
+ values: JSON.parse(content('application/json'))
60
+ )
61
+ multiplex_return << query_result
62
+ end
63
+
64
+ multiplex_return
65
+ end
66
+
53
67
  private
54
68
 
55
69
  def default_response(env)
@@ -23,12 +23,12 @@ module Datadog
23
23
  appsec_component.processor if appsec_component
24
24
  end
25
25
 
26
- def reconfigure(ruleset:, actions:)
26
+ def reconfigure(ruleset:, actions:, telemetry:)
27
27
  appsec_component = components.appsec
28
28
 
29
29
  return unless appsec_component
30
30
 
31
- appsec_component.reconfigure(ruleset: ruleset, actions: actions)
31
+ appsec_component.reconfigure(ruleset: ruleset, actions: actions, telemetry: telemetry)
32
32
  end
33
33
 
34
34
  def reconfigure_lock(&block)
@@ -56,5 +56,6 @@ require_relative 'appsec/contrib/rack/integration'
56
56
  require_relative 'appsec/contrib/sinatra/integration'
57
57
  require_relative 'appsec/contrib/rails/integration'
58
58
  require_relative 'appsec/contrib/devise/integration'
59
+ require_relative 'appsec/contrib/graphql/integration'
59
60
 
60
61
  require_relative 'appsec/autoload'
@@ -13,6 +13,7 @@ require_relative '../remote/component'
13
13
  require_relative '../../tracing/component'
14
14
  require_relative '../../profiling/component'
15
15
  require_relative '../../appsec/component'
16
+ require_relative '../crashtracking/component'
16
17
 
17
18
  module Datadog
18
19
  module Core
@@ -56,19 +57,18 @@ module Datadog
56
57
  end
57
58
 
58
59
  def build_telemetry(settings, agent_settings, logger)
59
- enabled = settings.telemetry.enabled
60
- if agent_settings.adapter != Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
61
- enabled = false
62
- logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
60
+ Telemetry::Component.build(settings, agent_settings, logger)
61
+ end
62
+
63
+ def build_crashtracker(settings, agent_settings, logger:)
64
+ return unless settings.crashtracking.enabled
65
+
66
+ if (libdatadog_api_failure = Datadog::Core::Crashtracking::Component::LIBDATADOG_API_FAILURE)
67
+ logger.debug("Cannot enable crashtracking: #{libdatadog_api_failure}")
68
+ return
63
69
  end
64
70
 
65
- Telemetry::Component.new(
66
- enabled: enabled,
67
- metrics_enabled: enabled && settings.telemetry.metrics_enabled,
68
- heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
69
- metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
70
- dependency_collection: settings.telemetry.dependency_collection
71
- )
71
+ Datadog::Core::Crashtracking::Component.build(settings, agent_settings, logger: logger)
72
72
  end
73
73
  end
74
74
 
@@ -82,6 +82,7 @@ module Datadog
82
82
  :runtime_metrics,
83
83
  :telemetry,
84
84
  :tracer,
85
+ :crashtracker,
85
86
  :appsec
86
87
 
87
88
  def initialize(settings)
@@ -93,20 +94,22 @@ module Datadog
93
94
  # the Core resolver from within your product/component's namespace.
94
95
  agent_settings = AgentSettingsResolver.call(settings, logger: @logger)
95
96
 
96
- @remote = Remote::Component.build(settings, agent_settings)
97
+ @telemetry = self.class.build_telemetry(settings, agent_settings, @logger)
98
+
99
+ @remote = Remote::Component.build(settings, agent_settings, telemetry: telemetry)
97
100
  @tracer = self.class.build_tracer(settings, agent_settings, logger: @logger)
101
+ @crashtracker = self.class.build_crashtracker(settings, agent_settings, logger: @logger)
98
102
 
99
103
  @profiler, profiler_logger_extra = Datadog::Profiling::Component.build_profiler_component(
100
104
  settings: settings,
101
105
  agent_settings: agent_settings,
102
- optional_tracer: @tracer,
106
+ optional_tracer: @tracer
103
107
  )
104
108
  @environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
105
109
 
106
110
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings)
107
111
  @health_metrics = self.class.build_health_metrics(settings)
108
- @telemetry = self.class.build_telemetry(settings, agent_settings, logger)
109
- @appsec = Datadog::AppSec::Component.build_appsec_component(settings)
112
+ @appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
110
113
 
111
114
  self.class.configure_tracing(settings)
112
115
  end
@@ -301,6 +301,16 @@ module Datadog
301
301
  o.default true
302
302
  end
303
303
 
304
+ # Can be used to enable/disable the Datadog::Profiling.allocation_count feature.
305
+ #
306
+ # Requires allocation profiling to be enabled.
307
+ #
308
+ # @default false
309
+ option :allocation_counting_enabled do |o|
310
+ o.type :bool
311
+ o.default false
312
+ end
313
+
304
314
  # Can be used to enable/disable the collection of heap profiles.
305
315
  #
306
316
  # This feature is alpha and disabled by default
@@ -400,10 +410,8 @@ module Datadog
400
410
  # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
401
411
  #
402
412
  # We've discovered that this can trigger a bug in a number of Ruby APIs in the `Dir` class, as
403
- # described in https://github.com/DataDog/dd-trace-rb/issues/3450 . This workaround prevents the issue
404
- # from happening by monkey patching the affected APIs.
405
- #
406
- # (In the future, once a fix lands upstream, we'll disable this workaround for Rubies that don't need it)
413
+ # described in https://bugs.ruby-lang.org/issues/20586 .
414
+ # This was fixed for Ruby 3.4+, and this setting is a no-op for those versions.
407
415
  #
408
416
  # @default `DD_PROFILING_DIR_INTERRUPTION_WORKAROUND_ENABLED` environment variable as a boolean,
409
417
  # otherwise `true`
@@ -441,14 +449,70 @@ module Datadog
441
449
  o.default 60
442
450
  end
443
451
 
444
- # Enables reporting of information when the Ruby VM crashes.
445
- #
446
- # @default `DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED` environment variable as a boolean,
447
- # otherwise `false`
452
+ # DEV-3.0: Remove `experimental_crash_tracking_enabled` option
448
453
  option :experimental_crash_tracking_enabled do |o|
454
+ o.after_set do |_, _, precedence|
455
+ unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
456
+ Core.log_deprecation(key: :experimental_crash_tracking_enabled) do
457
+ 'The profiling.advanced.experimental_crash_tracking_enabled setting has been deprecated for removal '\
458
+ 'and no longer does anything. Please remove it from your Datadog.configure block.'
459
+ end
460
+ end
461
+ end
462
+ end
463
+
464
+ # Enables GVL profiling. This will show when threads are waiting for GVL in the timeline view.
465
+ #
466
+ # This is a preview feature and disabled by default. It requires Ruby 3.2+.
467
+ #
468
+ # @default `DD_PROFILING_PREVIEW_GVL_ENABLED` environment variable as a boolean, otherwise `false`
469
+ option :preview_gvl_enabled do |o|
449
470
  o.type :bool
450
- o.env 'DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED'
471
+ o.env 'DD_PROFILING_PREVIEW_GVL_ENABLED'
472
+ o.default false
473
+ end
474
+
475
+ # Controls the smallest time period the profiler will report a thread waiting for the GVL.
476
+ #
477
+ # The default value was set to minimize overhead. Periods smaller than the set value will not be reported (e.g.
478
+ # the thread will be reported as whatever it was doing before it waited for the GVL).
479
+ #
480
+ # We do not recommend setting this to less than 1ms. Tweaking this value can increase application latency and
481
+ # memory use.
482
+ #
483
+ # @default 10_000_000 (10ms)
484
+ option :waiting_for_gvl_threshold_ns do |o|
485
+ o.type :int
486
+ o.default 10_000_000
487
+ end
488
+
489
+ # Controls if the profiler should attempt to read context from the otel library
490
+ #
491
+ # @default false
492
+ option :preview_otel_context_enabled do |o|
493
+ o.env 'DD_PROFILING_PREVIEW_OTEL_CONTEXT_ENABLED'
451
494
  o.default false
495
+ o.env_parser do |value|
496
+ if value
497
+ value = value.strip.downcase
498
+ if ['only', 'both'].include?(value)
499
+ value
500
+ elsif ['true', '1'].include?(value)
501
+ 'both'
502
+ else
503
+ 'false'
504
+ end
505
+ end
506
+ end
507
+ o.setter do |value|
508
+ if value == true
509
+ :both
510
+ elsif ['only', 'both', :only, :both].include?(value)
511
+ value.to_sym
512
+ else
513
+ false
514
+ end
515
+ end
452
516
  end
453
517
  end
454
518
 
@@ -618,6 +682,33 @@ module Datadog
618
682
  end
619
683
  end
620
684
 
685
+ # The monotonic clock time provider used by Datadog. This option is internal and is used by `datadog-ci`
686
+ # gem to avoid traces' durations being skewed by timecop.
687
+ #
688
+ # It must respect the interface of [Datadog::Core::Utils::Time#get_time] method.
689
+ #
690
+ # For [Timecop](https://rubygems.org/gems/timecop), for example,
691
+ # `->(unit = :float_second) { ::Process.clock_gettime_without_mock(::Process::CLOCK_MONOTONIC, unit) }`
692
+ # allows Datadog features to use the real monotonic time when time is frozen with
693
+ # `Timecop.mock_process_clock = true`.
694
+ #
695
+ # @default `->(unit = :float_second) { ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, unit)}`
696
+ # @return [Proc<Numeric>]
697
+ option :get_time_provider do |o|
698
+ o.default_proc { |unit = :float_second| ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, unit) }
699
+ o.type :proc
700
+
701
+ o.after_set do |get_time_provider|
702
+ Core::Utils::Time.get_time_provider = get_time_provider
703
+ end
704
+
705
+ o.resetter do |_value|
706
+ ->(unit = :float_second) { ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, unit) }.tap do |default|
707
+ Core::Utils::Time.get_time_provider = default
708
+ end
709
+ end
710
+ end
711
+
621
712
  # The `version` tag in Datadog. Use it to enable [Deployment Tracking](https://docs.datadoghq.com/tracing/deployment_tracking/).
622
713
  # @see https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging
623
714
  # @default `DD_VERSION` environment variable, otherwise `nils`
@@ -663,6 +754,24 @@ module Datadog
663
754
  o.type :bool
664
755
  end
665
756
 
757
+ # Enable agentless mode for telemetry: submit telemetry events directly to the intake without Datadog Agent.
758
+ #
759
+ # @return [Boolean]
760
+ # @!visibility private
761
+ option :agentless_enabled do |o|
762
+ o.type :bool
763
+ o.default false
764
+ end
765
+
766
+ # Overrides agentless telemetry URL. To be used internally for testing purposes only.
767
+ #
768
+ # @return [String]
769
+ # @!visibility private
770
+ option :agentless_url_override do |o|
771
+ o.type :string, nilable: true
772
+ o.env Core::Telemetry::Ext::ENV_AGENTLESS_URL_OVERRIDE
773
+ end
774
+
666
775
  # Enable metrics collection for telemetry. Metrics collection only works when telemetry is enabled and
667
776
  # metrics are enabled.
668
777
  # @default `DD_TELEMETRY_METRICS_ENABLED` environment variable, otherwise `true`.
@@ -734,6 +843,14 @@ module Datadog
734
843
  o.type :string, nilable: true
735
844
  o.env Core::Telemetry::Ext::ENV_INSTALL_TIME
736
845
  end
846
+
847
+ # Telemetry shutdown timeout in seconds
848
+ #
849
+ # @!visibility private
850
+ option :shutdown_timeout_seconds do |o|
851
+ o.type :float
852
+ o.default 1.0
853
+ end
737
854
  end
738
855
 
739
856
  # Remote configuration
@@ -794,6 +911,15 @@ module Datadog
794
911
  option :service
795
912
  end
796
913
 
914
+ settings :crashtracking do
915
+ # Enables reporting of information when Ruby VM crashes.
916
+ option :enabled do |o|
917
+ o.type :bool
918
+ o.default false
919
+ o.env 'DD_CRASHTRACKING_ENABLED'
920
+ end
921
+ end
922
+
797
923
  # TODO: Tracing should manage its own settings.
798
924
  # Keep this extension here for now to keep things working.
799
925
  extend Datadog::Tracing::Configuration::Settings
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../configuration/ext'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Crashtracking
8
+ # This module provides a method to resolve the base URL of the agent
9
+ module AgentBaseUrl
10
+ def self.resolve(agent_settings)
11
+ case agent_settings.adapter
12
+ when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
13
+ "#{agent_settings.ssl ? 'https' : 'http'}://#{agent_settings.hostname}:#{agent_settings.port}/"
14
+ when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
15
+ "unix://#{agent_settings.uds_path}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'libdatadog'
4
+
5
+ require_relative 'tag_builder'
6
+ require_relative 'agent_base_url'
7
+ require_relative '../utils/only_once'
8
+ require_relative '../utils/at_fork_monkey_patch'
9
+
10
+ module Datadog
11
+ module Core
12
+ module Crashtracking
13
+ # Used to report Ruby VM crashes.
14
+ #
15
+ # NOTE: The crashtracker native state is a singleton;
16
+ # so even if you create multiple instances of `Crashtracking::Component` and start them,
17
+ # it only works as "last writer wins". Same for stop -- there's only one state, so calling stop
18
+ # on it will stop the crash tracker, regardless of which instance started it.
19
+ #
20
+ # Methods prefixed with _native_ are implemented in `crashtracker.c`
21
+ class Component
22
+ LIBDATADOG_API_FAILURE =
23
+ begin
24
+ require "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
25
+ nil
26
+ rescue LoadError => e
27
+ e.message
28
+ end
29
+
30
+ ONLY_ONCE = Core::Utils::OnlyOnce.new
31
+
32
+ def self.build(settings, agent_settings, logger:)
33
+ 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
36
+
37
+ ld_library_path = ::Libdatadog.ld_library_path
38
+ logger.warn('Missing ld_library_path; cannot enable crash tracking') unless ld_library_path
39
+
40
+ path_to_crashtracking_receiver_binary = ::Libdatadog.path_to_crashtracking_receiver_binary
41
+ unless path_to_crashtracking_receiver_binary
42
+ logger.warn('Missing path_to_crashtracking_receiver_binary; cannot enable crash tracking')
43
+ end
44
+
45
+ return unless agent_base_url
46
+ return unless ld_library_path
47
+ return unless path_to_crashtracking_receiver_binary
48
+
49
+ new(
50
+ tags: tags,
51
+ agent_base_url: agent_base_url,
52
+ ld_library_path: ld_library_path,
53
+ path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary,
54
+ logger: logger
55
+ ).tap(&:start)
56
+ end
57
+
58
+ def initialize(tags:, agent_base_url:, ld_library_path:, path_to_crashtracking_receiver_binary:, logger:)
59
+ @tags = tags
60
+ @agent_base_url = agent_base_url
61
+ @ld_library_path = ld_library_path
62
+ @path_to_crashtracking_receiver_binary = path_to_crashtracking_receiver_binary
63
+ @logger = logger
64
+ end
65
+
66
+ def start
67
+ Utils::AtForkMonkeyPatch.apply!
68
+
69
+ start_or_update_on_fork(action: :start)
70
+ ONLY_ONCE.run do
71
+ Utils::AtForkMonkeyPatch.at_fork(:child) do
72
+ # Must NOT reference `self` here, as only the first instance will
73
+ # be captured by the ONLY_ONCE and we want to pick the latest active one
74
+ # (which may have different tags or agent config)
75
+ Datadog.send(:components).crashtracker&.update_on_fork
76
+ end
77
+ end
78
+ end
79
+
80
+ def update_on_fork
81
+ start_or_update_on_fork(action: :update_on_fork)
82
+ end
83
+
84
+ def stop
85
+ self.class._native_stop
86
+ logger.debug('Crash tracking stopped successfully')
87
+ rescue => e
88
+ logger.error("Failed to stop crash tracking: #{e.message}")
89
+ end
90
+
91
+ private
92
+
93
+ attr_reader :tags, :agent_base_url, :ld_library_path, :path_to_crashtracking_receiver_binary, :logger
94
+
95
+ def start_or_update_on_fork(action:)
96
+ self.class._native_start_or_update_on_fork(
97
+ action: action,
98
+ agent_base_url: agent_base_url,
99
+ path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary,
100
+ ld_library_path: ld_library_path,
101
+ tags_as_array: tags.to_a,
102
+ upload_timeout_seconds: 1
103
+ )
104
+ logger.debug("Crash tracking #{action} successfully")
105
+ rescue => e
106
+ logger.error("Failed to #{action} crash tracking: #{e.message}")
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end