datadog 2.22.0 → 2.23.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -2
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
  5. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  6. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  7. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  8. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  9. data/ext/libdatadog_api/feature_flags.c +554 -0
  10. data/ext/libdatadog_api/feature_flags.h +5 -0
  11. data/ext/libdatadog_api/init.c +2 -0
  12. data/ext/libdatadog_api/library_config.c +12 -11
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  15. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  16. data/lib/datadog/appsec/assets/blocked.html +8 -0
  17. data/lib/datadog/appsec/assets/blocked.json +1 -1
  18. data/lib/datadog/appsec/assets/blocked.text +3 -1
  19. data/lib/datadog/appsec/assets.rb +1 -1
  20. data/lib/datadog/appsec/remote.rb +4 -0
  21. data/lib/datadog/appsec/response.rb +18 -4
  22. data/lib/datadog/core/configuration/components.rb +30 -3
  23. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  24. data/lib/datadog/core/configuration/settings.rb +14 -0
  25. data/lib/datadog/core/configuration/supported_configurations.rb +330 -301
  26. data/lib/datadog/core/ddsketch.rb +0 -2
  27. data/lib/datadog/core/environment/ext.rb +6 -0
  28. data/lib/datadog/core/environment/process.rb +79 -0
  29. data/lib/datadog/core/feature_flags.rb +61 -0
  30. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  31. data/lib/datadog/core/remote/transport/config.rb +2 -10
  32. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  33. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  34. data/lib/datadog/core/remote/transport/http.rb +2 -0
  35. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  36. data/lib/datadog/core/remote/worker.rb +25 -37
  37. data/lib/datadog/core/tag_builder.rb +0 -4
  38. data/lib/datadog/core/tag_normalizer.rb +84 -0
  39. data/lib/datadog/core/telemetry/component.rb +7 -3
  40. data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
  41. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  42. data/lib/datadog/core/telemetry/logger.rb +2 -2
  43. data/lib/datadog/core/telemetry/logging.rb +2 -8
  44. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  45. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  46. data/lib/datadog/core/transport/http/client.rb +69 -0
  47. data/lib/datadog/core/utils/array.rb +29 -0
  48. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  49. data/lib/datadog/core/utils/network.rb +3 -1
  50. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  51. data/lib/datadog/core/utils.rb +2 -0
  52. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  53. data/lib/datadog/data_streams/configuration.rb +11 -0
  54. data/lib/datadog/data_streams/ext.rb +11 -0
  55. data/lib/datadog/data_streams/extensions.rb +16 -0
  56. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  57. data/lib/datadog/data_streams/processor.rb +509 -0
  58. data/lib/datadog/data_streams/transport/http/api.rb +33 -0
  59. data/lib/datadog/data_streams/transport/http/client.rb +21 -0
  60. data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
  61. data/lib/datadog/data_streams/transport/http.rb +41 -0
  62. data/lib/datadog/data_streams/transport/stats.rb +60 -0
  63. data/lib/datadog/data_streams.rb +100 -0
  64. data/lib/datadog/di/component.rb +0 -16
  65. data/lib/datadog/di/el/evaluator.rb +1 -1
  66. data/lib/datadog/di/error.rb +4 -0
  67. data/lib/datadog/di/instrumenter.rb +76 -30
  68. data/lib/datadog/di/probe.rb +20 -0
  69. data/lib/datadog/di/probe_manager.rb +10 -2
  70. data/lib/datadog/di/probe_notification_builder.rb +62 -23
  71. data/lib/datadog/di/proc_responder.rb +32 -0
  72. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  73. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  74. data/lib/datadog/di/transport/http/input.rb +2 -4
  75. data/lib/datadog/di/transport/http.rb +6 -2
  76. data/lib/datadog/di/transport/input.rb +64 -4
  77. data/lib/datadog/open_feature/component.rb +60 -0
  78. data/lib/datadog/open_feature/configuration.rb +27 -0
  79. data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
  80. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  81. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  82. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  83. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  84. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  85. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  86. data/lib/datadog/open_feature/ext.rb +14 -0
  87. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  88. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  89. data/lib/datadog/open_feature/provider.rb +141 -0
  90. data/lib/datadog/open_feature/remote.rb +74 -0
  91. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  92. data/lib/datadog/open_feature/transport.rb +72 -0
  93. data/lib/datadog/open_feature.rb +19 -0
  94. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  95. data/lib/datadog/opentelemetry/metrics.rb +110 -0
  96. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  97. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
  98. data/lib/datadog/opentelemetry.rb +3 -0
  99. data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
  100. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  101. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  102. data/lib/datadog/profiling/profiler.rb +4 -0
  103. data/lib/datadog/profiling/tag_builder.rb +36 -3
  104. data/lib/datadog/profiling.rb +1 -2
  105. data/lib/datadog/single_step_instrument.rb +1 -1
  106. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  107. data/lib/datadog/tracing/configuration/settings.rb +74 -0
  108. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  109. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  110. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  111. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  112. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  113. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  114. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  115. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  116. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  117. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  118. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  119. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  120. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  121. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  122. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  123. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  124. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  125. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  126. data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
  127. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  128. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  129. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  130. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  131. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  132. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  133. data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
  134. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  135. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  136. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  137. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  138. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  139. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  140. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  141. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  142. data/lib/datadog/tracing/contrib.rb +1 -0
  143. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  144. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  145. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  146. data/lib/datadog/tracing/transport/traces.rb +3 -5
  147. data/lib/datadog/version.rb +2 -2
  148. data/lib/datadog.rb +2 -0
  149. metadata +78 -15
  150. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  151. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  152. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -13,8 +13,7 @@ module Datadog
