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,6 +13,7 @@ require_relative 'ext'
13
13
  require_relative 'header_collection'
14
14
  require_relative 'header_tagging'
15
15
  require_relative 'request_queue'
16
+ require_relative 'route_inference'
16
17
  require_relative 'trace_proxy_middleware'
17
18
 
18
19
  module Datadog
@@ -138,28 +139,7 @@ module Datadog
138
139
  request_span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST)
139
140
  request_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER)
140
141
 
141
- if status != 404 && (last_route = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE))
142
- last_script_name = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH) || ''
143
-
144
- # If the last_script_name is empty but the env['SCRIPT_NAME'] is NOT empty
145
- # then the current rack request was not routed and must be accounted for
146
- # which only happens in pure nested rack requests i.e /rack/rack/hello/world
147
- #
148
- # To account for the unaccounted nested rack requests of /rack/hello/world,
149
- # we use 'PATH_INFO knowing that rack cannot have named parameters
150
- if last_script_name == '' && env['SCRIPT_NAME'] && env['SCRIPT_NAME'] != ''
151
- last_script_name = last_route
152
- last_route = env['PATH_INFO']
153
- end
154
-
155
- # Clear the route and route path tags from the request trace to avoid possibility of misplacement
156
- trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE)
157
- trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
158
-
159
- # Ensure tags are placed in rack.request span as desired
160
- request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, last_script_name + last_route)
161
- request_span.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
162
- end
142
+ set_route_and_endpoint_tags(trace: trace, request_span: request_span, status: status, env: env)
163
143
 
164
144
  # Set analytics sample rate
165
145
  if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
@@ -218,16 +198,14 @@ module Datadog
218
198
  request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT, user_agent)
219
199
  end
220
200
 
221
- if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE).nil? && status != 404
222
- request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, env['PATH_INFO'])
223
- end
224
-
225
201
  HeaderTagging.tag_request_headers(request_span, request_header_collection, configuration)
226
202
  HeaderTagging.tag_response_headers(request_span, headers, configuration) if headers
227
203
 
228
204
  # detect if the status code is a 5xx and flag the request span as an error
229
205
  # unless it has been already set by the underlying framework
230
- request_span.status = 1 if status.to_s.start_with?('5') && request_span.status.zero?
206
+ if request_span.status.zero? && Datadog.configuration.tracing.http_error_statuses.server.include?(status)
207
+ request_span.status = Tracing::Metadata::Ext::Errors::STATUS
208
+ end
231
209
  end
232
210
  # rubocop:enable Metrics/AbcSize
233
211
  # rubocop:enable Metrics/CyclomaticComplexity
@@ -240,6 +218,60 @@ module Datadog
240
218
  Datadog.configuration.tracing[:rack]
241
219
  end
242
220
 
221
+ # rubocop:disable Metrics/AbcSize
222
+ # rubocop:disable Metrics/MethodLength
223
+ # rubocop:disable Metrics/CyclomaticComplexity
224
+ # rubocop:disable Metrics/PerceivedComplexity
225
+ def set_route_and_endpoint_tags(trace:, request_span:, status:, env:)
226
+ return if status == 404
227
+
228
+ if (last_route = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE))
229
+ last_script_name = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH) || ''
230
+
231
+ # This happens when processing requests to a nested rack application,
232
+ # when parent app is instrumented, and the nested app is not instrumented
233
+ #
234
+ # When resource_renaming.always_simplified_endpoint is set to true,
235
+ # we infer the route from the full request path.
236
+ if last_script_name == '' && env['SCRIPT_NAME'] != '' &&
237
+ !Datadog.configuration.tracing.resource_renaming.always_simplified_endpoint &&
238
+ (inferred_route = RouteInference.infer(env['PATH_INFO']))
239
+ set_endpoint_tag(request_span, last_route + inferred_route)
240
+ end
241
+
242
+ # Clear the route and route path tags from the request trace to avoid possibility of misplacement
243
+ trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE)
244
+ trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
245
+
246
+ # Ensure tags are placed in rack.request span as desired
247
+ request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, last_script_name + last_route)
248
+ request_span.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH)
249
+ end
250
+
251
+ if Datadog.configuration.tracing.resource_renaming.always_simplified_endpoint ||
252
+ request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE).nil?
253
+ if (inferred_route = RouteInference.read_or_infer(env))
254
+ set_endpoint_tag(request_span, inferred_route) if inferred_route
255
+ end
256
+ elsif !request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ENDPOINT)
257
+ set_endpoint_tag(request_span, request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE))
258
+ end
259
+ end
260
+ # rubocop:enable Metrics/AbcSize
261
+ # rubocop:enable Metrics/MethodLength
262
+ # rubocop:enable Metrics/CyclomaticComplexity
263
+ # rubocop:enable Metrics/PerceivedComplexity
264
+
265
+ def set_endpoint_tag(request_span, value)
266
+ # In the first iteration, http.endpoint must be reported in 2 cases:
267
+ # 1. resource renaming is enabled
268
+ # 2. AppSec is enabled and resource renaming is disabled (by default, not explicitly)
269
+ if Datadog.configuration.tracing.resource_renaming.enabled ||
270
+ Datadog.configuration.appsec.enabled && Datadog.configuration.tracing.resource_renaming.options[:enabled].default_precedence?
271
+ request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ENDPOINT, value)
272
+ end
273
+ end
274
+
243
275
  # rubocop:disable Metrics/AbcSize
