datadog 2.15.0 → 2.16.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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -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/extconf.rb +3 -0
  6. data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
  7. data/ext/libdatadog_api/crashtracker.c +1 -9
  8. data/ext/libdatadog_api/crashtracker.h +5 -0
  9. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  10. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  11. data/ext/libdatadog_api/init.c +15 -0
  12. data/ext/libdatadog_api/library_config.c +122 -0
  13. data/ext/libdatadog_api/library_config.h +19 -0
  14. data/ext/libdatadog_api/process_discovery.c +117 -0
  15. data/ext/libdatadog_api/process_discovery.h +5 -0
  16. data/lib/datadog/appsec/actions_handler.rb +3 -2
  17. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1344 -0
  18. data/lib/datadog/appsec/assets/waf_rules/strict.json +1344 -0
  19. data/lib/datadog/appsec/autoload.rb +1 -1
  20. data/lib/datadog/appsec/component.rb +11 -4
  21. data/lib/datadog/appsec/configuration/settings.rb +31 -18
  22. data/lib/datadog/appsec/context.rb +1 -1
  23. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  24. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  25. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  26. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +2 -3
  27. data/lib/datadog/appsec/contrib/devise/ext.rb +1 -0
  28. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  29. data/lib/datadog/appsec/contrib/devise/patcher.rb +3 -5
  30. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +17 -4
  31. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  32. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  33. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  34. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  35. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  36. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +22 -32
  38. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  39. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +16 -16
  40. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  41. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  42. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  43. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  44. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  45. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  46. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  47. data/lib/datadog/appsec/event.rb +85 -95
  48. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +5 -2
  49. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  50. data/lib/datadog/appsec/monitor/gateway/watcher.rb +42 -12
  51. data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
  52. data/lib/datadog/appsec/processor/rule_merger.rb +5 -5
  53. data/lib/datadog/appsec/processor.rb +1 -1
  54. data/lib/datadog/appsec/remote.rb +14 -13
  55. data/lib/datadog/appsec/response.rb +6 -6
  56. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  57. data/lib/datadog/appsec/security_event.rb +39 -0
  58. data/lib/datadog/appsec.rb +1 -1
  59. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  60. data/lib/datadog/core/configuration/components.rb +19 -10
  61. data/lib/datadog/core/configuration/option.rb +61 -25
  62. data/lib/datadog/core/configuration/settings.rb +10 -0
  63. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  64. data/lib/datadog/core/configuration.rb +24 -0
  65. data/lib/datadog/core/crashtracking/component.rb +1 -9
  66. data/lib/datadog/core/environment/git.rb +1 -0
  67. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  68. data/lib/datadog/core/metrics/client.rb +8 -7
  69. data/lib/datadog/core/process_discovery.rb +32 -0
  70. data/lib/datadog/core/remote/client.rb +7 -0
  71. data/lib/datadog/core/runtime/metrics.rb +1 -1
  72. data/lib/datadog/core/telemetry/component.rb +60 -50
  73. data/lib/datadog/core/telemetry/emitter.rb +17 -11
  74. data/lib/datadog/core/telemetry/event.rb +7 -4
  75. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  76. data/lib/datadog/core/telemetry/request.rb +3 -3
  77. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  78. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  79. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  80. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  81. data/lib/datadog/core/telemetry/transport/telemetry.rb +52 -0
  82. data/lib/datadog/core/telemetry/worker.rb +45 -0
  83. data/lib/datadog/core/utils/time.rb +12 -0
  84. data/lib/datadog/core/workers/async.rb +20 -2
  85. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  86. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  87. data/lib/datadog/core.rb +8 -0
  88. data/lib/datadog/di/boot.rb +34 -0
  89. data/lib/datadog/di/remote.rb +2 -0
  90. data/lib/datadog/di.rb +5 -32
  91. data/lib/datadog/error_tracking/collector.rb +87 -0
  92. data/lib/datadog/error_tracking/component.rb +167 -0
  93. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  94. data/lib/datadog/error_tracking/configuration.rb +11 -0
  95. data/lib/datadog/error_tracking/ext.rb +18 -0
  96. data/lib/datadog/error_tracking/extensions.rb +16 -0
  97. data/lib/datadog/error_tracking/filters.rb +77 -0
  98. data/lib/datadog/error_tracking.rb +18 -0
  99. data/lib/datadog/kit/identity.rb +1 -1
  100. data/lib/datadog/profiling/exporter.rb +1 -1
  101. data/lib/datadog/tracing/analytics.rb +1 -1
  102. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  103. data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
  104. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  105. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  106. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  107. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  108. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  109. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  110. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  111. data/lib/datadog/tracing/span_operation.rb +38 -14
  112. data/lib/datadog/tracing/trace_operation.rb +15 -7
  113. data/lib/datadog/tracing/tracer.rb +7 -3
  114. data/lib/datadog/tracing/utils.rb +1 -1
  115. data/lib/datadog/version.rb +1 -1
  116. data/lib/datadog.rb +2 -3
  117. metadata +34 -8
  118. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  119. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  120. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  121. data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -74,10 +74,9 @@ module Datadog
