datadog 2.3.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +10 -22
  5. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +3 -3
  6. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +198 -41
  7. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -2
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +89 -46
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +645 -107
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +15 -1
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +0 -27
  12. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -4
  13. data/ext/datadog_profiling_native_extension/extconf.rb +42 -25
  14. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  15. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  16. data/ext/datadog_profiling_native_extension/heap_recorder.c +194 -34
  17. data/ext/datadog_profiling_native_extension/heap_recorder.h +11 -0
  18. data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
  19. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +1 -1
  20. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +53 -2
  21. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
  22. data/ext/datadog_profiling_native_extension/profiling.c +1 -1
  23. data/ext/datadog_profiling_native_extension/ruby_helpers.c +14 -11
  24. data/ext/datadog_profiling_native_extension/stack_recorder.c +58 -22
  25. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  26. data/ext/libdatadog_api/crashtracker.c +20 -18
  27. data/ext/libdatadog_api/datadog_ruby_common.c +0 -27
  28. data/ext/libdatadog_api/datadog_ruby_common.h +0 -4
  29. data/ext/libdatadog_extconf_helpers.rb +1 -1
  30. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  31. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  32. data/lib/datadog/appsec/component.rb +29 -8
  33. data/lib/datadog/appsec/configuration/settings.rb +10 -2
  34. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  35. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  36. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  37. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +0 -14
  38. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +67 -31
  39. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +14 -15
  40. data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
  41. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +7 -20
  42. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
  43. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -15
  44. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +6 -18
  45. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +7 -20
  46. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +5 -18
  47. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -1
  48. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -5
  49. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +5 -18
  50. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -10
  51. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +7 -20
  52. data/lib/datadog/appsec/event.rb +25 -1
  53. data/lib/datadog/appsec/ext.rb +4 -0
  54. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -5
  55. data/lib/datadog/appsec/monitor/reactive/set_user.rb +7 -20
  56. data/lib/datadog/appsec/processor/context.rb +109 -0
  57. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  58. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  59. data/lib/datadog/appsec/processor.rb +42 -107
  60. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  61. data/lib/datadog/appsec/remote.rb +7 -3
  62. data/lib/datadog/appsec/scope.rb +1 -4
  63. data/lib/datadog/appsec/utils/trace_operation.rb +15 -0
  64. data/lib/datadog/appsec/utils.rb +2 -0
  65. data/lib/datadog/appsec.rb +3 -2
  66. data/lib/datadog/core/configuration/agent_settings_resolver.rb +26 -25
  67. data/lib/datadog/core/configuration/components.rb +4 -3
  68. data/lib/datadog/core/configuration/settings.rb +96 -5
  69. data/lib/datadog/core/configuration.rb +1 -3
  70. data/lib/datadog/core/crashtracking/component.rb +9 -6
  71. data/lib/datadog/core/environment/execution.rb +5 -5
  72. data/lib/datadog/core/environment/yjit.rb +5 -0
  73. data/lib/datadog/core/metrics/client.rb +7 -0
  74. data/lib/datadog/core/rate_limiter.rb +183 -0
  75. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  76. data/lib/datadog/core/remote/component.rb +4 -2
  77. data/lib/datadog/core/remote/negotiation.rb +4 -4
  78. data/lib/datadog/core/remote/tie.rb +2 -0
  79. data/lib/datadog/core/remote/transport/http.rb +5 -0
  80. data/lib/datadog/core/remote/worker.rb +1 -1
  81. data/lib/datadog/core/runtime/ext.rb +1 -0
  82. data/lib/datadog/core/runtime/metrics.rb +5 -1
  83. data/lib/datadog/core/semaphore.rb +35 -0
  84. data/lib/datadog/core/telemetry/component.rb +2 -0
  85. data/lib/datadog/core/telemetry/event.rb +12 -7
  86. data/lib/datadog/core/telemetry/logger.rb +51 -0
  87. data/lib/datadog/core/telemetry/logging.rb +50 -14
  88. data/lib/datadog/core/telemetry/request.rb +13 -1
  89. data/lib/datadog/core/transport/ext.rb +1 -0
  90. data/lib/datadog/core/utils/time.rb +12 -0
  91. data/lib/datadog/core/workers/async.rb +1 -1
  92. data/lib/datadog/di/code_tracker.rb +166 -0
  93. data/lib/datadog/di/configuration/settings.rb +163 -0
  94. data/lib/datadog/di/configuration.rb +11 -0
  95. data/lib/datadog/di/error.rb +31 -0
  96. data/lib/datadog/di/extensions.rb +16 -0
  97. data/lib/datadog/di/instrumenter.rb +301 -0
  98. data/lib/datadog/di/probe.rb +162 -0
  99. data/lib/datadog/di/probe_builder.rb +47 -0
  100. data/lib/datadog/di/probe_notification_builder.rb +207 -0
  101. data/lib/datadog/di/probe_notifier_worker.rb +244 -0
  102. data/lib/datadog/di/redactor.rb +188 -0
  103. data/lib/datadog/di/serializer.rb +215 -0
  104. data/lib/datadog/di/transport.rb +67 -0
  105. data/lib/datadog/di/utils.rb +39 -0
  106. data/lib/datadog/di.rb +57 -0
  107. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -10
  109. data/lib/datadog/profiling/collectors/info.rb +12 -3
  110. data/lib/datadog/profiling/collectors/thread_context.rb +32 -8
  111. data/lib/datadog/profiling/component.rb +21 -4
  112. data/lib/datadog/profiling/http_transport.rb +6 -1
  113. data/lib/datadog/profiling/scheduler.rb +2 -0
  114. data/lib/datadog/profiling/stack_recorder.rb +40 -9
  115. data/lib/datadog/single_step_instrument.rb +12 -0
  116. data/lib/datadog/tracing/component.rb +13 -0
  117. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  118. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  119. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  120. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  121. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  122. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  123. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  124. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -1
  125. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  126. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  127. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  128. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -0
  129. data/lib/datadog/tracing/contrib/excon/middleware.rb +3 -0
  130. data/lib/datadog/tracing/contrib/faraday/middleware.rb +12 -0
  131. data/lib/datadog/tracing/contrib/grape/endpoint.rb +24 -2
  132. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  133. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  134. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  135. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +13 -9
  136. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +6 -3
  137. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +9 -0
  138. data/lib/datadog/tracing/contrib/http/instrumentation.rb +22 -15
  139. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +10 -5
  140. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  141. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +9 -0
  142. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  143. data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -2
  144. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  145. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  146. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  147. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  148. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  149. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  150. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  151. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +3 -0
  152. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  153. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  154. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  155. data/lib/datadog/tracing/metadata/ext.rb +2 -0
  156. data/lib/datadog/tracing/remote.rb +5 -2
  157. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  158. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  159. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  160. data/lib/datadog/tracing/sampling/rule_sampler.rb +15 -9
  161. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  162. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  163. data/lib/datadog/tracing/trace_operation.rb +26 -2
  164. data/lib/datadog/tracing/tracer.rb +29 -22
  165. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  166. data/lib/datadog/tracing/transport/http.rb +4 -0
  167. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  168. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  169. data/lib/datadog/tracing/workers.rb +2 -2
  170. data/lib/datadog/tracing/writer.rb +26 -28
  171. data/lib/datadog/version.rb +1 -1
  172. metadata +40 -15
  173. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -20,17 +20,41 @@ module Datadog