244
276
  # rubocop:disable Metrics/MethodLength
245
277
  def parse_url(env, original_env)
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Rack
7
+ # This module provides logic for inferring HTTP route pattern
8
+ # from an HTTP path.
9
+ module RouteInference
10
+ MAX_NUMBER_OF_SEGMENTS = 8
11
+
12
+ INT_PARAM_REGEX = /\A[0-9]+\z/.freeze
13
+ INT_ID_PARAM_REGEX = /\A(?=.*\d)[\d._-]{3,}\z/.freeze
14
+ HEX_PARAM_REGEX = /\A(?=.*\d)[A-Fa-f0-9]{6,}\z/.freeze
15
+ HEX_ID_PARAM_REGEX = /\A(?=.*\d)[A-Fa-f0-9._-]{6,}\z/.freeze
16
+ STRING_PARAM_REGEX = /\A.{20,}|.*[%&'()*+,:=@].*\z/.freeze
17
+
18
+ DATADOG_INFERRED_ROUTE_ENV_KEY = 'datadog.inferred_route'
19
+
20
+ module_function
21
+
22
+ def read_or_infer(request_env)
23
+ request_env[DATADOG_INFERRED_ROUTE_ENV_KEY] ||=
24
+ infer(request_env['SCRIPT_NAME'].to_s + request_env['PATH_INFO'].to_s)
25
+ end
26
+
27
+ def infer(path)
28
+ segments = path.delete_prefix('/').split('/', MAX_NUMBER_OF_SEGMENTS + 1).first(MAX_NUMBER_OF_SEGMENTS)
29
+
30
+ segments.map! do |segment| #: Array[String?]
31
+ next if segment.empty?
32
+
33
+ case segment
34
+ when INT_PARAM_REGEX then '{param:int}'
35
+ when INT_ID_PARAM_REGEX then '{param:int_id}'
36
+ when HEX_PARAM_REGEX then '{param:hex}'
37
+ when HEX_ID_PARAM_REGEX then '{param:hex_id}'
38
+ when STRING_PARAM_REGEX then '{param:str}'
39
+ else segment
40
+ end
41
+ end
42
+
43
+ segments.compact! #: Array[String]
44
+
45
+ "/#{segments.join("/")}"
46
+ rescue
47
+ nil
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -26,8 +26,8 @@ module Datadog
26
26
  rescue Exception => e
27
27
  span = Tracing.active_span
28
28
  if !span.nil? && ActionPack::Utils.exception_is_error?(e)
29
- # Only set error if it's supposed to be flagged as such
30
- # e.g. we don't want to flag 404s.
29
+ # By default, only 5XX exceptions are actually errors (e.g. don't flag 404s).
30
+ # This can be changed by setting `DD_TRACE_HTTP_SERVER_ERROR_STATUSES` environment variable.
31
31
  # You can add custom errors via `config.action_dispatch.rescue_responses`
32
32
  span.set_error(e)
33
33
 
@@ -99,7 +99,10 @@ module Datadog
99
99
  end
100
100
  end
101
101
  rescue ::RestClient::ExceptionWithResponse => e
102
- span.set_error(e) if Tracing::Metadata::Ext::HTTP::ERROR_RANGE.cover?(e.http_code)
102
+ # DEV-3.0: This was previously checking against a 500..599 range.
103
+ # To not introduce breaking change, this was changed to use `http_error_statuses.server`,
104
+ # but `rest_client` is a client library, this check should use `http_error_statuses.client` instead.
105
+ span.set_error(e) if Datadog.configuration.tracing.http_error_statuses.server.include?(e.http_code)
103
106
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, e.http_code)
104
107
 
