datadog 2.15.0 → 2.17.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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -2
  3. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
  5. data/ext/datadog_profiling_native_extension/encoded_profile.c +22 -12
  6. data/ext/datadog_profiling_native_extension/encoded_profile.h +1 -0
  7. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
  9. data/ext/datadog_profiling_native_extension/http_transport.c +45 -72
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +4 -5
  11. data/ext/libdatadog_api/crashtracker.c +11 -12
  12. data/ext/libdatadog_api/crashtracker.h +5 -0
  13. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  14. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  15. data/ext/libdatadog_api/init.c +15 -0
  16. data/ext/libdatadog_api/library_config.c +122 -0
  17. data/ext/libdatadog_api/library_config.h +19 -0
  18. data/ext/libdatadog_api/macos_development.md +3 -3
  19. data/ext/libdatadog_api/process_discovery.c +117 -0
  20. data/ext/libdatadog_api/process_discovery.h +5 -0
  21. data/ext/libdatadog_extconf_helpers.rb +1 -1
  22. data/lib/datadog/appsec/actions_handler.rb +3 -2
  23. data/lib/datadog/appsec/api_security/lru_cache.rb +49 -0
  24. data/lib/datadog/appsec/api_security.rb +9 -0
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1344 -0
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +1344 -0
  27. data/lib/datadog/appsec/autoload.rb +1 -1
  28. data/lib/datadog/appsec/component.rb +11 -4
  29. data/lib/datadog/appsec/configuration/settings.rb +31 -18
  30. data/lib/datadog/appsec/context.rb +1 -1
  31. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  32. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  33. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  34. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +2 -3
  35. data/lib/datadog/appsec/contrib/devise/ext.rb +1 -0
  36. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/devise/patcher.rb +3 -5
  38. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +17 -4
  39. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  40. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  41. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  42. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  43. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  44. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +22 -32
  46. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +16 -16
  48. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  49. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  50. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  51. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  52. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  53. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  54. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  55. data/lib/datadog/appsec/event.rb +85 -95
  56. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +5 -2
  57. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  58. data/lib/datadog/appsec/monitor/gateway/watcher.rb +42 -12
  59. data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
  60. data/lib/datadog/appsec/processor/rule_merger.rb +5 -5
  61. data/lib/datadog/appsec/processor.rb +1 -1
  62. data/lib/datadog/appsec/remote.rb +14 -13
  63. data/lib/datadog/appsec/response.rb +6 -6
  64. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  65. data/lib/datadog/appsec/security_event.rb +39 -0
  66. data/lib/datadog/appsec.rb +1 -1
  67. data/lib/datadog/core/buffer/random.rb +18 -2
  68. data/lib/datadog/core/configuration/agent_settings_resolver.rb +5 -5
  69. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  70. data/lib/datadog/core/configuration/components.rb +48 -30
  71. data/lib/datadog/core/configuration/components_state.rb +23 -0
  72. data/lib/datadog/core/configuration/option.rb +79 -43
  73. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  74. data/lib/datadog/core/configuration/options.rb +1 -1
  75. data/lib/datadog/core/configuration/settings.rb +20 -10
  76. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  77. data/lib/datadog/core/configuration.rb +40 -16
  78. data/lib/datadog/core/crashtracking/component.rb +3 -10
  79. data/lib/datadog/core/encoding.rb +1 -1
  80. data/lib/datadog/core/environment/cgroup.rb +10 -12
  81. data/lib/datadog/core/environment/container.rb +38 -40
  82. data/lib/datadog/core/environment/ext.rb +6 -6
  83. data/lib/datadog/core/environment/git.rb +1 -0
  84. data/lib/datadog/core/environment/identity.rb +3 -3
  85. data/lib/datadog/core/environment/platform.rb +3 -3
  86. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  87. data/lib/datadog/core/error.rb +11 -9
  88. data/lib/datadog/core/logger.rb +2 -2
  89. data/lib/datadog/core/metrics/client.rb +20 -21
  90. data/lib/datadog/core/metrics/logging.rb +5 -5
  91. data/lib/datadog/core/process_discovery.rb +32 -0
  92. data/lib/datadog/core/rate_limiter.rb +4 -2
  93. data/lib/datadog/core/remote/client.rb +39 -31
  94. data/lib/datadog/core/remote/component.rb +3 -3
  95. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  96. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  97. data/lib/datadog/core/remote/transport/http/client.rb +1 -1
  98. data/lib/datadog/core/remote/transport/http/config.rb +21 -5
  99. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -1
  100. data/lib/datadog/core/runtime/metrics.rb +4 -4
  101. data/lib/datadog/core/telemetry/component.rb +78 -53
  102. data/lib/datadog/core/telemetry/emitter.rb +23 -11
  103. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +65 -0
  104. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  105. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  106. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  107. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  108. data/lib/datadog/core/telemetry/event/app_started.rb +179 -0
  109. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  110. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  111. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  112. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  113. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  114. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  115. data/lib/datadog/core/telemetry/event.rb +17 -472
  116. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  117. data/lib/datadog/core/telemetry/logger.rb +1 -1
  118. data/lib/datadog/core/telemetry/metric.rb +3 -3
  119. data/lib/datadog/core/telemetry/request.rb +3 -3
  120. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  121. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  122. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  123. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  124. data/lib/datadog/core/telemetry/transport/telemetry.rb +51 -0
  125. data/lib/datadog/core/telemetry/worker.rb +90 -24
  126. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  127. data/lib/datadog/core/transport/http/builder.rb +13 -13
  128. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  129. data/lib/datadog/core/utils/duration.rb +32 -32
  130. data/lib/datadog/core/utils/forking.rb +2 -2
  131. data/lib/datadog/core/utils/network.rb +6 -6
  132. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  133. data/lib/datadog/core/utils/time.rb +20 -0
  134. data/lib/datadog/core/utils/truncation.rb +21 -0
  135. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  136. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  137. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  138. data/lib/datadog/core/worker.rb +1 -1
  139. data/lib/datadog/core/workers/async.rb +29 -12
  140. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  141. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  142. data/lib/datadog/core.rb +8 -0
  143. data/lib/datadog/di/boot.rb +34 -0
  144. data/lib/datadog/di/remote.rb +2 -0
  145. data/lib/datadog/di.rb +5 -32
  146. data/lib/datadog/error_tracking/collector.rb +87 -0
  147. data/lib/datadog/error_tracking/component.rb +167 -0
  148. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  149. data/lib/datadog/error_tracking/configuration.rb +11 -0
  150. data/lib/datadog/error_tracking/ext.rb +18 -0
  151. data/lib/datadog/error_tracking/extensions.rb +16 -0
  152. data/lib/datadog/error_tracking/filters.rb +77 -0
  153. data/lib/datadog/error_tracking.rb +18 -0
  154. data/lib/datadog/kit/identity.rb +1 -1
  155. data/lib/datadog/profiling/collectors/code_provenance.rb +1 -1
  156. data/lib/datadog/profiling/exporter.rb +1 -1
  157. data/lib/datadog/profiling/ext.rb +0 -1
  158. data/lib/datadog/profiling/flush.rb +1 -1
  159. data/lib/datadog/profiling/http_transport.rb +1 -6
  160. data/lib/datadog/profiling/scheduler.rb +8 -1
  161. data/lib/datadog/profiling/tag_builder.rb +1 -5
  162. data/lib/datadog/tracing/analytics.rb +1 -1
  163. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +4 -1
  164. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
  165. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  166. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
  167. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  168. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  169. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -5
  170. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -5
  171. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -5
  172. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  173. data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
  174. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  175. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  176. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  177. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  178. data/lib/datadog/tracing/contrib/support.rb +28 -0
  179. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  180. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  181. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  182. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  183. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  184. data/lib/datadog/tracing/span_operation.rb +38 -14
  185. data/lib/datadog/tracing/trace_operation.rb +15 -7
  186. data/lib/datadog/tracing/tracer.rb +7 -3
  187. data/lib/datadog/tracing/utils.rb +1 -1
  188. data/lib/datadog/version.rb +1 -1
  189. data/lib/datadog.rb +2 -3
  190. metadata +53 -10
  191. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  192. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  193. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  194. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../encoding'
