datadog 2.2.0 → 2.4.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +14 -26
  5. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  7. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
  18. data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
  19. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  21. data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
  22. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  23. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  24. data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  27. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
  30. data/ext/datadog_profiling_native_extension/profiling.c +1 -3
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  33. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  38. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
  39. data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
  40. data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
  41. data/ext/libdatadog_api/extconf.rb +108 -0
  42. data/ext/libdatadog_api/macos_development.md +26 -0
  43. data/ext/libdatadog_extconf_helpers.rb +130 -0
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  46. data/lib/datadog/appsec/component.rb +29 -8
  47. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  48. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  49. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  50. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  51. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
  52. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  53. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
  54. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  55. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  56. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  57. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
  58. data/lib/datadog/appsec/event.rb +1 -1
  59. data/lib/datadog/appsec/processor/actions.rb +1 -1
  60. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  61. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  62. data/lib/datadog/appsec/processor.rb +36 -37
  63. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  64. data/lib/datadog/appsec/remote.rb +7 -3
  65. data/lib/datadog/appsec/response.rb +15 -1
  66. data/lib/datadog/appsec.rb +3 -2
  67. data/lib/datadog/core/configuration/components.rb +18 -15
  68. data/lib/datadog/core/configuration/settings.rb +135 -9
  69. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  70. data/lib/datadog/core/crashtracking/component.rb +111 -0
  71. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  72. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  73. data/lib/datadog/core/environment/execution.rb +5 -5
  74. data/lib/datadog/core/metrics/client.rb +7 -0
  75. data/lib/datadog/core/rate_limiter.rb +183 -0
  76. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  77. data/lib/datadog/core/remote/component.rb +4 -2
  78. data/lib/datadog/core/remote/negotiation.rb +4 -4
  79. data/lib/datadog/core/remote/tie.rb +2 -0
  80. data/lib/datadog/core/runtime/metrics.rb +1 -1
  81. data/lib/datadog/core/telemetry/component.rb +51 -2
  82. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  83. data/lib/datadog/core/telemetry/event.rb +37 -1
  84. data/lib/datadog/core/telemetry/ext.rb +1 -0
  85. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  86. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  87. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  88. data/lib/datadog/core/telemetry/logger.rb +51 -0
  89. data/lib/datadog/core/telemetry/logging.rb +71 -0
  90. data/lib/datadog/core/telemetry/request.rb +13 -1
  91. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  92. data/lib/datadog/core/utils/time.rb +12 -0
  93. data/lib/datadog/di/code_tracker.rb +168 -0
  94. data/lib/datadog/di/configuration/settings.rb +163 -0
  95. data/lib/datadog/di/configuration.rb +11 -0
  96. data/lib/datadog/di/error.rb +31 -0
  97. data/lib/datadog/di/extensions.rb +16 -0
  98. data/lib/datadog/di/probe.rb +133 -0
  99. data/lib/datadog/di/probe_builder.rb +41 -0
  100. data/lib/datadog/di/redactor.rb +188 -0
  101. data/lib/datadog/di/serializer.rb +193 -0
  102. data/lib/datadog/di.rb +14 -0
  103. data/lib/datadog/kit/appsec/events.rb +2 -4
  104. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  105. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  106. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  107. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
  109. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  110. data/lib/datadog/profiling/collectors/info.rb +15 -6
  111. data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
  112. data/lib/datadog/profiling/component.rb +89 -95
  113. data/lib/datadog/profiling/exporter.rb +3 -3
  114. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  115. data/lib/datadog/profiling/ext.rb +21 -21
  116. data/lib/datadog/profiling/flush.rb +1 -1
  117. data/lib/datadog/profiling/http_transport.rb +14 -7
  118. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  119. data/lib/datadog/profiling/preload.rb +1 -1
  120. data/lib/datadog/profiling/profiler.rb +5 -8
  121. data/lib/datadog/profiling/scheduler.rb +33 -25
  122. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  123. data/lib/datadog/profiling/tag_builder.rb +2 -2
  124. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  125. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  126. data/lib/datadog/profiling.rb +4 -5
  127. data/lib/datadog/single_step_instrument.rb +12 -0
  128. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  129. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  130. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  132. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  133. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  134. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  135. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
  136. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  137. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  138. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  139. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  141. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  142. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  143. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  144. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  145. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
  146. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  149. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  150. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  151. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  152. data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
  153. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  154. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  155. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  156. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  157. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  158. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  159. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  160. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  161. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  162. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  163. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  164. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  165. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  166. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  167. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  168. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  169. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  170. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  171. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  172. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  173. data/lib/datadog/tracing/metadata/ext.rb +6 -0
  174. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  175. data/lib/datadog/tracing/remote.rb +5 -2
  176. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  177. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  178. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  179. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  180. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  181. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  182. data/lib/datadog/tracing/span.rb +9 -2
  183. data/lib/datadog/tracing/span_event.rb +41 -0
  184. data/lib/datadog/tracing/span_operation.rb +6 -2
  185. data/lib/datadog/tracing/trace_operation.rb +26 -2
  186. data/lib/datadog/tracing/tracer.rb +14 -12
  187. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  188. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  190. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  191. data/lib/datadog/tracing/workers.rb +1 -1
  192. data/lib/datadog/version.rb +1 -1
  193. metadata +46 -11
  194. data/lib/datadog/profiling/crashtracker.rb +0 -91
  195. data/lib/datadog/profiling/ext/forking.rb +0 -98
  196. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../utils'