105
108
  raise e
@@ -61,7 +61,9 @@ module Datadog
61
61
  # Adds status code to the resource name once the resource comes back
62
62
  span.resource = "#{request_method} #{status_code}"
63
63
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, status_code)
64
- span.status = 1 if status_code.to_s.start_with?('5')
64
+ if Datadog.configuration.tracing.http_error_statuses.server.include?(status_code)
65
+ span.status = Tracing::Metadata::Ext::Errors::STATUS
66
+ end
65
67
  response
66
68
  end
67
69
  end
@@ -70,7 +70,9 @@ module Datadog
70
70
  sinatra_response = ::Sinatra::Response.new([], status) # Build object to use status code helpers
71
71
 
72
72
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, sinatra_response.status)
73
- span.set_error(env['sinatra.error']) if sinatra_response.server_error?
73
+ if Datadog.configuration.tracing.http_error_statuses.server.include?(sinatra_response.status)
74
+ span.set_error(env['sinatra.error'])
75
+ end
74
76
  end
75
77
 
76
78
  if (headers = response[1])
@@ -5,10 +5,17 @@ module Datadog
5
5
  module Contrib
6
6
  # Useful checking whether the defined range covers status code
7
7
  class StatusRangeMatcher
8
+ attr_reader :ranges
9
+
8
10
  def initialize(ranges)
9
11
  @ranges = Array(ranges)
10
12
  end
11
13
 
14
+ def +(other)
15
+ StatusRangeMatcher.new(@ranges + other.ranges)
16
+ end
17
+ alias_method :concat, :+
18
+
12
19
  def include?(status)
13
20
  @ranges.any? do |e|
