datadog 2.3.0 → 2.5.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 (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)