4
+ require_relative '../environment/socket'
5
+ require_relative '../environment/identity'
6
+ require_relative '../environment/git'
7
+
8
+ module Datadog
9
+ module Core
10
+ module Crashtracking
11
+ # This module builds a hash of tags
12
+ module TagBuilder
13
+ def self.call(settings)
14
+ hash = {
15
+ 'host' => Environment::Socket.hostname,
16
+ 'process_id' => Process.pid.to_s,
17
+ 'runtime_engine' => Environment::Identity.lang_engine,
18
+ 'runtime-id' => Environment::Identity.id,
19
+ 'runtime_platform' => Environment::Identity.lang_platform,
20
+ 'runtime_version' => Environment::Identity.lang_version,
21
+ 'env' => settings.env,
22
+ 'service' => settings.service,
23
+ 'version' => settings.version,
24
+ 'git.repository_url' => Environment::Git.git_repository_url,
25
+ 'git.commit.sha' => Environment::Git.git_commit_sha,
26
+ 'is_crash' => 'true',
27
+ 'language' => 'ruby',
28
+ 'library_version' => Core::Environment::Identity.gem_datadog_version,
29
+ }.compact
30
+
31
+ # Make sure everything is an utf-8 string, to avoid encoding issues in downstream
32
+ settings.tags.merge(hash).each_with_object({}) do |(key, value), h|
33
+ h[Utils.utf8_encode(key)] = Utils.utf8_encode(value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -8,11 +8,17 @@ module Datadog
8
8
  module Core
9
9
  module Diagnostics
10
10
  # Base class for EnvironmentLoggers - should allow for easy reporting by users to Datadog support.
11
+ #
12
+ # The EnvironmentLogger should not pollute the logs in a development environment.
11
13
  module EnvironmentLogging
12
14
  def log_configuration!(prefix, data)
13
15
  logger.info("DATADOG CONFIGURATION - #{prefix} - #{data}")
14
16
  end
15
17
 
18
+ def log_debug!(prefix, data)
19
+ logger.debug("DATADOG CONFIGURATION - #{prefix} - #{data}")
20
+ end
21
+
16
22
  def log_error!(prefix, type, error)
17
23
  logger.warn("DATADOG ERROR - #{prefix} - #{type}: #{error}")
18
24
  end
@@ -27,21 +33,12 @@ module Datadog
27
33
  def log?
28
34
  startup_logs_enabled = Datadog.configuration.diagnostics.startup_logs.enabled
29
35
  if startup_logs_enabled.nil?
30
- !repl? && !rspec? # Suppress logs if we are running in a REPL or rspec
36
+ # Do not pollute the logs in a development environment.
37
+ !Datadog::Core::Environment::Execution.development?
31
38
  else
32
39
  startup_logs_enabled
33
40
  end
34
41
  end
35
-
36
- REPL_PROGRAM_NAMES = %w[irb pry].freeze
37
-
38
- def repl?
39
- REPL_PROGRAM_NAMES.include?($PROGRAM_NAME)
40
- end
41
-
42
- def rspec?
43
- $PROGRAM_NAME.end_with?('rspec')
44
- end
45
42
  end
46
43
 
47
44
  # Collects and logs Core diagnostic information
@@ -25,9 +25,9 @@ module Datadog
25
25
  # 2. Checking if `Net::HTTP` is referring to the original one
26
26
  # => ::Net::HTTP.equal?(::WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP)
27
27
  def webmock_enabled?
28
- defined?(::WebMock::HttpLibAdapters::NetHttpAdapter) &&
28
+ !!(defined?(::WebMock::HttpLibAdapters::NetHttpAdapter) &&
29
29
  defined?(::Net::HTTP) &&
30
- ::Net::HTTP.equal?(::WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP))
30
+ ::Net::HTTP.equal?(::WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP)))
31
31
  end