13
13
  # Gets the equivalent status code for the exception (not all are 5XX)
14
14
  # You can add custom errors via `config.action_dispatch.rescue_responses`
15
15
  status = ::ActionDispatch::ExceptionWrapper.status_code_for_exception(exception.class.name)
16
- # Only 5XX exceptions are actually errors (e.g. don't flag 404s)
17
- status.to_s.start_with?('5')
16
+ Datadog.configuration.tracing.http_error_statuses.server.include?(status)
18
17
  else
19
18
  true
20
19
  end
@@ -6,17 +6,31 @@ module Datadog
6
6
  module ActiveJob
7
7
  # Active Job log injection wrapped around job execution
8
8
  module LogInjection
9
- def self.included(base)
10
- base.class_eval do
11
- around_perform do |_, block|
12
- if Datadog.configuration.tracing.log_injection && logger.respond_to?(:tagged)
13
- logger.tagged(Tracing.log_correlation, &block)
14
- else
15
- block.call
9
+ # Active Job 4 / 5 don't execute `perform_now` at the right point, so we do best effort log correlation tagging
10
+ module AroundPerformPatch
11
+ def self.included(base)
12
+ base.class_eval do
13
+ around_perform do |_, block|
14
+ if Datadog.configuration.tracing.log_injection && logger.respond_to?(:tagged)
15
+ logger.tagged(Tracing.log_correlation, &block)
16
+ else
17
+ block.call
18
+ end
16
19
  end
17
20
  end
18
21
  end
19
22
  end
23
+
24
+ # Active Job 6+ executes `perform_now` at the right point, so we can provide better log correlation tagging
25
+ module PerformNowPatch
26
+ def perform_now
27
+ if Datadog.configuration.tracing.log_injection && logger.respond_to?(:tagged)
28
+ logger.tagged(Tracing.log_correlation) { super }
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
20
34
  end
21
35
  end
22
36
  end
@@ -26,7 +26,11 @@ module Datadog
26
26
 
27
27
  def inject_log_correlation
28
28
  ::ActiveSupport.on_load(:active_job) do
29
- include LogInjection
29
+ if ::ActiveJob.gem_version < Gem::Version.new('6.0.0')
30
+ include LogInjection::AroundPerformPatch
31
+ else
32
+ include LogInjection::PerformNowPatch
33
+ end
30
34
  end
31
35
  end
32
36
  end
@@ -36,8 +36,10 @@ module Datadog
36
36
  span.name = Ext::SPAN_COMMAND
37
37
  span.resource = context.safely(:resource)
38
38
 
39
- # Set error on the span if the Response Status Code is in error range
40
- if Tracing::Metadata::Ext::HTTP::ERROR_RANGE.cover?(context.safely(:status_code))
39
+ # DEV-3.0: This was previously checking against a 500..599 range.
40
+ # To not introduce breaking change, this was changed to use `http_error_statuses.server`,
41
+ # but `aws` is a client library, this check should use `http_error_statuses.client` instead.
42
+ if Datadog.configuration.tracing.http_error_statuses.server.include?(context.safely(:status_code))
41
43
  # At this point we do not have any additional diagnostics