20
20
  tracer:,
21
21
  endpoint_collection_enabled:,
22
22
  timeline_enabled:,
23
- allocation_type_enabled: true
23
+ waiting_for_gvl_threshold_ns:,
24
+ otel_context_enabled:
24
25
  )
25
26
  tracer_context_key = safely_extract_context_key_from(tracer)
26
27
  self.class._native_initialize(
27
- self,
28
- recorder,
29
- max_frames,
30
- tracer_context_key,
31
- endpoint_collection_enabled,
32
- timeline_enabled,
33
- allocation_type_enabled,
28
+ self_instance: self,
29
+ recorder: recorder,
30
+ max_frames: max_frames,
31
+ tracer_context_key: tracer_context_key,
32
+ endpoint_collection_enabled: endpoint_collection_enabled,
33
+ timeline_enabled: timeline_enabled,
34
+ waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
35
+ otel_context_enabled: otel_context_enabled,
36
+ )
37
+ end
38
+
39
+ def self.for_testing(
40
+ recorder:,
41
+ max_frames: 400,
42
+ tracer: nil,
43
+ endpoint_collection_enabled: false,
44
+ timeline_enabled: false,
45
+ waiting_for_gvl_threshold_ns: 10_000_000,
46
+ otel_context_enabled: false,
47
+ **options
48
+ )
49
+ new(
50
+ recorder: recorder,
51
+ max_frames: max_frames,
52
+ tracer: tracer,
53
+ endpoint_collection_enabled: endpoint_collection_enabled,
54
+ timeline_enabled: timeline_enabled,
55
+ waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
56
+ otel_context_enabled: otel_context_enabled,
57
+ **options,
34
58
  )