32
32
 
33
33
  private
@@ -68,7 +68,7 @@ module Datadog
68
68
 
69
69
  # Check if we are running from `bin/cucumber` or `cucumber/rake/task`.
70
70
  def cucumber?
71
- defined?(::Cucumber::Cli)
71
+ !!defined?(::Cucumber::Cli)
72
72
  end
73
73
 
74
74
  # If this is a Rails application, use different heuristics to detect
@@ -80,7 +80,7 @@ module Datadog
80
80
  # detecting its presence is enough to deduct if this is a development environment.
81
81
  #
82
82
  # @see https://github.com/rails/spring/blob/48b299348ace2188444489a0c216a6f3e9687281/README.md?plain=1#L204-L207
83
- defined?(::Spring) || rails_env_development?
83
+ !!defined?(::Spring) || rails_env_development?
84
84
  end
85
85
 
86
86
  RAILS_ENV_DEVELOPMENT = Set['development', 'test'].freeze
@@ -94,7 +94,7 @@ module Datadog
94
94
  # it's common to have a custom "staging" environment, and such environment normally want to run as close
95
95
  # to production as possible.
96
96
  def rails_env_development?
97
- defined?(::Rails.env) && RAILS_ENV_DEVELOPMENT.include?(::Rails.env)
97
+ !!defined?(::Rails.env) && RAILS_ENV_DEVELOPMENT.include?(::Rails.env)
98
98
  end
99
99
  end
100
100
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../utils/time'
4
4
  require_relative '../utils/only_once'
5
+ require_relative '../telemetry/logger'
5
6
  require_relative '../configuration/ext'
6
7
 
7
8
  require_relative 'ext'
@@ -100,6 +101,7 @@ module Datadog
100
101
  Datadog.logger.error(
101
102
  "Failed to send count stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
102
103
  )
104
+ Telemetry::Logger.report(e, description: 'Failed to send count stat')
103
105
  end
104
106
 
105
107
  def distribution(stat, value = nil, options = nil, &block)
@@ -113,6 +115,7 @@ module Datadog
113
115
  Datadog.logger.error(
114
116
  "Failed to send distribution stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
115
117
  )
118
+ Telemetry::Logger.report(e, description: 'Failed to send distribution stat')
116
119
  end
117
120
 
118
121
  def increment(stat, options = nil)
@@ -125,6 +128,7 @@ module Datadog
125
128
  Datadog.logger.error(
126
129
  "Failed to send increment stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
127
130
  )
131
+ Telemetry::Logger.report(e, description: 'Failed to send increment stat')
128
132
  end