14
21
  case e
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../configuration/settings'
4
+ require_relative '../ext'
5
+
6
+ module Datadog
7
+ module Tracing
8
+ module Contrib
9
+ module WaterDrop
10
+ module Configuration
11
+ # @public_api
12
+ class Settings < Contrib::Configuration::Settings
13
+ option :enabled do |o|
14
+ o.type :bool
15
+ o.env Ext::ENV_ENABLED
16
+ o.default true
17
+ end
18
+
19
+ option :service_name
20
+
21
+ option :distributed_tracing, default: false, type: :bool
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../distributed/fetcher'
4
+ require_relative '../../../distributed/propagation'
5
+ require_relative '../../../distributed/b3_multi'
6
+ require_relative '../../../distributed/b3_single'
7
+ require_relative '../../../distributed/datadog'
8
+ require_relative '../../../distributed/none'
9
+ require_relative '../../../distributed/trace_context'
10
+ require_relative '../../../configuration/ext'
11
+
12
+ module Datadog
13
+ module Tracing
14
+ module Contrib
15
+ module WaterDrop
16
+ module Distributed
17
+ # Extracts and injects propagation through Kafka message headers.
18
+ class Propagation < Tracing::Distributed::Propagation
19
+ def initialize(
20
+ propagation_style_inject:,
21
+ propagation_style_extract:,
22
+ propagation_extract_first:
23
+ )
24
+ super(
25
+ propagation_styles: {
26
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_MULTI_HEADER =>
27
+ Tracing::Distributed::B3Multi.new(fetcher: Tracing::Distributed::Fetcher),
28
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER =>
29
+ Tracing::Distributed::B3Single.new(fetcher: Tracing::Distributed::Fetcher),
30
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG =>
31
+ Tracing::Distributed::Datadog.new(fetcher: Tracing::Distributed::Fetcher),
32
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT =>
33
+ Tracing::Distributed::TraceContext.new(fetcher: Tracing::Distributed::Fetcher),
34
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_BAGGAGE =>
35
+ Tracing::Distributed::Baggage.new(fetcher: Tracing::Distributed::Fetcher),
36
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_NONE => Tracing::Distributed::None.new
37
+ },
38
+ propagation_style_inject: propagation_style_inject,
39
+ propagation_style_extract: propagation_style_extract,
40
+ propagation_extract_first: propagation_extract_first
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module WaterDrop
7
+ module Ext
8
+ ENV_ENABLED = 'DD_TRACE_WATERDROP_ENABLED'
9
+
10
+ SPAN_PRODUCER = 'karafka.produce'
11
+
12
+ TAG_PRODUCER = 'kafka.producer'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../integration'
4
+ require_relative 'configuration/settings'
5
+ require_relative 'patcher'
6
+
7
+ module Datadog
8
+ module Tracing
9
+ module Contrib
10
+ module WaterDrop
11
+ # Description of WaterDrop integration
12
+ class Integration
13
+ include Contrib::Integration
14
+
15
+ # WaterDrop added class-level instrumentation in version 2.8.8.rc1
16
+ MINIMUM_VERSION = Gem::Version.new('2.8.8.rc1')
17
+
18
+ register_as :waterdrop, auto_patch: false
19
+
20
+ def self.version
21
+ Gem.loaded_specs['waterdrop']&.version
22
+ end
23
+
24
+ def self.loaded?
25
+ !defined?(::WaterDrop).nil?
26
+ end
27
+
28
+ def self.compatible?
29
+ super && version >= MINIMUM_VERSION
30
+ end
31
+
32
+ def new_configuration
33
+ Configuration::Settings.new
34
+ end
35
+
36
+ def patcher
37
+ Patcher
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module WaterDrop
9
+ # Middleware to propagate tracing context in messages produced by WaterDrop
10
+ module Middleware
11
+ class << self
12
+ def call(message)
13
+ trace_op = Datadog::Tracing.active_trace
14
+
15
+ if trace_op && Datadog::Tracing::Distributed::PropagationPolicy.enabled?(
16
+ global_config: configuration,
17
+ trace: trace_op
18
+ )
19
+ WaterDrop.inject(trace_op.to_digest, message[:headers] ||= {})
20
+ end
21
+
22
+ if Datadog::DataStreams.enabled?
23
+ Datadog::DataStreams.set_produce_checkpoint(
24
+ type: 'kafka',
25
+ destination: message[:topic],
26
+ auto_instrumentation: true
27
+ ) do |key, value|
28
+ message[:headers] ||= {}
29
+ message[:headers][key] = value
30
+ end
31
+ end
32
+
33
+ message
34
+ end
35
+
36
+ private
37
+
38
+ def configuration
39
+ Datadog.configuration.tracing[:waterdrop]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../patcher'
4
+ require_relative 'ext'
5
+ require_relative 'distributed/propagation'
6
+
7
+ module Datadog
8
+ module Tracing
9
+ module Contrib
10
+ module WaterDrop
11
+ # Patcher enables patching of 'waterdrop' module.
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
+ require_relative 'producer'
23
+ require_relative 'middleware'
24
+
25
+ ::WaterDrop::Producer.prepend(Producer)
26
+ ::WaterDrop.instrumentation.subscribe('producer.configured') do |event|
27
+ producer = event[:producer]
28
+
29
+ included_middlewares = producer.middleware.instance_variable_get(:@steps)
30
+ producer.middleware.append(Middleware) unless included_middlewares.include?(Middleware)
31
+
32
+ if Datadog.configuration.data_streams.enabled
33
+ producer.monitor.subscribe('message.acknowledged') do |ack_event|
34
+ if Datadog::DataStreams.enabled?
35
+ payload = ack_event.payload
36
+ Datadog::DataStreams.track_kafka_produce(payload[:topic], payload[:partition], payload[:offset])
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module WaterDrop
9
+ # Producer integration for WaterDrop
10
+ module Producer
11
+ %i[
12
+ produce_many_sync
13
+ produce_many_async
14
+ produce_sync
15
+ produce_async
16
+ ].each do |method|
17
+ define_method(method) do |messages|
18
+ Datadog::Tracing.trace(Ext::SPAN_PRODUCER, resource: "waterdrop.#{__method__}") do
19
+ extract_span_tags(messages)
20
+ super(messages)
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def extract_span_tags(messages)
28
+ messages = [messages] if messages.is_a?(Hash)
29
+ span = Datadog::Tracing.active_span
30
+ return unless span
31
+
32
+ topics = []
33
+ partitions = []
34
+ messages.each do |message|
35
+ topics << message[:topic]
36
+ partitions << message[:partition] if message.key?(:partition)
37
+ end
38
+
39
+ span.set_tag(Ext::TAG_PRODUCER, id)
40
+ span.set_tag(Contrib::Karafka::Ext::TAG_MESSAGE_COUNT, messages.size)
41
+ span.set_tag(Contrib::Ext::Messaging::TAG_SYSTEM, Contrib::Karafka::Ext::TAG_SYSTEM)
42
+
43
+ span.set_tag(Contrib::Ext::Messaging::TAG_DESTINATION, topics.uniq.sort.join(','))
44
+ span.set_tag(Contrib::Karafka::Ext::TAG_PARTITION, partitions.uniq.sort.join(',')) if partitions.any?
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'component'
4
+ require_relative 'waterdrop/integration'
5
+ require_relative 'waterdrop/distributed/propagation'
6
+
7
+ module Datadog
8
+ module Tracing
9
+ module Contrib
10
+ # `WaterDrop` integration public API
11
+ module WaterDrop
12
+ def self.inject(digest, data)
13
+ raise 'Please invoke Datadog.configure at least once before calling this method' unless @propagation
14
+
15
+ @propagation.inject!(digest, data)
16
+ end
17
+
18
+ def self.extract(data)
19
+ raise 'Please invoke Datadog.configure at least once before calling this method' unless @propagation
20
+
21
+ @propagation.extract(data)
22
+ end
23
+
24
+ Contrib::Component.register('waterdrop') do |config|
25
+ tracing = config.tracing
26
+ tracing.propagation_style
27
+
28
+ @propagation = WaterDrop::Distributed::Propagation.new(
29
+ propagation_style_inject: tracing.propagation_style_inject,
30
+ propagation_style_extract: tracing.propagation_style_extract,
31
+ propagation_extract_first: tracing.propagation_extract_first
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -55,6 +55,7 @@ require_relative 'contrib/httpclient/integration'
55
55
  require_relative 'contrib/httprb/integration'
56
56
  require_relative 'contrib/integration'
57
57
  require_relative 'contrib/kafka/integration'
58
+ require_relative 'contrib/waterdrop'
58
59
  require_relative 'contrib/karafka'
59
60
  require_relative 'contrib/lograge/integration'
60
61
  require_relative 'contrib/mongodb/integration'
@@ -94,8 +94,8 @@ module Datadog
94
94
 
95
95
  # @public_api
96
96
  module HTTP
97
- ERROR_RANGE = (500...600).freeze
98
97
  TAG_BASE_URL = 'http.base_url'
98
+ TAG_ENDPOINT = 'http.endpoint'
99
99
  TAG_METHOD = 'http.method'
100
100
  TAG_STATUS_CODE = 'http.status_code'
101
101
  TAG_USER_AGENT = 'http.useragent'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'statistics'
4
+ require_relative '../../../core/transport/http/client'
4
5
  require_relative '../../../core/transport/http/env'
5
6
  require_relative '../../../core/transport/http/response'
6
7
 
@@ -9,33 +10,24 @@ module Datadog
9
10
  module Transport
10
11
  module HTTP
11
12
  # Routes, encodes, and sends tracer data to the trace agent via HTTP.
12
- class Client
13
+ class Client < Core::Transport::HTTP::Client
13
14
  include Datadog::Tracing::Transport::HTTP::Statistics
14
15
 
15
- attr_reader :api, :logger
16
+ private
16
17
 
17
- def initialize(api, logger: Datadog.logger)
18
- @api = api
19
- @logger = logger
20
- end
21
-
22
- def send_request(request, &block)
23
- # Build request into env
24
- env = build_env(request)
25
-
26
- # Get responses from API
27
- response = yield(api, env)
18
+ def on_response(response)
19
+ super
28
20
 
29
21
  # Update statistics
30
22
  update_stats_from_response!(response)
23
+ end
31
24
 
32
- response
33
- rescue => e
34
- message =
35
- "Internal error during #{self.class.name} request. Cause: #{e.class.name} #{e.message} " \
36
- "Location: #{Array(e.backtrace).first}"
25
+ def on_exception(exception)
26
+ # Note: this method does NOT call super - it has replacement
27
+ # logic for how to log the exception.
28
+
29
+ message = build_exception_message(exception)
37
30
 
38
- # Log error
39
31
  if stats.consecutive_errors > 0
40
32
  logger.debug(message)
41
33
  else
@@ -44,13 +36,7 @@ module Datadog
44
36
  end
45
37
 
46
38
  # Update statistics
47
- update_stats_from_exception!(e)
48
-
49
- Datadog::Core::Transport::InternalErrorResponse.new(e)
50
- end
51
-
52
- def build_env(request)
53
- Datadog::Core::Transport::HTTP::Env.new(request)
39
+ update_stats_from_exception!(exception)
54
40
  end
55
41
  end
56
42
  end