42
44
  # besides the HTTP status code which is recorded in the span tags
43
45
  # later in this method.
@@ -57,7 +57,10 @@ module Datadog
57
57
  set_span_error_message("Request has failed: #{message}")
58
58
  else
59
59
  @datadog_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, response_code)
60
- if Tracing::Metadata::Ext::HTTP::ERROR_RANGE.cover?(response_code)
60
+ # DEV-3.0: This was previously checking against a 500..599 range.
61
+ # To not introduce breaking change, this was changed to use `http_error_statuses.server`,
62
+ # but `ethon` is a client library, this check should use `http_error_statuses.client` instead.
63
+ if Datadog.configuration.tracing.http_error_statuses.server.include?(response_code)
61
64
  set_span_error_message("Request has failed with HTTP error: #{response_code}")
62
65
  end
63
66
  end
@@ -41,9 +41,17 @@ module Datadog
41
41
 
42
42
  option :error_status_codes do |o|
43
43
  o.env Ext::ENV_ERROR_STATUS_CODES
44
- o.default 400...600
45
- o.setter do |v|
46
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
44
+ o.setter do |value|
45
+ if value.nil?
46
+ # Fallback to global config, which is defaulted to client (400..499) + server (500..599)
47
+ # DEV-3.0: `excon` is a client library, this should fall back to `http_error_statuses.client` only.
48
+ # We cannot change it without causing a breaking change.
49
+ client_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.client
50
+ server_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.server
51
+ client_global_error_statuses + server_global_error_statuses
52
+ else
53
+ Tracing::Contrib::StatusRangeMatcher.new(value)
54
+ end
47
55
  end
48
56
  o.env_parser do |v|
49
57
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -13,10 +13,6 @@ module Datadog
13
13
  # Custom settings for the Faraday integration
14
14
  # @public_api
15
15
  class Settings < Contrib::Configuration::Settings
16
- DEFAULT_ERROR_HANDLER = lambda do |env|
17
- Tracing::Metadata::Ext::HTTP::ERROR_RANGE.cover?(env[:status])
18
- end
19
-
20
16
  option :enabled do |o|
21
17
  o.type :bool
22
18
  o.env Ext::ENV_ENABLED
@@ -44,9 +40,17 @@ module Datadog
44
40
 
45
41
  option :error_status_codes do |o|
46
42
  o.env Ext::ENV_ERROR_STATUS_CODES
47
- o.default 400...600
48
- o.setter do |v|
49
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
43
+ o.setter do |value|
44
+ if value.nil?
45
+ # Fallback to global config, which is defaulted to client (400..499) + server (500..599)
46
+ # DEV-3.0: `faraday` is a client library, this should fall back to `http_error_statuses.client` only.
47
+ # We cannot change it without causing a breaking change.
48
+ client_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.client
49
+ server_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.server
50
+ client_global_error_statuses + server_global_error_statuses
51
+ else
52
+ Tracing::Contrib::StatusRangeMatcher.new(value)
53
+ end
50
54
  end
51
55
  o.env_parser do |v|
52
56
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -39,9 +39,13 @@ module Datadog
39
39
 
40
40
  option :error_status_codes do |o|
41
41
  o.env Ext::ENV_ERROR_STATUS_CODES
42
- o.default 500...600
43
- o.setter do |v|
44
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
42
+ o.setter do |value|
43
+ if value.nil?
44
+ # Fallback to global config, which is defaulted to server (500..599)
45
+ Datadog.configuration.tracing.http_error_statuses.server
46
+ else
47
+ Tracing::Contrib::StatusRangeMatcher.new(value)
48
+ end
45
49
  end
46
50
  o.env_parser do |v|
47
51
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -176,6 +176,26 @@ module Datadog
176
176
  "#{type.graphql_name}.resolve_type"
177
177
  end
178
178
 