129
133
 
130
134
  def gauge(stat, value = nil, options = nil, &block)
@@ -138,6 +142,7 @@ module Datadog
138
142
  Datadog.logger.error(
139
143
  "Failed to send gauge stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
140
144
  )
145
+ Telemetry::Logger.report(e, description: 'Failed to send gauge stat')
141
146
  end
142
147
 
143
148
  def time(stat, options = nil)
@@ -153,9 +158,11 @@ module Datadog
153
158
  distribution(stat, ((finished - start) * 1000), options)
154
159
  end
155
160
  rescue StandardError => e
161
+ # TODO: Likely to be redundant, since `distribution` handles its own errors.
156
162
  Datadog.logger.error(
157
163
  "Failed to send time stat. Cause: #{e.class.name} #{e.message} Source: #{Array(e.backtrace).first}"
158
164
  )
165
+ Telemetry::Logger.report(e, description: 'Failed to send time stat')
159
166
  end
160
167
  end
161
168
 
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utils/time'
4
+
5
+ module Datadog
6
+ module Core
7
+ # Checks for rate limiting on a resource.
8
+ class RateLimiter
9
+ # Checks if resource of specified size can be
10
+ # conforms with the current limit.
11
+ #
12
+ # Implementations of this method are not guaranteed
13
+ # to be side-effect free.
14
+ #
15
+ # @return [Boolean] whether a resource conforms with the current limit
16
+ def allow?(size = 1); end
17
+
18
+ # The effective rate limiting ratio based on
19
+ # recent calls to `allow?`.
20
+ #
21
+ # @return [Float] recent allowance ratio
22
+ def effective_rate; end
23
+ end
24
+
25
+ # Implementation of the Token Bucket metering algorithm
26
+ # for rate limiting.
27
+ #
28
+ # @see https://en.wikipedia.org/wiki/Token_bucket Token bucket
29
+ class TokenBucket < RateLimiter
30
+ attr_reader :rate, :max_tokens
31
+
32
+ # @param rate [Numeric] Allowance rate, in units per second
33
+ # if rate is negative, always allow
34
+ # if rate is zero, never allow
35
+ # @param max_tokens [Numeric] Limit of available tokens
36
+ def initialize(rate, max_tokens = rate)
37
+ super()
38
+
39
+ raise ArgumentError, "rate must be a number: #{rate}" unless rate.is_a?(Numeric)
40
+ raise ArgumentError, "max_tokens must be a number: #{max_tokens}" unless max_tokens.is_a?(Numeric)
41
+
42
+ @rate = rate
43
+ @max_tokens = max_tokens
44
+
45
+ @tokens = max_tokens
46
+ @total_messages = 0
47
+ @conforming_messages = 0
48
+ @prev_conforming_messages = nil
49
+ @prev_total_messages = nil
50
+ @current_window = nil
51
+
52
+ @last_refill = Core::Utils::Time.get_time
53
+ end
54
+
55
+ # Checks if a message of provided +size+
56
+ # conforms with the current bucket limit.
57
+ #
58
+ # If it does, return +true+ and remove +size+
59
+ # tokens from the bucket.
60
+ # If it does not, return +false+ without affecting
61
+ # the tokens from the bucket.
62
+ #
63
+ # @return [Boolean] +true+ if message conforms with current bucket limit
64
+ def allow?(size = 1)
65
+ allowed = should_allow?(size)
66
+ update_rate_counts(allowed)
67
+ allowed
68
+ end
69
+
70
+ # Ratio of 'conformance' per 'total messages' checked
71
+ # averaged for the past 2 buckets
72
+ #
73
+ # Returns +1.0+ when no messages have been checked yet.
74
+ #
75
+ # @return [Float] Conformance ratio, between +[0,1]+
76
+ def effective_rate
77
+ return 0.0 if @rate.zero?
78
+ return 1.0 if @rate < 0 || @total_messages.zero?
79
+
80
+ return current_window_rate if @prev_conforming_messages.nil? || @prev_total_messages.nil?
81
+
82
+ (@conforming_messages.to_f + @prev_conforming_messages.to_f) / (@total_messages + @prev_total_messages)
83
+ end
84
+
85
+ # Ratio of 'conformance' per 'total messages' checked
86
+ # on this bucket
87
+ #
88
+ # Returns +1.0+ when no messages have been checked yet.
89
+ #
90
+ # @return [Float] Conformance ratio, between +[0,1]+
91
+ def current_window_rate
92
+ return 1.0 if @total_messages.zero?
93
+
94
+ @conforming_messages.to_f / @total_messages
95
+ end
96
+
97
+ # @return [Numeric] number of tokens currently available
98
+ def available_tokens
99
+ @tokens
100
+ end
101
+
102
+ private
103
+
104
+ def refill_since_last_message
105
+ now = Core::Utils::Time.get_time
106
+ elapsed = now - @last_refill
107
+
108
+ # Update the number of available tokens, but ensure we do not exceed the max
109
+ # we return the min of tokens + rate*elapsed, or max tokens
110
+ refill_tokens(@rate * elapsed)
111
+
112
+ @last_refill = now
113
+ end
114
+
115
+ def refill_tokens(size)
116
+ @tokens += size
117
+ @tokens = @max_tokens if @tokens > @max_tokens
118
+ end
119
+
120
+ def increment_total_count
121
+ @total_messages += 1
122
+ end
123
+
124
+ def increment_conforming_count
125
+ @conforming_messages += 1
126
+ end
127
+
128
+ def should_allow?(size = 1)
129
+ # rate limit of 0 blocks everything
130
+ return false if @rate.zero?
131
+
132
+ # negative rate limit disables rate limiting
133
+ return true if @rate < 0
134
+
135
+ refill_since_last_message
136
+
137
+ # if tokens < 1 we don't allow?
138
+ return false if @tokens < size
139
+
140
+ @tokens -= size
141
+
142
+ true
143
+ end
144
+
145
+ # Sets and Updates the past two 1 second windows for which
146
+ # the rate limiter must compute it's rate over and updates
147
+ # the total count, and conforming message count if +allowed+
148
+ def update_rate_counts(allowed)
149
+ now = Core::Utils::Time.get_time
150
+
151
+ # No tokens have been seen yet, start a new window
152
+ if @current_window.nil?
153
+ @current_window = now
154
+ # If more than 1 second has past since last window, reset
155
+ elsif now - @current_window >= 1
156
+ @prev_conforming_messages = @conforming_messages
157
+ @prev_total_messages = @total_messages
158
+ @conforming_messages = 0
159
+ @total_messages = 0
160
+ @current_window = now
161
+ end
162
+
163
+ increment_conforming_count if allowed
164
+
165
+ increment_total_count
166
+ end
167
+ end
168
+
169
+ # {Datadog::Core::RateLimiter} that accepts all resources,
170
+ # with no limits.
171
+ class UnlimitedLimiter < RateLimiter
172
+ # @return [Boolean] always +true+
173
+ def allow?(_ = 1)
174
+ true
175
+ end
176
+
177
+ # @return [Float] always 100%
178
+ def effective_rate
179
+ 1.0
180
+ end
181
+ end
182
+ end
183
+ end
@@ -12,10 +12,11 @@ module Datadog
12
12
  class Capabilities