74
74
  profiler: {
75
75
  enabled: Datadog::Profiling.enabled?,
76
76
  },
77
- # DEV: Not implemented yet
78
- # dynamic_instrumentation: {
79
- # enabled: true,
80
- # }
77
+ dynamic_instrumentation: {
78
+ enabled: defined?(Datadog::DI) && Datadog::DI.respond_to?(:enabled?) && Datadog::DI.enabled?,
79
+ }
81
80
  }
82
81
 
83
82
  if (unsupported_reason = Datadog::Profiling.unsupported_reason)
@@ -91,6 +90,7 @@ module Datadog
91
90
  end
92
91
 
93
92
  TARGET_OPTIONS = %w[
93
+ dynamic_instrumentation.enabled
94
94
  logger.level
95
95
  profiling.advanced.code_provenance_enabled
96
96
  profiling.advanced.endpoint.collection.enabled
@@ -114,6 +114,9 @@ module Datadog
114
114
  seq_id = Event.configuration_sequence.next
115
115
 
116
116
  list = [
117
+ conf_value('DD_GIT_REPOSITORY_URL', Core::Environment::Git.git_repository_url, seq_id, 'env_var'),
118
+ conf_value('DD_GIT_COMMIT_SHA', Core::Environment::Git.git_commit_sha, seq_id, 'env_var'),
119
+
117
120
  conf_value('DD_AGENT_HOST', config.agent.host, seq_id),
118
121
  conf_value('DD_AGENT_TRANSPORT', agent_transport(config), seq_id),
119
122
  conf_value('DD_TRACE_SAMPLE_RATE', to_value(config.tracing.sampling.default_rate), seq_id),
@@ -1,108 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../response'
3
+ # datadog-ci-rb versions 1.15.0 and lower require this file and guard
4
+ # the require with a rescue of StandardError. Unfortunately LoadError,
5
+ # which would be raised if the file is missing, is not a subclass of
6
+ # StandardError and thus would not be caught by the rescue.
7
+ # We provide this file with a dummy class in it to avoid exceptions
8
+ # in datadog-ci-rb until version 2.0 is released.
9
+ #
10
+ # Note that datadog-ci-rb patches telemetry transport to be "real" even when
11
+ # webmock is used; this patching won't work with datadog-ci-rb versions
12
+ # 1.15.0 and older and dd-trace-rb 2.16.0 and newer. There will be no
13
+ # errors/exceptions reported but telemetry events will not be sent.
4
14
 
5
15
  module Datadog
6
16
  module Core
7
17
  module Telemetry
8
18
  module Http
9
19
  module Adapters
10
- # Class defining methods to make http requests via NET
11
- class Net
12
- attr_reader \
13
- :hostname,
14
- :port,
15
- :timeout,
16
- :ssl
17
-
18
- DEFAULT_TIMEOUT = 2
19
-
20
- def initialize(hostname:, port: nil, timeout: DEFAULT_TIMEOUT, ssl: true)
21
- @hostname = hostname
22
- @port = port
23
- @timeout = timeout
24
- @ssl = ssl.nil? || ssl
25
- end
26
-
27
- def open(&block)
28
- req = ::Net::HTTP.new(@hostname, @port)
29
-
30
- req.use_ssl = @ssl
31
- req.open_timeout = req.read_timeout = @timeout
32
-
33
- req.start(&block)
34
- end
35
-
36
- def post(env)
37
- post = ::Net::HTTP::Post.new(env.path, env.headers)
38
- post.body = env.body
39
-
40
- http_response = open do |http|
41
- http.request(post)
42
- end
43
-
44
- Response.new(http_response)
45
- rescue StandardError => e
46
- Datadog.logger.debug("Unable to send telemetry event to agent: #{e}")
47
- Telemetry::Http::InternalErrorResponse.new(e)
48
- end
49
-
50
- # Data structure for an HTTP Response
51
- class Response
52
- include Datadog::Core::Telemetry::Http::Response
53
-
54
- attr_reader :http_response
55
-
56
- def initialize(http_response)
57
- @http_response = http_response
58
- end
59
-
60
- def payload
61
- return super if http_response.nil?
62
-
63
- http_response.body
64
- end
65
-
66
- def code
67
- return super if http_response.nil?
68
-
69
- http_response.code.to_i
70
- end
71
-
72
- def ok?
73
- return super if http_response.nil?
74
-
75
- code.between?(200, 299)
76
- end
77
-
78
- def unsupported?
79
- return super if http_response.nil?
80
-
81
- code == 415
82
- end
83
-
84
- def not_found?
85
- return super if http_response.nil?
86
-
87
- code == 404
88
- end
89
-
90
- def client_error?
91
- return super if http_response.nil?
92
-
93
- code.between?(400, 499)
94
- end
95
-
96
- def server_error?
97
- return super if http_response.nil?
98
-
99
- code.between?(500, 599)
100
- end
101
-
102
- def inspect
103
- "#{super}, http_response:#{http_response}"
104
- end
105
- end
20
+ class Net # rubocop:disable Lint/EmptyClass
106
21
  end
107
22
  end
108
23
  end
@@ -11,11 +11,11 @@ module Datadog
11
11
  class << self
12
12
  using Core::Utils::Hash::Refinement
13
13
 
14
- def build_payload(event, seq_id)
14
+ def build_payload(event, seq_id, api_version: 'v2', debug: false)
15
15
  hash = {
16
- api_version: Http::Ext::API_VERSION,
16
+ api_version: api_version,
17
17
  application: application,
18
- debug: false,
18
+ debug: debug,
19
19
  host: host,
20
20
  payload: event.payload,
21
21
  request_type: event.type,
@@ -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_version: 'v2', api_key:)
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,52 @@
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
+
18
+ attr_reader :request_type
19
+ attr_reader :api_key
20
+
21
+ def initialize(request_type, parcel, api_key)
22
+ @request_type = request_type
23
+ super(parcel)
24
+ @api_key = api_key
25
+ end
26
+ end
27
+
28
+ class Transport
29
+ attr_reader :client, :apis, :default_api, :current_api_id, :logger
30
+ attr_accessor :api_key
31
+
32
+ def initialize(apis, default_api, logger:)
33
+ @apis = apis
34
+ @logger = logger
35
+
36
+ @client = HTTP::Client.new(@apis[default_api], logger: logger)
37
+ end
38
+
39
+ def send_telemetry(request_type:, payload:)
40
+ json = JSON.dump(payload)
41
+ parcel = EncodedParcel.new(json)
42
+ request = Request.new(request_type, parcel, api_key)
43
+
44
+ @client.send_telemetry_payload(request)
45
+ # Perform no error checking here
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -52,10 +52,15 @@ module Datadog
52
52
 
53
53
  attr_reader :logger
54
54
 
55
+ # Returns true if worker thread is successfully started,
56
+ # false if worker thread was not started but telemetry is enabled,
57
+ # nil if telemetry is disabled.
55
58
  def start
56
59
  return if !enabled? || forked?
57
60
 
58
61
  # starts async worker
62
+ # perform should return true if thread was actually started,
63
+ # false otherwise
59
64
  perform
60
65
  end
61
66
 
@@ -65,10 +70,15 @@ module Datadog
65
70
  super
66
71
  end
67
72
 
73
+ # Returns true if event was enqueued, nil if not.
74
+ # While returning false may seem more reasonable, the only reason
75
+ # for not enqueueing event (presently) is that telemetry is disabled
76
+ # altogether, and in this case other methods return nil.
68
77
  def enqueue(event)
69
78
  return if !enabled? || forked?
70
79
 
71
80
  buffer.push(event)
81
+ true
72
82
  end
73
83
 
74
84
  def sent_started_event?
@@ -79,6 +89,39 @@ module Datadog
79
89
  TELEMETRY_STARTED_ONCE.failed?
80
90
  end
81
91
 
92
+ # Wait for the worker to send out all events that have already
93
+ # been queued, up to 15 seconds. Returns whether all events have
94
+ # been flushed.
95
+ #
96
+ # @api private
97
+ def flush
98
+ return true unless enabled? || !run_loop?
99
+
100
+ started = Utils::Time.get_time
101
+ loop do
102
+ # The AppStarted event is triggered by the worker itself,
103
+ # from the worker thread. As such the main thread has no way
104
+ # to delay itself until that event is queued and we need some
105
+ # way to wait until that event is sent out to assert on it in
106
+ # the test suite. Check the run once flag which *should*
107
+ # indicate the event has been queued (at which point our queue
108
+ # depth check should waint until it's sent).
109
+ # This is still a hack because the flag can be overridden
110
+ # either way with or without the event being sent out.
111
+ # Note that if the AppStarted sending fails, this check
112
+ # will return false and flushing will be blocked until the
113
+ # 15 second timeout.
114
+ # Note that the first wait interval between telemetry event
115
+ # sending is 10 seconds, the timeout needs to be strictly
116
+ # greater than that.
117
+ return true if buffer.empty? && !in_iteration? && TELEMETRY_STARTED_ONCE.success?
118
+
119
+ sleep 0.5
120
+
121
+ return false if Utils::Time.get_time - started > 15
122
+ end
123
+ end
124
+
82
125
  private
83
126
 
84
127
  def perform(*events)
@@ -99,6 +142,8 @@ module Datadog
99
142
 
100
143
  def flush_events(events)
101
144
  return if events.empty?
145
+ # TODO: can this method silently drop events which are
146
+ # generated prior to the started event being submitted?
102
147
  return if !enabled? || !sent_started_event?
103
148
 
104
149
  events = deduplicate_logs(events)
@@ -31,6 +31,12 @@ module Datadog
31
31
  #
32
32
  # @param block [Proc] block that returns a `Time` object representing the current wall time
33
33
  def now_provider=(block)
34
+ class << self
35
+ # Avoid method redefinition warning.
36
+ # `rescue nil` is added in case customers remove the method
37
+ # themselves to squelch the warning.
38
+ remove_method(:now) rescue nil
39
+ end
34
40
  define_singleton_method(:now, &block)
35
41
  end
36
42
 
@@ -43,6 +49,12 @@ module Datadog
43
49
  #
44
50
  # @param block [Proc] block that accepts unit and returns timestamp in the requested unit
45
51
  def get_time_provider=(block)
52
+ class << self
53
+ # Avoid method redefinition warning
54
+ # `rescue nil` is added in case customers remove the method
55
+ # themselves to squelch the warning.
56
+ remove_method(:get_time) rescue nil
57
+ end
46
58
  define_singleton_method(:get_time, &block)
47
59
  end
48
60
 
@@ -47,6 +47,14 @@ module Datadog
47
47
  @run_async = false
48
48
  Datadog.logger.debug { "Forcibly terminating worker thread for: #{self}" }
49
49
  worker.terminate
50
+ # Wait for the worker thread to end
51
+ begin
52
+ Timeout.timeout(SHUTDOWN_TIMEOUT) do
53
+ worker.join
54
+ end
55
+ rescue Timeout::Error
56
+ Datadog.logger.debug { "Worker thread did not end after #{SHUTDOWN_TIMEOUT} seconds: #{self}" }
57
+ end
50
58
  true
51
59
  end
52
60
 
@@ -112,23 +120,33 @@ module Datadog
112
120
  @worker ||= nil
113
121
  end
114
122
 
123
+ # Returns true if worker thread is successfully started,
124
+ # false if it is not started. Reasons for not starting the worker
125
+ # thread: it is already running, or the process forked and fork
126
+ # policy is to stop the workers on fork (which means they are
127
+ # not started in children, really).
115
128
  def start_async(&block)
116
129
  mutex.synchronize do
117
- return if running?
130
+ return false if running?
118
131
 
119
132
  if forked?
120
133
  case fork_policy
121
134
  when FORK_POLICY_STOP
122
135
  stop_fork
136
+ false
123
137
  when FORK_POLICY_RESTART
138
+ # restart_after_fork should return true
124
139
  restart_after_fork(&block)
125
140
  end
126
141
  elsif !run_async?
142
+ # start_worker should return true
127
143
  start_worker(&block)
128
144
  end
129
145
  end
130
146
  end
131
147
 
148
+ # Returns true if worker thread is successfully started,
149
+ # which should generally be always.
132
150
  def start_worker
133
151
  @run_async = true
134
152
  @pid = Process.pid
@@ -151,7 +169,7 @@ module Datadog
151
169
  @worker.name = self.class.name
152
170
  @worker.thread_variable_set(:fork_safe, true)
153
171
 
154
- nil
172
+ true
155
173
  end
156
174
 
157
175
  def stop_fork
@@ -21,7 +21,18 @@ module Datadog
21
21
  # Methods that must be prepended
22
22
  module PrependedMethods
23
23
  def perform(*args)
24
- perform_loop { super(*args) }
24
+ perform_loop do
25
+ @in_iteration = true
26
+ begin
27
+ super(*args)
28
+ ensure
29
+ @in_iteration = false
30
+ end
31
+ end
32
+ end
33
+
34
+ def in_iteration?
35
+ defined?(@in_iteration) && @in_iteration
25
36
  end
26
37
  end
27
38
 
@@ -20,8 +20,8 @@ module Datadog
20
20
  attr_reader \
21
21
  :metrics
22
22
 
23
- def initialize(options = {})
24
- @metrics = options.fetch(:metrics) { Core::Runtime::Metrics.new(logger: options[:logger]) }
23
+ def initialize(telemetry:, **options)
24
+ @metrics = options.fetch(:metrics) { Core::Runtime::Metrics.new(logger: options[:logger], telemetry: telemetry) }
25
25
 
26
26
  # Workers::Async::Thread settings
27
27
  self.fork_policy = options.fetch(:fork_policy, Workers::Async::Thread::FORK_POLICY_STOP)
data/lib/datadog/core.rb CHANGED
@@ -11,6 +11,14 @@ module Datadog
11
11
  # for higher-level features.
12
12
  module Core
13
13
  extend Core::Deprecations
14
+
15
+ LIBDATADOG_API_FAILURE =
16
+ begin
17
+ require "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
18
+ nil
19
+ rescue LoadError => e
20
+ e.message
21
+ end
14
22
  end
15
23
 
16
24
  extend Core::Extensions