179
+ # Serialize error's `locations` array as an array of Strings, given
180
+ # Span Events do not support hashes nested inside arrays.
181
+ #
182
+ # Here's an example in which `locations`:
183
+ # [
184
+ # {"line" => 3, "column" => 10},
185
+ # {"line" => 7, "column" => 8},
186
+ # ]
187
+ # is serialized as:
188
+ # ["3:10", "7:8"]
189
+ def self.serialize_error_locations(locations)
190
+ # locations are only provided by the `graphql` library when the error can
191
+ # be associated to a particular point in the query.
192
+ return [] if locations.nil?
193
+
194
+ locations.map do |location|
195
+ "#{location["line"]}:#{location["column"]}"
196
+ end
197
+ end
198
+
179
199
  private
180
200
 
181
201
  # Traces the given callable with the given trace key, resource, and kwargs.
@@ -268,28 +288,13 @@ module Datadog
268
288
  @type_key => parsed_error.type,
269
289
  @stacktrace_key => parsed_error.backtrace,
270
290
  @message_key => graphql_error['message'],
271
- @locations_key => serialize_error_locations(graphql_error['locations']),
291
+ @locations_key =>
292
+ Datadog::Tracing::Contrib::GraphQL::UnifiedTrace.serialize_error_locations(graphql_error['locations']),
272
293
  @path_key => graphql_error['path'],
273
294
  )
274
295
  )
275
296
  end
276
297
  end
277
-
278
- # Serialize error's `locations` array as an array of Strings, given
279
- # Span Events do not support hashes nested inside arrays.
280
- #
281
- # Here's an example in which `locations`:
282
- # [
283
- # {"line" => 3, "column" => 10},
284
- # {"line" => 7, "column" => 8},
285
- # ]
286
- # is serialized as:
287
- # ["3:10", "7:8"]
288
- def serialize_error_locations(locations)
289
- locations.map do |location|
290
- "#{location["line"]}:#{location["column"]}"
291
- end
292
- end
293
298
  end
294
299
  end
295
300
  end
@@ -46,9 +46,17 @@ module Datadog
46
46
 
47
47
  option :error_status_codes do |o|
48
48
  o.env Ext::ENV_ERROR_STATUS_CODES
49
- o.default 400...600
50
- o.setter do |v|
51
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
49
+ o.setter do |value|
50
+ if value.nil?
51
+ # Fallback to global config, which is defaulted to client (400..499) + server (500..599)
52
+ # DEV-3.0: `http` is a client library, this should fall back to `http_error_statuses.client` only.
53
+ # We cannot change it without causing a breaking change.
54
+ client_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.client
55
+ server_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.server
56
+ client_global_error_statuses + server_global_error_statuses
57
+ else
58
+ Tracing::Contrib::StatusRangeMatcher.new(value)
59
+ end
52
60
  end
53
61
  o.env_parser do |v|
54
62
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -45,9 +45,17 @@ module Datadog
45
45
 
46
46
  option :error_status_codes do |o|
47
47
  o.env Ext::ENV_ERROR_STATUS_CODES
48
- o.default 400...600
49
- o.setter do |v|
50
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
48
+ o.setter do |value|
49
+ if value.nil?
50
+ # Fallback to global config, which is defaulted to client (400..499) + server (500..599)
51
+ # DEV-3.0: `httpclient` is a client library, this should fall back to `http_error_statuses.client` only.
52
+ # We cannot change it without causing a breaking change.
53
+ client_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.client
54
+ server_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.server
55
+ client_global_error_statuses + server_global_error_statuses
56
+ else
57
+ Tracing::Contrib::StatusRangeMatcher.new(value)
58
+ end
51
59
  end
52
60
  o.env_parser do |v|
53
61
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -45,9 +45,17 @@ module Datadog
45
45
 
46
46
  option :error_status_codes do |o|
47
47
  o.env Ext::ENV_ERROR_STATUS_CODES
48
- o.default 400...600
49
- o.setter do |v|
50
- Tracing::Contrib::StatusRangeMatcher.new(v) if v
48
+ o.setter do |value|
49
+ if value.nil?
50
+ # Fallback to global config, which is defaulted to client (400..499) + server (500..599)
51
+ # DEV-3.0: `httprb` is a client library, this should fall back to `http_error_statuses.client` only.
52
+ # We cannot change it without causing a breaking change.
53
+ client_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.client
54
+ server_global_error_statuses = Datadog.configuration.tracing.http_error_statuses.server
55
+ client_global_error_statuses + server_global_error_statuses
56
+ else
57
+ Tracing::Contrib::StatusRangeMatcher.new(value)
58
+ end
51
59
  end