13
13
  attr_reader :products, :capabilities, :receivers, :base64_capabilities
14
14
 
15
- def initialize(settings)
15
+ def initialize(settings, telemetry)
16
16
  @capabilities = []
17
17
  @products = []
18
18
  @receivers = []
19
+ @telemetry = telemetry
19
20
 
20
21
  register(settings)
21
22
 
@@ -28,12 +29,12 @@ module Datadog
28
29
  if settings.respond_to?(:appsec) && settings.appsec.enabled
29
30
  register_capabilities(Datadog::AppSec::Remote.capabilities)
30
31
  register_products(Datadog::AppSec::Remote.products)
31
- register_receivers(Datadog::AppSec::Remote.receivers)
32
+ register_receivers(Datadog::AppSec::Remote.receivers(@telemetry))
32
33
  end
33
34
 
34
35
  register_capabilities(Datadog::Tracing::Remote.capabilities)
35
36
  register_products(Datadog::Tracing::Remote.products)
36
- register_receivers(Datadog::Tracing::Remote.receivers)
37
+ register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
37
38
  end
38
39
 
39
40
  def register_capabilities(capabilities)
@@ -39,6 +39,7 @@ module Datadog
39
39
  @client.sync
40
40
  @healthy ||= true
41
41
  rescue Client::SyncError => e