35
59
  end
36
60
 
@@ -53,6 +53,7 @@ module Datadog
53
53
  heap_size_enabled: heap_size_profiling_enabled,
54
54
  heap_sample_every: heap_sample_every,
55
55
  timeline_enabled: timeline_enabled,
56
+ heap_clean_after_gc_enabled: settings.profiling.advanced.heap_clean_after_gc_enabled,
56
57
  )
57
58
  thread_context_collector = build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
58
59
  worker = Datadog::Profiling::Collectors::CpuAndWallTimeWorker.new(
@@ -62,6 +63,7 @@ module Datadog
62
63
  dynamic_sampling_rate_overhead_target_percentage: overhead_target_percentage,
63
64
  allocation_profiling_enabled: allocation_profiling_enabled,
64
65
  allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
66
+ gvl_profiling_enabled: enable_gvl_profiling?(settings),
65
67
  )
66
68
 
67
69
  internal_metadata = {
@@ -89,6 +91,8 @@ module Datadog
89
91
  tracer: optional_tracer,
90
92
  endpoint_collection_enabled: settings.profiling.advanced.endpoint.collection.enabled,
91
93
  timeline_enabled: timeline_enabled,
94
+ waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
95
+ otel_context_enabled: settings.profiling.advanced.preview_otel_context_enabled,
92
96
  )
93
97
  end
94
98
 
@@ -252,6 +256,7 @@ module Datadog
252
256
  legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?("2.5.")
253
257
 
254
258
  unless [true, false, :auto].include?(setting_value)