52
60
  o.env_parser do |v|
53
61
  Tracing::Contrib::StatusRangeEnvParser.call(v) if v
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Kafka
7
+ module Instrumentation
8
+ # Instrumentation for Kafka::Consumer
9
+ module Consumer
10
+ def self.prepended(base)
11
+ base.prepend(InstanceMethods)
12
+ end
13
+
14
+ # Instance methods for consumer instrumentation
15
+ module InstanceMethods
16
+ def each_message(**kwargs, &block)
17
+ return super unless Datadog::DataStreams.enabled?
18
+
19
+ wrapped_block = proc do |message|
20
+ Datadog.logger.debug { "Kafka each_message: DSM enabled for topic #{message.topic}" }
21
+
22
+ begin
23
+ headers = message.headers || {}
24
+ Datadog::DataStreams.set_consume_checkpoint(
25
+ type: 'kafka',
26
+ source: message.topic,
27
+ auto_instrumentation: true
28
+ ) { |key| headers[key] }
29
+ rescue => e
30
+ Datadog.logger.debug("Error setting DSM checkpoint: #{e.class}: #{e}")
31
+ end
32
+
33
+ yield(message) if block
34
+ end
35
+
36
+ super(**kwargs, &wrapped_block)
37
+ end
38
+
39
+ def each_batch(**kwargs, &block)
40
+ return super unless Datadog::DataStreams.enabled?
41
+
42
+ wrapped_block = proc do |batch|
43
+ Datadog.logger.debug { "Kafka each_batch: DSM enabled for topic #{batch.topic}" }
44
+
45
+ begin
46
+ Datadog::DataStreams.set_consume_checkpoint(
47
+ type: 'kafka',
48
+ source: batch.topic,
49
+ auto_instrumentation: true
50
+ )
51
+ rescue => e
52
+ Datadog.logger.debug("Error setting DSM checkpoint: #{e.class}: #{e}")
53
+ end
54
+
55
+ yield(batch) if block
56
+ end
57
+
58
+ super(**kwargs, &wrapped_block)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Kafka
7
+ module Instrumentation
8
+ # Instrumentation for Kafka::Producer
9
+ module Producer
10
+ def self.prepended(base)
11
+ base.prepend(InstanceMethods)
12
+ end
13
+
14
+ module InstanceMethods
15
+ def deliver_messages(**kwargs)
16
+ if Datadog::DataStreams.enabled?
17
+ begin
18
+ pending_messages = instance_variable_get(:@pending_message_queue)
19
+
20
+ if pending_messages && !pending_messages.empty?
21
+ pending_messages.each do |message|
22
+ message.headers ||= {}
23
+ Datadog::DataStreams.set_produce_checkpoint(
24
+ type: 'kafka',
25
+ destination: message.topic,
26
+ auto_instrumentation: true
27
+ ) do |key, value|
28
+ message.headers[key] = value
29
+ end
30
+ end
31
+ end
32
+ rescue => e
33
+ Datadog.logger.debug("Error setting DSM checkpoint: #{e.class}: #{e}")
34
+ end
35
+ end
36
+
37
+ super
38
+ end
39
+
40
+ def send_messages(messages, **kwargs)
41
+ if Datadog::DataStreams.enabled?
42
+ begin
43
+ messages.each do |message|
44
+ message[:headers] ||= {}
45
+ Datadog::DataStreams.set_produce_checkpoint(
46
+ type: 'kafka',
47
+ destination: message[:topic],
48
+ auto_instrumentation: true
49
+ ) do |key, value|
50
+ message[:headers][key] = value
51
+ end
52
+ end
53
+ rescue => e
54
+ Datadog.logger.debug("Error setting DSM checkpoint: #{e.class}: #{e}")
55
+ end
56
+ end
57
+
58
+ super
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -21,6 +21,20 @@ module Datadog
21
21
  def patch
22
22
  # Subscribe to Kafka events
23
23
  Events.subscribe!