4
+ require_relative '../../../transport/http/api/map'
5
+ require_relative '../../../transport/http/api/instance'
6
+ require_relative '../../../transport/http/api/spec'
7
+ require_relative 'telemetry'
8
+
9
+ module Datadog
10
+ module Core
11
+ module Telemetry
12
+ module Transport
13
+ module HTTP
14
+ # Namespace for API components
15
+ module API
16
+ # Default API versions
17
+ AGENT_TELEMETRY = 'agent_telemetry'
18
+ AGENTLESS_TELEMETRY = 'agentless_telemetry'
19
+
20
+ module_function
21
+
22
+ def defaults
23
+ Datadog::Core::Transport::HTTP::API::Map[
24
+ AGENT_TELEMETRY => Telemetry::API::Spec.new do |s|
25
+ s.telemetry = Telemetry::API::Endpoint.new(
26
+ '/telemetry/proxy/api/v2/apmtelemetry',
27
+ Core::Encoding::JSONEncoder,
28
+ )
29
+ end,
30
+ AGENTLESS_TELEMETRY => Telemetry::API::Spec.new do |s|
31
+ s.telemetry = Telemetry::API::Endpoint.new(
32
+ '/api/v2/apmtelemetry',
33
+ Core::Encoding::JSONEncoder,
34
+ )
35
+ end,
36
+ ]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../transport/http/env'
4
+ require_relative '../../../transport/http/response'
5
+
6
+ # TODO: Decouple transport/http/client
7
+ #
8
+ # The standard one does `include Transport::HTTP::Statistics` and performs
9
+ # stats updates, which may or may not be desirable in general.
10
+
11
+ module Datadog
12
+ module Core
13
+ module Telemetry
14
+ module Transport
15
+ module HTTP
16
+ # Routes, encodes, and sends DI data to the trace agent via HTTP.
17
+ class Client
18
+ attr_reader :api, :logger
19
+
20
+ def initialize(api, logger:)
21
+ @api = api
22
+ @logger = logger
23
+ end
24
+
25
+ def send_request(request, &block)
26
+ # Build request into env
27
+ env = build_env(request)
28
+
29
+ # Get responses from API
30
+ yield(api, env)
31
+ rescue => e
32
+ message =
33
+ "Internal error during #{self.class.name} request. Cause: #{e.class.name} #{e.message} " \
34
+ "Location: #{Array(e.backtrace).first}"
35
+
36
+ logger.debug(message)
37
+
38
+ Datadog::Core::Transport::InternalErrorResponse.new(e)
39
+ end
40
+
41
+ def build_env(request)
42
+ Datadog::Core::Transport::HTTP::Env.new(request)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../transport/http/api/endpoint'
4
+ require_relative '../../../transport/http/api/instance'
5
+ require_relative '../../../transport/http/api/spec'
6
+ require_relative '../../../transport/request'
7
+ require_relative 'client'
8
+
9
+ module Datadog
10
+ module Core
11
+ module Telemetry
12
+ module Transport
13
+ module HTTP
14
+ module Telemetry
15
+ module Client
16
+ def send_telemetry_payload(request)
17
+ send_request(request) do |api, env| # steep:ignore
18
+ api.send_telemetry(env)
19
+ end
20
+ end
21
+ end
22
+
23
+ module API
24
+ class Instance < Core::Transport::HTTP::API::Instance
25
+ def send_telemetry(env)
26
+ raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('telemetry', self) unless spec.is_a?(Telemetry::API::Spec)
27
+
28
+ spec.send_telemetry(env) do |request_env|
29
+ call(request_env)
30
+ end
31
+ end
32
+ end
33
+
34
+ class Spec < Core::Transport::HTTP::API::Spec
35
+ attr_accessor :telemetry
36
+
37
+ def send_telemetry(env, &block)
38
+ raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('telemetry', self) if telemetry.nil?
39
+
40
+ telemetry.call(env, &block)
41
+ end
42
+ end
43
+
44
+ class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
45
+ HEADER_CONTENT_TYPE = 'Content-Type'
46
+
47
+ attr_reader \
48
+ :encoder
49
+
50
+ def initialize(path, encoder)
51
+ super(:post, path)
52
+ @encoder = encoder
53
+ end
54
+
55
+ def call(env, &block)
56
+ # Encode body & type
57
+ env.headers[HEADER_CONTENT_TYPE] = encoder.content_type
58
+ env.headers.update(headers(
59
+ request_type: env.request.request_type,
60
+ api_key: env.request.api_key,
61
+ ))
62
+ env.body = env.request.parcel.data
63
+
64
+ super
65
+ end
66
+
67
+ def headers(request_type:, api_key:, api_version: 'v2')
68
+ {
69
+ Core::Transport::Ext::HTTP::HEADER_DD_INTERNAL_UNTRACED_REQUEST => '1',
70
+ # Provided by encoder
71
+ # 'Content-Type' => 'application/json',
72
+ 'DD-Telemetry-API-Version' => api_version,
73
+ 'DD-Telemetry-Request-Type' => request_type,
74
+ 'DD-Client-Library-Language' => Core::Environment::Ext::LANG,
75
+ 'DD-Client-Library-Version' => Core::Environment::Identity.gem_datadog_version_semver2,
76
+
77
+ # Enable debug mode for telemetry
78
+ # 'DD-Telemetry-Debug-Enabled' => 'true',
79
+ }.tap do |result|
80
+ result['DD-API-KEY'] = api_key unless api_key.nil?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ HTTP::Client.include(Telemetry::Client)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'telemetry'
4
+ require_relative 'http/api'
5
+ require_relative '../../transport/http'
6
+
7
+ module Datadog
8
+ module Core
9
+ module Telemetry
10
+ module Transport
11
+ # Namespace for HTTP transport components
12
+ module HTTP
13
+ module_function
14
+
15
+ # Builds a new Transport::HTTP::Client with default settings
16
+ # Pass a block to override any settings.
17
+ def agentless_telemetry(
18
+ agent_settings:,
19
+ logger:,
20
+ api_key: nil,
21
+ api_version: nil,
22
+ headers: nil
23
+ )
24
+ Core::Transport::HTTP.build(api_instance_class: Telemetry::API::Instance,
25
+ logger: logger,
26
+ agent_settings: agent_settings,
27
+ api_version: api_version,
28
+ headers: headers) do |transport|
29
+ apis = API.defaults
30
+
31
+ transport.api API::AGENTLESS_TELEMETRY, apis[API::AGENTLESS_TELEMETRY]
32
+
33
+ # Call block to apply any customization, if provided
34
+ yield(transport) if block_given?
35
+ end.to_transport(Core::Telemetry::Transport::Telemetry::Transport).tap do |transport|
36
+ transport.api_key = api_key
37
+ end
38
+ end
39
+
40
+ # Builds a new Transport::HTTP::Client with default settings
41
+ # Pass a block to override any settings.
42
+ def agent_telemetry(
43
+ agent_settings:,
44
+ logger:,
45
+ api_version: nil,
46
+ headers: nil
47
+ )
48
+ Core::Transport::HTTP.build(api_instance_class: Telemetry::API::Instance,
49
+ logger: logger,
50
+ agent_settings: agent_settings, api_version: api_version, headers: headers) do |transport|
51
+ apis = API.defaults
52
+
53
+ transport.api API::AGENT_TELEMETRY, apis[API::AGENT_TELEMETRY]
54
+
55
+ # Call block to apply any customization, if provided
56
+ yield(transport) if block_given?
57
+ end.to_transport(Core::Telemetry::Transport::Telemetry::Transport)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../transport/parcel'
4
+ require_relative 'http/client'
5
+ require_relative 'http/telemetry'
6
+
7
+ module Datadog
8
+ module Core
9
+ module Telemetry
10
+ module Transport
11
+ module Telemetry
12
+ class EncodedParcel
13
+ include Datadog::Core::Transport::Parcel
14
+ end
15
+
16
+ class Request < Datadog::Core::Transport::Request
17
+ attr_reader :request_type
18
+ attr_reader :api_key
19
+
20
+ def initialize(request_type, parcel, api_key)
21
+ @request_type = request_type
22
+ super(parcel)
23
+ @api_key = api_key
24
+ end
25
+ end
26
+
27
+ class Transport
28
+ attr_reader :client, :apis, :default_api, :current_api_id, :logger
29
+ attr_accessor :api_key
30
+
31
+ def initialize(apis, default_api, logger:)
32
+ @apis = apis
33
+ @logger = logger
34
+
35
+ @client = HTTP::Client.new(@apis[default_api], logger: logger)
36
+ end
37
+
38
+ def send_telemetry(request_type:, payload:)
39
+ json = JSON.dump(payload)
40
+ parcel = EncodedParcel.new(json)
41
+ request = Request.new(request_type, parcel, api_key)
42
+
43
+ @client.send_telemetry_payload(request)
44
+ # Perform no error checking here
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -17,8 +17,6 @@ module Datadog
17
17
  DEFAULT_BUFFER_MAX_SIZE = 1000