259
+ # TODO: Replace with a warning instead.
255
260
  Datadog.logger.error(
256
261
  "Ignoring invalid value for profiling no_signals_workaround_enabled setting: #{setting_value.inspect}. " \
257
262
  "Valid options are `true`, `false` or (default) `:auto`."
@@ -394,6 +399,7 @@ module Datadog
394
399
  if overhead_target_percentage > 0 && overhead_target_percentage <= 20
395
400
  overhead_target_percentage
396
401
  else
402
+ # TODO: Replace with a warning instead.
397
403
  Datadog.logger.error(
398
404
  "Ignoring invalid value for profiling overhead_target_percentage setting: " \
399
405
  "#{overhead_target_percentage.inspect}. Falling back to default value."
@@ -433,13 +439,24 @@ module Datadog
433
439
  end
434
440
 
435
441
  private_class_method def self.dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
436
- return false if no_signals_workaround_enabled
437
-
438
- # NOTE: In the future this method will evolve to check for Ruby versions affected and not apply the workaround
439
- # when it's not needed but currently all known Ruby versions are affected.
442
+ return false if no_signals_workaround_enabled || RUBY_VERSION >= "3.4"
440
443
 
441
444
  settings.profiling.advanced.dir_interruption_workaround_enabled
442
445
  end
446
+
447
+ private_class_method def self.enable_gvl_profiling?(settings)
448
+ if RUBY_VERSION < "3.2"
449
+ if settings.profiling.advanced.preview_gvl_enabled
450
+ Datadog.logger.warn("GVL profiling is currently not supported in Ruby < 3.2 and will not be enabled.")
451
+ end
452
+
453
+ return false
454
+ end
455
+
456
+ # GVL profiling only makes sense in the context of timeline. We could emit a warning here, but not sure how
457
+ # useful it is -- if a customer disables timeline, there's nowhere to look for GVL profiling anyway!
458
+ settings.profiling.advanced.timeline_enabled && settings.profiling.advanced.preview_gvl_enabled
459
+ end
443
460
  end
444
461
  end
445
462
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../core/transport/ext"
4
+ require_relative "../core/telemetry/logger"
4
5
 
5
6
  module Datadog
6
7
  module Profiling
@@ -60,10 +61,14 @@ module Datadog
60
61
  "Failed to report profiling data (#{config_without_api_key}): " \
61
62
  "server returned unexpected HTTP #{result} status code"
62
63
  )
64
+ Datadog::Core::Telemetry::Logger.error(
65
+ "Failed to report profiling data: unexpected HTTP #{result} status code"
66
+ )
63
67
  false
64
68
  end
65
69
  else
66
70
  Datadog.logger.error("Failed to report profiling data (#{config_without_api_key}): #{result}")
71
+ Datadog::Core::Telemetry::Logger.error("Failed to report profiling data")
67
72
  false
68
73
  end
69
74
  end
@@ -134,7 +139,7 @@ module Datadog
134
139
  end
135
140
 
136
141
  def config_without_api_key
137
- [exporter_configuration[0..1]].to_h
142
+ "#{exporter_configuration[0]}: #{exporter_configuration[1]}"
138
143
  end
139
144
  end
140
145
  end
@@ -4,6 +4,7 @@ require_relative "../core/utils/time"
4
4
 
5
5
  require_relative "../core/worker"
6
6
  require_relative "../core/workers/polling"
7
+ require_relative "../core/telemetry/logger"
7
8
 
8
9
  module Datadog
9
10
  module Profiling
@@ -134,6 +135,7 @@ module Datadog
134
135
  Datadog.logger.error(
135
136
  "Unable to report profile. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
136
137
  )
138
+ Datadog::Core::Telemetry::Logger.report(e, description: "Unable to report profile")
137
139
  end
138
140
 
139
141
  true
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../core/telemetry/logger"
4
+
3
5
  module Datadog
4
6
  module Profiling
5
7
  # Stores stack samples in a native libdatadog data structure and expose Ruby-level serialization APIs
@@ -7,8 +9,13 @@ module Datadog
7
9
  # Methods prefixed with _native_ are implemented in `stack_recorder.c`
8
10
  class StackRecorder
9
11
  def initialize(
10
- cpu_time_enabled:, alloc_samples_enabled:, heap_samples_enabled:, heap_size_enabled:,
11
- heap_sample_every:, timeline_enabled:
12
+ cpu_time_enabled:,
13
+ alloc_samples_enabled:,
14
+ heap_samples_enabled:,
15
+ heap_size_enabled:,
16
+ heap_sample_every:,
17
+ timeline_enabled:,
18
+ heap_clean_after_gc_enabled:
12
19
  )
13
20
  # This mutex works in addition to the fancy C-level mutexes we have in the native side (see the docs there).
14
21
  # It prevents multiple Ruby threads calling serialize at the same time -- something like
@@ -19,13 +26,36 @@ module Datadog
19
26
  @no_concurrent_synchronize_mutex = Mutex.new
20
27
 
21
28
  self.class._native_initialize(
22
- self,
23
- cpu_time_enabled,
24
- alloc_samples_enabled,
25
- heap_samples_enabled,
26
- heap_size_enabled,
27
- heap_sample_every,
28
- timeline_enabled,
29
+ self_instance: self,
30
+ cpu_time_enabled: cpu_time_enabled,
31
+ alloc_samples_enabled: alloc_samples_enabled,
32
+ heap_samples_enabled: heap_samples_enabled,
33
+ heap_size_enabled: heap_size_enabled,
34
+ heap_sample_every: heap_sample_every,
35
+ timeline_enabled: timeline_enabled,
36
+ heap_clean_after_gc_enabled: heap_clean_after_gc_enabled,
37
+ )
38
+ end
39
+
40
+ def self.for_testing(
41
+ cpu_time_enabled: true,
42
+ alloc_samples_enabled: false,
43
+ heap_samples_enabled: false,
44
+ heap_size_enabled: false,
45
+ heap_sample_every: 1,
46
+ timeline_enabled: false,
47
+ heap_clean_after_gc_enabled: true,
48
+ **options
49
+ )
50
+ new(
51
+ cpu_time_enabled: cpu_time_enabled,
52
+ alloc_samples_enabled: alloc_samples_enabled,
53
+ heap_samples_enabled: heap_samples_enabled,
54
+ heap_size_enabled: heap_size_enabled,
55
+ heap_sample_every: heap_sample_every,
56
+ timeline_enabled: timeline_enabled,
57
+ heap_clean_after_gc_enabled: heap_clean_after_gc_enabled,
58
+ **options,
29
59
  )
30
60
  end
31
61
 
@@ -42,6 +72,7 @@ module Datadog
42
72
  error_message = result
43
73
 
44
74
  Datadog.logger.error("Failed to serialize profiling data: #{error_message}")
75
+ Datadog::Core::Telemetry::Logger.error("Failed to serialize profiling data")
45
76
 
46
77
  nil
47
78
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Entrypoint file for single step instrumentation.
5
+ #
6
+ # This file's path is private. Do not reference this file.
7
+ #
8
+ begin
9
+ require_relative 'auto_instrument'
10
+ rescue StandardError, LoadError => e
11
+ warn "Single step instrumentation failed: #{e.class}:#{e.message}\n\tSource:\n\t#{Array(e.backtrace).join("\n\t")}"
12
+ end
@@ -73,6 +73,19 @@ module Datadog
73
73
  return sampler
74
74
  end
75
75
 
76
+ # AppSec events are sent to the backend using traces.
77
+ # Standalone ASM billing means that we don't want to charge clients for APM traces,
78
+ # so we want to send the minimum amount of traces possible (idealy only traces that contains security events),
79
+ # but for features such as API Security, we need to send at least one trace per minute,
80
+ # to keep the service alive on the backend side.
81
+ if settings.appsec.standalone.enabled
82
+ post_sampler = Tracing::Sampling::RuleSampler.new(
83
+ [Tracing::Sampling::SimpleRule.new(sample_rate: 1.0)],
84
+ rate_limiter: Datadog::Core::TokenBucket.new(1.0 / 60, 1.0),
85
+ default_sample_rate: 1.0 / 60
86
+ )
87
+ end
88
+
76
89
  # Sampling rules are provided
77
90
  if (rules = settings.tracing.sampling.rules)
78
91
  post_sampler = Tracing::Sampling::RuleSampler.parse(
@@ -14,21 +14,17 @@ module Datadog
14
14
  module ActionCableConnection
15
15
  def on_open
16
16
  Tracing.trace(Ext::SPAN_ON_OPEN) do |span, trace|
17
- begin
18
- span.resource = "#{self.class}#on_open"
19
- span.type = Tracing::Metadata::Ext::AppTypes::TYPE_WEB
17
+ span.resource = "#{self.class}#on_open"
18
+ span.type = Tracing::Metadata::Ext::AppTypes::TYPE_WEB
20
19
 
21
- span.set_tag(Ext::TAG_ACTION, 'on_open')
22
- span.set_tag(Ext::TAG_CONNECTION, self.class.to_s)
20
+ span.set_tag(Ext::TAG_ACTION, 'on_open')
21
+ span.set_tag(Ext::TAG_CONNECTION, self.class.to_s)
23
22
 
24
- span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
25
- span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_ON_OPEN)
23
+ span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
24
+ span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_ON_OPEN)
26
25
 