24
+
25
+ # Apply monkey patches for additional instrumentation (e.g., DSM)
26
+ patch_producer if defined?(::Kafka::Producer)
27
+ patch_consumer if defined?(::Kafka::Consumer)
28
+ end
29
+
30
+ def patch_producer
31
+ require_relative 'instrumentation/producer'
32
+ ::Kafka::Producer.prepend(Instrumentation::Producer)
33
+ end
34
+
35
+ def patch_consumer
36
+ require_relative 'instrumentation/consumer'
37
+ ::Kafka::Consumer.prepend(Instrumentation::Consumer)
24
38
  end
25
39
  end
26
40
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Karafka
7
+ # Karafka framework code, used to essentially:
8
+ # - handle configuration entries which are specific to Datadog tracing
9
+ # - instrument parts of the framework when needed
10
+ module Framework
11
+ def self.setup
12
+ Datadog.configure do |datadog_config|
13
+ karafka_config = datadog_config.tracing[:karafka]
14
+ activate_waterdrop!(datadog_config, karafka_config)
15
+ end
16
+ end
17
+
18
+ # Apply relevant configuration from Karafka to WaterDrop
19
+ def self.activate_waterdrop!(datadog_config, karafka_config)
20
+ datadog_config.tracing.instrument(
21
+ :waterdrop,
22
+ service_name: karafka_config[:service_name],
23
+ distributed_tracing: karafka_config[:distributed_tracing],
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -47,6 +47,17 @@ module Datadog
47
47
  span.set_tag(Ext::TAG_CONSUMER, consumer)
48
48
  span.set_tag(Contrib::Ext::Messaging::TAG_DESTINATION, job.executor.topic.name)
49
49
  span.set_tag(Contrib::Ext::Messaging::TAG_SYSTEM, Ext::TAG_SYSTEM)
50
+
51
+ # DSM: Track consumer offset stats for batch processing
52
+ if Datadog.configuration.data_streams.enabled
53
+ job.messages.each do |message|
54
+ Datadog::DataStreams.track_kafka_consume(
55
+ job.executor.topic.name,
56
+ job.executor.partition,
57
+ message.metadata.offset
58
+ )
59
+ end
60
+ end
50
61
  end
51
62
 
52
63
  super
@@ -35,6 +35,24 @@ module Datadog
35
35
  Datadog::Tracing.continue_trace!(trace_digest) if trace_digest
36
36
  end
37
37
 
38
+ if Datadog::DataStreams.enabled?
39
+ begin
40
+ headers = if message.metadata.respond_to?(:raw_headers)
41
+ message.metadata.raw_headers
42
+ else
43
+ message.metadata.headers
44
+ end
45
+
46
+ Datadog::DataStreams.set_consume_checkpoint(
47
+ type: 'kafka',
48
+ source: message.topic,
49
+ auto_instrumentation: true
50
+ ) { |key| headers[key] }
51
+ rescue => e
52
+ Datadog.logger.debug("Error setting DSM checkpoint: #{e.class}: #{e}")
53
+ end
54
+ end
55
+
38
56
  Tracing.trace(Ext::SPAN_MESSAGE_CONSUME) do |span|
39
57
  span.set_tag(Ext::TAG_OFFSET, message.metadata.offset)
40
58
  span.set_tag(Contrib::Ext::Messaging::TAG_DESTINATION, message.topic)
@@ -48,6 +66,18 @@ module Datadog
48
66
  end
49
67
  end
50
68
 
69
+ module AppPatch
70
+ ONLY_ONCE_PER_APP = Hash.new { |h, key| h[key] = Core::Utils::OnlyOnce.new }
71
+
72
+ def initialized!
73
+ ONLY_ONCE_PER_APP[self].run do
74
+ # Activate tracing on components related to Karafka (e.g. WaterDrop)
75
+ Contrib::Karafka::Framework.setup
76
+ end
77
+ super
78
+ end
79
+ end
80
+
51
81
  # Patcher enables patching of 'karafka' module.
52
82
  module Patcher
53
83
  include Contrib::Patcher
@@ -60,9 +90,11 @@ module Datadog
60
90
 
61
91
  def patch
62
92
  require_relative 'monitor'
93
+ require_relative 'framework'
63
94
 
64
95
  ::Karafka::Instrumentation::Monitor.prepend(Monitor)
65
96
  ::Karafka::Messages::Messages.prepend(MessagesPatch)
97
+ ::Karafka::App.singleton_class.prepend(AppPatch)
66
98
  end
67
99
  end
68
100
  end