18
18
  APP_STARTED_EVENT_RETRIES = 10
19
19
 
20
- TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
21
-
22
20
  def initialize(
23
21
  heartbeat_interval_seconds:,
24
22
  metrics_aggregation_interval_seconds:,
@@ -48,14 +46,25 @@ module Datadog
48
46
  @buffer_size = buffer_size
49
47
 
50
48
  self.buffer = buffer_klass.new(@buffer_size)
49
+
50
+ @initial_event_once = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
51
51
  end
52
52
 
53
53
  attr_reader :logger
54
+ attr_reader :initial_event_once
55
+ attr_reader :initial_event
54
56
 
55
- def start
57
+ # Returns true if worker thread is successfully started,
58
+ # false if worker thread was not started but telemetry is enabled,
59
+ # nil if telemetry is disabled.
60
+ def start(initial_event)
56
61
  return if !enabled? || forked?
57
62
 
63
+ @initial_event = initial_event
64
+
58
65
  # starts async worker
66
+ # perform should return true if thread was actually started,
67
+ # false otherwise
59
68
  perform
60
69
  end
61
70
 
@@ -65,18 +74,60 @@ module Datadog
65
74
  super
66
75
  end
67
76
 
77
+ # Returns true if event was enqueued, nil if not.
78
+ # While returning false may seem more reasonable, the only reason
79
+ # for not enqueueing event (presently) is that telemetry is disabled
80
+ # altogether, and in this case other methods return nil.
68
81
  def enqueue(event)
69
82
  return if !enabled? || forked?
70
83
 
71
84
  buffer.push(event)
85
+ true
86
+ end
87
+
88
+ def sent_initial_event?
89
+ initial_event_once.success?
72
90
  end
73
91
 
74
- def sent_started_event?
75
- TELEMETRY_STARTED_ONCE.success?
92
+ def failed_initial_event?
93
+ initial_event_once.failed?
76
94
  end
77
95
 
78
- def failed_to_start?
79
- TELEMETRY_STARTED_ONCE.failed?
96
+ def need_initial_event?
97
+ !sent_initial_event? && !failed_initial_event?
98
+ end
99
+
100
+ # Wait for the worker to send out all events that have already
101
+ # been queued, up to 15 seconds. Returns whether all events have
102
+ # been flushed.
103
+ #
104
+ # @api private
105
+ def flush
106
+ return true unless enabled? || !run_loop?
107
+
108
+ started = Utils::Time.get_time
109
+ loop do
110
+ # The AppStarted event is triggered by the worker itself,
111
+ # from the worker thread. As such the main thread has no way
112
+ # to delay itself until that event is queued and we need some
113
+ # way to wait until that event is sent out to assert on it in
114
+ # the test suite. Check the run once flag which *should*
115
+ # indicate the event has been queued (at which point our queue
116
+ # depth check should waint until it's sent).
117
+ # This is still a hack because the flag can be overridden
118
+ # either way with or without the event being sent out.
119
+ # Note that if the AppStarted sending fails, this check
120
+ # will return false and flushing will be blocked until the
121
+ # 15 second timeout.
122
+ # Note that the first wait interval between telemetry event
123
+ # sending is 10 seconds, the timeout needs to be strictly
124
+ # greater than that.
125
+ return true if buffer.empty? && !in_iteration? && sent_initial_event?
126
+
127
+ sleep 0.5
128
+
129
+ return false if Utils::Time.get_time - started > 15
130
+ end
80
131
  end
81
132
 
82
133
  private
@@ -84,11 +135,26 @@ module Datadog
84
135
  def perform(*events)
85
136
  return if !enabled? || forked?
86
137
 
87
- started! unless sent_started_event?
138
+ if need_initial_event?
139
+ started!
140
+ unless sent_initial_event?
141
+ # We still haven't succeeded in sending the started event,
142
+ # which will make flush_events do nothing - but the events
143
+ # given to us as the parameter have already been removed
144
+ # from the queue.
145
+ # Put the events back to the front of the queue to not
146
+ # lose them.
147
+ buffer.unshift(*events)
148
+ return
149
+ end
150
+ end
88
151
 
89
152
  metric_events = @metrics_manager.flush!
90
153
  events = [] if events.nil?
91
- flush_events(events + metric_events)
154
+ events += metric_events
155
+ if events.any?
156
+ flush_events(events)
157
+ end
92
158
 
93
159
  @current_ticks += 1
94
160
  return if @current_ticks < @ticks_per_heartbeat
@@ -98,9 +164,6 @@ module Datadog
98
164
  end
99
165
 
100
166
  def flush_events(events)
101
- return if events.empty?
102
- return if !enabled? || !sent_started_event?
103
-
104
167
  events = deduplicate_logs(events)
105
168
 
106
169
  logger.debug { "Sending #{events&.count} telemetry events" }
@@ -108,7 +171,7 @@ module Datadog
108
171
  end
109
172
 
110
173
  def heartbeat!
111
- return if !enabled? || !sent_started_event?
174
+ return if !enabled? || !sent_initial_event?
112
175
 
113
176
  send_event(Event::AppHeartbeat.new)
114
177
  end
@@ -116,26 +179,29 @@ module Datadog
116
179
  def started!
117
180
  return unless enabled?
118
181
 
119
- if failed_to_start?
120
- logger.debug('Telemetry app-started event exhausted retries, disabling telemetry worker')
121
- disable!
122
- return
123
- end
124
-
125
- TELEMETRY_STARTED_ONCE.run do
126
- res = send_event(Event::AppStarted.new)
182
+ initial_event_once.run do
183
+ res = send_event(initial_event)
127
184
 
128
185
  if res.ok?
129
- logger.debug('Telemetry app-started event is successfully sent')
186
+ logger.debug { "Telemetry initial event (#{initial_event.type}) is successfully sent" }
130
187
 
131
- send_event(Event::AppDependenciesLoaded.new) if @dependency_collection
188
+ # TODO Dependencies loaded event should probably check for new
189
+ # dependencies and send the new ones.
190
+ # System tests demand only one instance of this event per
191
+ # dependency.
192
+ send_event(Event::AppDependenciesLoaded.new) if @dependency_collection && initial_event.class.eql?(Telemetry::Event::AppStarted) # standard:disable Style/ClassEqualityComparison:
132
193
 
133
194
  true
134
195
  else
135
- logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...')
196
+ logger.debug("Error sending telemetry initial event (#{initial_event.type}), retry after heartbeat interval...")
136
197
  false
137
198
  end
138
199
  end
200
+
201
+ if failed_initial_event?
202
+ logger.debug { "Telemetry initial event (#{initial_event.type}) exhausted retries, disabling telemetry worker" }
203
+ disable!
204
+ end
139
205
  end
140
206
 
141
207
  def send_event(event)
@@ -38,7 +38,8 @@ module Datadog
38
38
  @status = status
39
39
  end
40
40
 
41
- def url; end
41
+ def url
42
+ end
42
43
 
43
44
  # Response for test adapter
44
45
  class Response
@@ -41,19 +41,19 @@ module Datadog
41
41
 
42
42
  def adapter(config, *args, **kwargs)
43
43
  @default_adapter = case config
44
- when Core::Configuration::AgentSettingsResolver::AgentSettings
45
- registry_klass = REGISTRY.get(config.adapter)
46
- raise UnknownAdapterError, config.adapter if registry_klass.nil?
47
-
48
- registry_klass.build(config)
49
- when Symbol
50
- registry_klass = REGISTRY.get(config)
51
- raise UnknownAdapterError, config if registry_klass.nil?
52
-
53
- registry_klass.new(*args, **kwargs)
54
- else
55
- config
56
- end
44
+ when Core::Configuration::AgentSettingsResolver::AgentSettings
45
+ registry_klass = REGISTRY.get(config.adapter)
46
+ raise UnknownAdapterError, config.adapter if registry_klass.nil?
47
+
48
+ registry_klass.build(config)
49
+ when Symbol
50
+ registry_klass = REGISTRY.get(config)
51
+ raise UnknownAdapterError, config if registry_klass.nil?
52
+
53
+ registry_klass.new(*args, **kwargs)
54
+ else
55
+ config
56
+ end
57
57
  end
58
58
 
59
59
  def headers(values = {})
@@ -51,13 +51,13 @@ module Datadog
51
51
  def fork
52
52
  # If a block is provided, it must be wrapped to trigger callbacks.
53
53
  child_block = if block_given?
54
- proc do
55
- AtForkMonkeyPatch.run_at_fork_blocks(:child)
54
+ proc do
55
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
56
56
 
57
- # Invoke original block
58
- yield
59
- end
60
- end
57
+ # Invoke original block
58
+ yield
59
+ end
60
+ end
61
61
 
62
62
  # Start fork
63
63
  # If a block is provided, use the wrapped version.
@@ -7,42 +7,42 @@ module Datadog
7
7
  module Duration
8
8
  def self.call(value, base: :s)
9
9
  cast = if value.include?('.')
10
- method(:Float)
11
- else
12
- method(:Integer)
13
- end
10
+ method(:Float)
11
+ else
12
+ method(:Integer)
13
+ end
14
14
 
15
15
  scale = case base
16
- when :s
17
- 1_000_000_000
18
- when :ms
19
- 1_000_000
20
- when :us
21
- 1000
22
- when :ns
23
- 1
24
- else
25
- raise ArgumentError, "invalid base: #{base.inspect}"
26
- end
16
+ when :s
17
+ 1_000_000_000
18
+ when :ms
19
+ 1_000_000
20
+ when :us
21
+ 1000
22
+ when :ns
23
+ 1
24
+ else
25
+ raise ArgumentError, "invalid base: #{base.inspect}"
26
+ end
27
27
 
28
28
  result = case value
29
- when /^(\d+(?:\.\d+)?)h$/
30
- cast.call(Regexp.last_match(1)) * 1_000_000_000 * 60 * 60 / scale
31
- when /^(\d+(?:\.\d+)?)m$/
32
- cast.call(Regexp.last_match(1)) * 1_000_000_000 * 60 / scale
33
- when /^(\d+(?:\.\d+)?)s$/
34
- cast.call(Regexp.last_match(1)) * 1_000_000_000 / scale
35
- when /^(\d+(?:\.\d+)?)ms$/
36
- cast.call(Regexp.last_match(1)) * 1_000_000 / scale
37
- when /^(\d+(?:\.\d+)?)us$/
38
- cast.call(Regexp.last_match(1)) * 1_000 / scale
39
- when /^(\d+(?:\.\d+)?)ns$/
40
- cast.call(Regexp.last_match(1)) / scale
41
- when /^(\d+(?:\.\d+)?)$/
42
- cast.call(Regexp.last_match(1))
43
- else
44
- raise ArgumentError, "invalid duration: #{value.inspect}"
45
- end
29
+ when /^(\d+(?:\.\d+)?)h$/
30
+ cast.call(Regexp.last_match(1)) * 1_000_000_000 * 60 * 60 / scale
31
+ when /^(\d+(?:\.\d+)?)m$/
32
+ cast.call(Regexp.last_match(1)) * 1_000_000_000 * 60 / scale
33
+ when /^(\d+(?:\.\d+)?)s$/
34
+ cast.call(Regexp.last_match(1)) * 1_000_000_000 / scale
35
+ when /^(\d+(?:\.\d+)?)ms$/
36
+ cast.call(Regexp.last_match(1)) * 1_000_000 / scale
37
+ when /^(\d+(?:\.\d+)?)us$/
38
+ cast.call(Regexp.last_match(1)) * 1_000 / scale
39
+ when /^(\d+(?:\.\d+)?)ns$/
40
+ cast.call(Regexp.last_match(1)) / scale
41
+ when /^(\d+(?:\.\d+)?)$/
42
+ cast.call(Regexp.last_match(1))
43
+ else
44
+ raise ArgumentError, "invalid duration: #{value.inspect}"
45
+ end
46
46
  # @type var result: Numeric
47
47
  result.round
48
48
  end
@@ -47,12 +47,12 @@ module Datadog
47
47
  # This wrapper prevents this by initializing the fork PID when the object is created.
48
48
  if RUBY_VERSION >= '3'
49
49
  def initialize(*args, **kwargs, &block)
50
- super(*args, **kwargs, &block)
50
+ super
51
51
  update_fork_pid!
52
52
  end
53
53
  else
54
54
  def initialize(*args, &block)
55
- super(*args, &block)
55
+ super
56
56
  update_fork_pid!
57
57
  end
58
58
  end
@@ -32,7 +32,7 @@ module Datadog
32
32
  def stripped_ip_from_request_headers(headers, ip_headers_to_check: DEFAULT_IP_HEADERS_NAMES)
33
33
  ip = ip_header(headers, ip_headers_to_check)
34
34
 
35
- ip ? ip.to_s : nil
35
+ ip&.to_s
36
36
  end
37
37
 
38
38
  # @param [String] IP value.
@@ -40,7 +40,7 @@ module Datadog
40
40
  # @return [nil] when no valid IP value found.
41
41
  def stripped_ip(ip)
42
42
  ip = ip_to_ipaddr(ip)
43
- ip ? ip.to_s : nil
43
+ ip&.to_s
44
44
  end
45
45
 
46
46
  private
@@ -52,10 +52,10 @@ module Datadog
52
52
  return unless ip
53
53
 
54
54
  clean_ip = if likely_ipv4?(ip)
55
- strip_ipv4_port(ip)
56
- else
57
- strip_zone_specifier(strip_ipv6_port(ip))
58
- end
55
+ strip_ipv4_port(ip)
56
+ else
57
+ strip_zone_specifier(strip_ipv6_port(ip))
58
+ end
59
59
 
60
60
  begin
61
61
  IPAddr.new(clean_ip)