27
- # Set the resource name of the trace
28
- trace.resource = span.resource
29
- rescue StandardError => e
30
- Datadog.logger.error("Error preparing span for ActionCable::Connection: #{e}")
31
- end
26
+ # Set the resource name of the trace
27
+ trace.resource = span.resource
32
28
 
33
29
  super
34
30
  end
@@ -7,6 +7,7 @@ require_relative '../ext'
7
7
  require_relative '../utils'
8
8
  require_relative '../../rack/middlewares'
9
9
  require_relative '../../analytics'
10
+ require_relative '../../../../core/telemetry/logger'
10
11
 
11
12
  module Datadog
12
13
  module Tracing
@@ -43,6 +44,7 @@ module Datadog
43
44
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CONTROLLER)
44
45
  rescue StandardError => e
45
46
  Datadog.logger.error(e.message)
47
+ Datadog::Core::Telemetry::Logger.report(e)
46
48
  end
47
49
 
48
50
  def finish_processing(payload)
@@ -81,10 +83,13 @@ module Datadog
81
83
  end
82
84
  rescue StandardError => e
83
85
  Datadog.logger.error(e.message)
86
+ Datadog::Core::Telemetry::Logger.report(e)
84
87
  end
85
88
 
86
89
  # Instrumentation for ActionController::Metal