42
+ # Transient errors due to network or agent. Logged the error but not via telemetry
42
43
  Datadog.logger.error do
43
44
  "remote worker client sync error: #{e.message} location: #{Array(e.backtrace).first}. skipping sync"
44
45
  end
@@ -48,6 +49,7 @@ module Datadog
48
49
  # negotiation object stores error logging state that should be reset.
49
50
  negotiation = Negotiation.new(settings, agent_settings)
50
51
 
52
+ # Transient errors due to network or agent. Logged the error but not via telemetry
51
53
  Datadog.logger.error do
52
54
  "remote worker error: #{e.class.name} #{e.message} location: #{Array(e.backtrace).first}. "\
53
55
  'reseting client state'
@@ -150,10 +152,10 @@ module Datadog
150
152
  #
151
153
  # Those checks are instead performed inside the worker loop.
152
154
  # This allows users to upgrade their agent while keeping their application running.
153
- def build(settings, agent_settings)
155
+ def build(settings, agent_settings, telemetry:)
154
156
  return unless settings.remote.enabled
155
157
 
156
- new(settings, Client::Capabilities.new(settings), agent_settings)
158
+ new(settings, Client::Capabilities.new(settings, telemetry), agent_settings)
157
159
  end
158
160
  end
159
161
  end
@@ -20,7 +20,7 @@ module Datadog
20
20
 
21
21
  if res.internal_error? && network_error?(res.error)
22
22
  unless @logged[:agent_unreachable]
23
- Datadog.logger.error { "agent unreachable: cannot negotiate #{path}" }
23
+ Datadog.logger.warn { "agent unreachable: cannot negotiate #{path}" }
24
24
  @logged[:agent_unreachable] = true
25
25
  end
26
26
 
@@ -29,7 +29,7 @@ module Datadog
29
29
 
30
30
  if res.not_found?
31
31
  unless @logged[:no_info_endpoint]
32
- Datadog.logger.error { "agent reachable but has no /info endpoint: cannot negotiate #{path}" }
32
+ Datadog.logger.warn { "agent reachable but has no /info endpoint: cannot negotiate #{path}" }
33
33
  @logged[:no_info_endpoint] = true
34
34
  end
35
35
 
@@ -38,7 +38,7 @@ module Datadog
38
38
 
39
39
  unless res.ok?
40
40
  unless @logged[:unexpected_response]
41
- Datadog.logger.error { "agent reachable but unexpected response: cannot negotiate #{path}" }
41
+ Datadog.logger.warn { "agent reachable but unexpected response: cannot negotiate #{path}" }
42
42
  @logged[:unexpected_response] = true
43
43
  end
44
44
 
@@ -47,7 +47,7 @@ module Datadog
47
47
 
48
48
  unless res.endpoints.include?(path)
49
49
  unless @logged[:no_config_endpoint]
50
- Datadog.logger.error { "agent reachable but does not report #{path}" }
50
+ Datadog.logger.warn { "agent reachable but does not report #{path}" }
51
51
  @logged[:no_config_endpoint] = true
52
52
  end
53
53
 
@@ -19,6 +19,8 @@ module Datadog
19
19
  barrier = Datadog::Core::Remote.active_remote.barrier(:once)