87
90
  module Metal
91
+ # TODO: Refactor this method to avoid using async API that splits the logic
92
+ # into two different methods (`start_processing` and `finish_processing`)
88
93
  def process_action(*args)
89
94
  # mutable payload with a tracing context that is used in two different
90
95
  # signals; it propagates the request span so that it can be finished
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../metadata/ext'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module ActionPack
9
+ module ActionDispatch
10
+ # Instrumentation for ActionDispatch components
11
+ module Instrumentation
12
+ module_function
13
+
14
+ def set_http_route_tags(route_spec, script_name)
15
+ return unless Tracing.enabled?
16
+
17
+ return unless route_spec
18
+
19
+ request_trace = Tracing.active_trace
20
+ return unless request_trace
21
+
22
+ request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, route_spec.to_s.gsub(/\(.:format\)\z/, ''))
23
+
24
+ if script_name && !script_name.empty?
25
+ request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, script_name)
26
+ end
27
+ end
28
+
29
+ def dispatcher_route?(route)
30
+ return true if route.dispatcher?
31
+
32
+ # in Rails 4 there is no #rack_app method on the app
33
+ return true if route.app.respond_to?(:rack_app) && !route.app.rack_app.nil?
34
+
35
+ false
36
+ end
37
+
38
+ # Instrumentation for ActionDispatch::Journey components
39
+ module Journey
40
+ # Instrumentation for ActionDispatch::Journey::Router for Rails versions older than 7.1
41
+ module Router
42
+ def find_routes(req)
43
+ result = super
44
+
45
+ # result is an array of [match, parameters, route] tuples
46
+ routes = result.map(&:last)
47
+
48
+ routes.each do |route|
49
+ if Instrumentation.dispatcher_route?(route)
50
+ Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
51
+ break
52
+ end
53
+ end
54
+
55
+ result
56
+ end
57
+ end
58
+
59
+ # Since Rails 7.1 `Router#find_routes` makes the route computation lazy
60
+ # https://github.com/rails/rails/commit/35b280fcc2d5d474f9f2be3aca3ae7aa6bba66eb
61
+ module LazyRouter
62
+ def find_routes(req)
63
+ super do |match, parameters, route|
64
+ if Instrumentation.dispatcher_route?(route)
65
+ Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
66
+ end
67
+
68
+ yield [match, parameters, route]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../patcher'
4
+ require_relative 'instrumentation'
5
+
6
+ module Datadog
7
+ module Tracing
8
+ module Contrib
9
+ module ActionPack
10
+ module ActionDispatch
11
+ # Patcher for ActionController components
12
+ module Patcher
13
+ include Contrib::Patcher
14
+
15
+ module_function
16
+
17
+ def target_version
18
+ Integration.version
19
+ end
20
+
21
+ def patch
22
+ if ::ActionPack.gem_version >= Gem::Version.new('7.1')
23
+ ::ActionDispatch::Journey::Router.prepend(ActionDispatch::Instrumentation::Journey::LazyRouter)
24
+ else
25
+ ::ActionDispatch::Journey::Router.prepend(ActionDispatch::Instrumentation::Journey::Router)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../patcher'
4
4
  require_relative 'action_controller/patcher'
5
+ require_relative 'action_dispatch/patcher'
5
6
 
6
7
  module Datadog
7
8
  module Tracing
@@ -19,6 +20,7 @@ module Datadog
19
20
 
20
21
  def patch