20
20
  end
21
21
 
22
+ # steep does not permit the next line due to
23
+ # https://github.com/soutaro/steep/issues/1231
22
24
  Boot.new(barrier, t)
23
25
  end
24
26
  end
@@ -94,7 +94,7 @@ module Datadog
94
94
  def try_flush
95
95
  yield
96
96
  rescue StandardError => e
97
- Datadog.logger.error("Error while sending runtime metric. Cause: #{e.class.name} #{e.message}")
97
+ Datadog.logger.warn("Error while sending runtime metric. Cause: #{e.class.name} #{e.message}")
98
98
  end
99
99
 
100
100
  def default_metric_options
@@ -2,8 +2,12 @@
2
2
 
3
3
  require_relative 'emitter'
4
4
  require_relative 'event'
5
+ require_relative 'http/transport'
5
6
  require_relative 'metrics_manager'
6
7
  require_relative 'worker'
8
+ require_relative 'logging'
9
+
10
+ require_relative '../configuration/ext'
7
11
  require_relative '../utils/forking'
8
12
 
9
13
  module Datadog
@@ -14,6 +18,42 @@ module Datadog
14
18
  attr_reader :enabled
15
19
 
16
20
  include Core::Utils::Forking
21
+ include Telemetry::Logging
22
+
23
+ def self.build(settings, agent_settings, logger)
24
+ enabled = settings.telemetry.enabled
25
+ agentless_enabled = settings.telemetry.agentless_enabled
26
+
27
+ if !agentless_enabled && agent_settings.adapter != Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
28
+ enabled = false
29
+ logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
30
+ end
31
+
32
+ if agentless_enabled && settings.api_key.nil?
33
+ enabled = false
34
+ logger.debug { 'Telemetry disabled. Agentless telemetry requires an DD_API_KEY variable to be set.' }
35
+ end
36
+
37
+ transport = if agentless_enabled
38
+ Datadog::Core::Telemetry::Http::Transport.build_agentless_transport(
39
+ api_key: settings.api_key,
40
+ dd_site: settings.site,
41
+ url_override: settings.telemetry.agentless_url_override
42
+ )
43
+ else
44
+ Datadog::Core::Telemetry::Http::Transport.build_agent_transport(agent_settings)
45
+ end
46
+
47
+ Telemetry::Component.new(
48
+ http_transport: transport,
49
+ enabled: enabled,
50
+ metrics_enabled: enabled && settings.telemetry.metrics_enabled,
51
+ heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
52
+ metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
53
+ dependency_collection: settings.telemetry.dependency_collection,
54
+ shutdown_timeout_seconds: settings.telemetry.shutdown_timeout_seconds,
55
+ )
56
+ end
17
57
 
18
58
  # @param enabled [Boolean] Determines whether telemetry events should be sent to the API
19
59
  # @param metrics_enabled [Boolean] Determines whether telemetry metrics should be sent to the API
@@ -24,6 +64,8 @@ module Datadog
24
64
  heartbeat_interval_seconds:,
25
65
  metrics_aggregation_interval_seconds:,
26
66
  dependency_collection:,
67
+ http_transport:,
68
+ shutdown_timeout_seconds:,
27
69
  enabled: true,
28
70
  metrics_enabled: true
29
71
  )
@@ -39,9 +81,10 @@ module Datadog
39
81
  enabled: @enabled,
40
82
  heartbeat_interval_seconds: heartbeat_interval_seconds,
41
83
  metrics_aggregation_interval_seconds: metrics_aggregation_interval_seconds,
42
- emitter: Emitter.new,
84
+ emitter: Emitter.new(http_transport: http_transport),
43
85
  metrics_manager: @metrics_manager,
44
- dependency_collection: dependency_collection
86
+ dependency_collection: dependency_collection,
87
+ shutdown_timeout: shutdown_timeout_seconds
45
88
  )
46
89
  @worker.start
47
90
  end
@@ -70,6 +113,12 @@ module Datadog
70
113
  @worker.enqueue(Event::AppIntegrationsChange.new)
71
114
  end
72
115
 
116
+ def log!(event)
117
+ return unless @enabled || forked?
118
+
119
+ @worker.enqueue(event)
120
+ end
121
+
73
122
  # Report configuration changes caused by Remote Configuration.
74
123
  def client_configuration_change!(changes)
75
124
  return if !@enabled || forked?
@@ -16,22 +16,20 @@ module Datadog
16
16
 
17
17
  # @param http_transport [Datadog::Core::Telemetry::Http::Transport] Transport object that can be used to send
18
18
  # telemetry requests via the agent
19
- def initialize(http_transport: Datadog::Core::Telemetry::Http::Transport.new)
19
+ def initialize(http_transport:)
20
20
  @http_transport = http_transport
21
21
  end
22
22
 
23
23
  # Retrieves and emits a TelemetryRequest object based on the request type specified
24
24
  def request(event)
25
- begin
26
- seq_id = self.class.sequence.next
27
- payload = Request.build_payload(event, seq_id)
28
- res = @http_transport.request(request_type: event.type, payload: payload.to_json)
29
- Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (code: #{res.code.inspect})" }
30
- res
31
- rescue => e
32
- Datadog.logger.debug("Unable to send telemetry request for event `#{event.type rescue 'unknown'}`: #{e}")
33
- Telemetry::Http::InternalErrorResponse.new(e)
34
- end
25
+ seq_id = self.class.sequence.next
26
+ payload = Request.build_payload(event, seq_id)
27
+ res = @http_transport.request(request_type: event.type, payload: payload.to_json)
28
+ Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (code: #{res.code.inspect})" }
29
+ res
30
+ rescue => e
31
+ Datadog.logger.debug("Unable to send telemetry request for event `#{event.type rescue 'unknown'}`: #{e}")
32
+ Telemetry::Http::InternalErrorResponse.new(e)
35
33
  end
36
34
 
37
35
  # Initializes a Sequence object to track seq_id if not already initialized; else returns stored
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../utils/forking'
4
+ require_relative '../utils/sequence'
5
+
3
6
  module Datadog
4
7
  module Core
5
8
  module Telemetry
@@ -18,7 +21,9 @@ module Datadog
18
21
  # The type of the event.
19
22
  # It must be one of the stings defined in the Telemetry V2
20
23
  # specification for event names.
21
- def type; end
24
+ def type
25
+ raise NotImplementedError, 'Must be implemented by subclass'
26
+ end
22
27
 
23
28
  # The JSON payload for the event.
24
29
  def payload
@@ -332,6 +337,37 @@ module Datadog
332
337
  end
333
338
  end
334
339
 
340
+ # Telemetry class for the 'logs' event
341
+ class Log < Base
342
+ LEVELS = {
343
+ error: 'ERROR',
344
+ warn: 'WARN',
345
+ }.freeze
346
+
347
+ def type
348
+ 'logs'
349
+ end
350
+
351
+ def initialize(message:, level:, stack_trace: nil)
352
+ super()
353
+ @message = message
354
+ @stack_trace = stack_trace
355
+ @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
356
+ end
357
+
358
+ def payload
359
+ {
360
+ logs: [
361
+ {
362
+ message: @message,
363
+ level: @level,
364
+ stack_trace: @stack_trace,
365
+ }.compact
366
+ ]
367
+ }
368
+ end
369
+ end
370
+
335
371
  # Telemetry class for the 'distributions' event
336
372
  class Distributions < GenerateMetrics
337
373
  def type
@@ -12,6 +12,7 @@ module Datadog
12
12
  ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID'
13
13
  ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE'
14
14
  ENV_INSTALL_TIME = 'DD_INSTRUMENTATION_INSTALL_TIME'
15
+ ENV_AGENTLESS_URL_OVERRIDE = 'DD_TELEMETRY_AGENTLESS_URL'
15
16
  end
16
17
  end
17
18
  end