21
22
  ActionController::Patcher.patch
23
+ ActionDispatch::Patcher.patch
22
24
  end
23
25
  end
24
26
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../configuration/resolver'
4
4
  require_relative 'makara_resolver'
5
+ require_relative '../../../../core/telemetry/logger'
5
6
 
6
7
  module Datadog
7
8
  module Tracing
@@ -72,10 +73,12 @@ module Datadog
72
73
  #
73
74
  # `db_config` input may contain sensitive information such as passwords,
74
75
  # hence provide a succinct summary for the error logging.
76
+ #
75
77
  Datadog.logger.error(
76
78
  'Failed to resolve ActiveRecord database configuration. '\
77
79
  "Cause: #{e.class.name} Source: #{Array(e.backtrace).first}"
78
80
  )
81
+ Core::Telemetry::Logger.report(e, description: 'Failed to resolve ActiveRecord database configuration')
79
82
 
80
83
  nil
81
84
  end
@@ -95,6 +98,7 @@ module Datadog
95
98
  "Failed to resolve key #{matcher.inspect}. " \
96
99
  "Cause: #{e.class.name} Source: #{Array(e.backtrace).first}"
97
100
  )
101
+ Core::Telemetry::Logger.report(e, description: 'Failed to resolve key')
98
102
 
99
103
  nil
100
104
  end
@@ -4,6 +4,7 @@ require_relative '../../../metadata/ext'
4
4
  require_relative '../../analytics'
5
5
  require_relative '../ext'
6
6
  require_relative '../event'
7
+ require_relative '../../../../core/telemetry/logger'
7
8
 
8
9
  module Datadog
9
10
  module Tracing
@@ -48,7 +49,8 @@ module Datadog
48
49
  span.set_tag(Ext::TAG_INSTANTIATION_CLASS_NAME, payload.fetch(:class_name))
49
50
  span.set_tag(Ext::TAG_INSTANTIATION_RECORD_COUNT, payload.fetch(:record_count))
50
51
  rescue StandardError => e
51
- Datadog.logger.debug(e.message)
52
+ Datadog.logger.error(e.message)
53
+ Datadog::Core::Telemetry::Logger.report(e)
52
54
  end
53
55
  end
54
56
  end
@@ -6,6 +6,7 @@ require_relative '../event'
6
6
  require_relative '../ext'
7
7
  require_relative '../../analytics'
8
8
  require_relative '../../utils/database'
9
+ require_relative '../../../../core/telemetry/logger'
9
10
 
10
11
  module Datadog
11
12
  module Tracing
@@ -68,7 +69,8 @@ module Datadog
68
69
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, config[:host]) if config[:host]
69
70
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, config[:port]) if config[:port]
70
71
  rescue StandardError => e
71
- Datadog.logger.debug(e.message)
72
+ Datadog.logger.error(e.message)
73
+ Datadog::Core::Telemetry::Logger.report(e)
72
74
  end
73
75
  end
74
76
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../ext'
4
4
  require_relative '../event'
5
+ require_relative '../../../../../core/telemetry/logger'
5
6
 
6
7
  module Datadog
7
8
  module Tracing
@@ -64,7 +65,7 @@ module Datadog
64
65
  key = payload[:key]
65
66
  store = payload[:store]
66
67
 
67
- mapping = MAPPING[event]
68
+ mapping = MAPPING.fetch(event)
68
69
 
69
70
  span.service = configuration[:cache_service]
70
71
  span.resource = mapping[:resource]
@@ -81,6 +82,9 @@ module Datadog
81
82
  span.set_tag('EVENT', event)
82
83
 
83
84
  set_cache_key(span, key, mapping[:multi_key])
85
+ rescue StandardError => e
86
+ Datadog.logger.error(e.message)
87
+ Datadog::Core::Telemetry::Logger.report(e)
84
88
  end
85
89
 
86
90
  def set_cache_key(span, key, multi_key)
@@ -29,6 +29,7 @@ module Datadog
29
29
  private
30
30
 
31
31
  # rubocop:disable Metrics/AbcSize
32
+ # rubocop:disable Metrics/MethodLength
32
33
  def annotate!(span, context)
33
34
  span.service = configuration[:service_name]
34
35
  span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
@@ -76,7 +77,11 @@ module Datadog
76
77
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, context.safely(:status_code))
77
78
 
78
79
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
80
+ rescue StandardError => e
81
+ Datadog.logger.error(e.message)
82
+ Datadog::Core::Telemetry::Logger.report(e)
79
83
  end
84
+ # rubocop:enable Metrics/MethodLength
80
85
  # rubocop:enable Metrics/AbcSize
81
86
 
82
87
  def configuration
@@ -93,11 +93,16 @@ module Datadog
93
93
  span.resource = "#{method} #{quantized_url}"
94
94
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
95
95
  rescue StandardError => e
96
+ # TODO: Refactor the code to streamline the execution without ensure
96
97
  Datadog.logger.error(e.message)
98
+ Datadog::Core::Telemetry::Logger.report(e)
97
99
  ensure
98
100
  # the call is still executed
99
101
  response = super
100
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, response.status)
102
+
103
+ if response && response.respond_to?(:status)
104
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, response.status)
105
+ end
101
106
  end
102
107
  end
103
108
  response
@@ -110,6 +110,10 @@ module Datadog
110
110
 
111
111
  datadog_tag_request
112
112
 
113
+ if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(datadog_trace)
114
+ datadog_trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
115
+ end
116
+
113
117
  if datadog_configuration[:distributed_tracing]
114
118
  @datadog_original_headers ||= {}
115
119
  Contrib::HTTP.inject(datadog_trace, @datadog_original_headers)
@@ -30,6 +30,9 @@ module Datadog
30
30
  trace = Tracing.active_trace
31
31
  datum[:datadog_span] = span
32
32
  annotate!(span, datum)
33
+ if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
34
+ trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
35
+ end
33
36
  propagate!(trace, span, datum) if distributed_tracing?
34
37
 
35
38
  span
@@ -7,6 +7,7 @@ require_relative '../http'
7
7
  require_relative '../analytics'
8
8
  require_relative 'ext'
9
9
  require_relative '../http_annotation_helper'
10
+ require_relative '../../../core/telemetry/logger'
10
11
 
11
12
  module Datadog
12
13
  module Tracing
@@ -28,6 +29,9 @@ module Datadog
28
29
 
29
30
  Tracing.trace(Ext::SPAN_REQUEST, on_error: request_options[:on_error]) do |span, trace|
30
31
  annotate!(span, env, request_options)
32
+ if Datadog::AppSec::Utils::TraceOperation.appsec_standalone_reject?(trace)
33
+ trace.sampling_priority = Tracing::Sampling::Ext::Priority::AUTO_REJECT
34
+ end
31
35
  propagate!(trace, span, env) if request_options[:distributed_tracing] && Tracing.enabled?
32
36
  app.call(env).on_complete { |resp| handle_response(span, resp, request_options) }
33
37
  end
@@ -37,6 +41,7 @@ module Datadog
37
41
 
38
42
  attr_reader :app
39
43
 
44
+ # rubocop:disable Metrics/AbcSize
40
45
  def annotate!(span, env, options)
41
46
  span.resource = resource_name(env)
42
47
  span.service = service_name(env[:url].host, options)
@@ -75,7 +80,11 @@ module Datadog
75
80
  )
76
81
 
77
82
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
83
+ rescue StandardError => e
84
+ Datadog.logger.error(e.message)
85
+ Datadog::Core::Telemetry::Logger.report(e)
78
86
  end
87
+ # rubocop:enable Metrics/AbcSize
79
88
 
80
89
  def handle_response(span, env, options)
81
90
  span.set_error(["Error #{env[:status]}", env[:body]]) if options[:error_status_codes].include? env[:status]
@@ -85,6 +94,9 @@ module Datadog
85
94
  span.set_tags(
86
95
  Datadog.configuration.tracing.header_tags.response_tags(env[:response_headers])
87
96
  )
97
+ rescue StandardError => e
98
+ Datadog.logger.error(e.message)
99
+ Datadog::Core::Telemetry::Logger.report(e)
88
100
  end
89
101
 
90
102
  def propagate!(trace, span, env)