datadog 2.39.0 → 2.40.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/ext/datadog_profiling_native_extension/collectors_stack.c +218 -147
  4. data/ext/datadog_profiling_native_extension/collectors_stack.h +16 -2
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +12 -7
  6. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  7. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +19 -11
  8. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
  9. data/ext/datadog_profiling_native_extension/extconf.rb +12 -7
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +259 -65
  11. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +16 -10
  12. data/ext/libdatadog_api/datadog_ruby_common.c +19 -11
  13. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  14. data/ext/libdatadog_api/init.c +4 -0
  15. data/ext/libdatadog_api/trace_exporter.c +974 -0
  16. data/ext/libdatadog_api/trace_exporter.h +5 -0
  17. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  18. data/lib/datadog/ai_guard/ext.rb +2 -0
  19. data/lib/datadog/appsec/configuration.rb +9 -0
  20. data/lib/datadog/appsec/contrib/devise/patcher.rb +1 -1
  21. data/lib/datadog/appsec/contrib/rack/patcher.rb +1 -1
  22. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +6 -3
  23. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  24. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  25. data/lib/datadog/core/configuration/settings.rb +33 -41
  26. data/lib/datadog/core/configuration/supported_configurations.rb +3 -0
  27. data/lib/datadog/core/environment/gc.rb +1 -1
  28. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  29. data/lib/datadog/core/environment/yjit.rb +2 -2
  30. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  31. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +80 -12
  32. data/lib/datadog/core/utils/sequence.rb +3 -6
  33. data/lib/datadog/core/utils/time.rb +16 -54
  34. data/lib/datadog/di/el/compiler.rb +65 -12
  35. data/lib/datadog/di/el/evaluator.rb +82 -2
  36. data/lib/datadog/di/el/expression.rb +10 -2
  37. data/lib/datadog/di/probe_builder.rb +6 -6
  38. data/lib/datadog/profiling/collectors/thread_context.rb +6 -13
  39. data/lib/datadog/profiling/component.rb +1 -0
  40. data/lib/datadog/profiling.rb +4 -0
  41. data/lib/datadog/tracing/component.rb +21 -0
  42. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  43. data/lib/datadog/tracing/configuration/settings.rb +16 -0
  44. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +2 -1
  45. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -0
  46. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +7 -2
  47. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +7 -2
  48. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -5
  49. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  50. data/lib/datadog/tracing/sync_writer.rb +8 -3
  51. data/lib/datadog/tracing/tracer.rb +8 -0
  52. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  53. data/lib/datadog/tracing/transport/native.rb +363 -0
  54. data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
  55. data/lib/datadog/tracing/writer.rb +16 -3
  56. data/lib/datadog/version.rb +1 -1
  57. metadata +9 -5
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include "datadog_ruby_common.h"
4
+
5
+ void trace_exporter_init(VALUE tracing_module);
@@ -15,6 +15,7 @@ module Datadog
15
15
  Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
16
16
  Tracing::Sampling::Ext::Decision::AI_GUARD
17
17
  )
18
+ trace.set_distributed_source(Ext::PRODUCT_BIT)
18
19
  trace.set_tag(Ext::EVENT_TAG, true)
19
20
  trace.set_tag(Ext::TRACE_EXECUTED_TAG, "1")
20
21
 
@@ -4,6 +4,8 @@ module Datadog
4
4
  module AIGuard
5
5
  # AI Guard specific constants
6
6
  module Ext
7
+ PRODUCT_BIT = 0x20
8
+
7
9
  SPAN_NAME = "ai_guard"
8
10
  TARGET_TAG = "ai_guard.target"
9
11
  TOOL_NAME_TAG = "ai_guard.tool_name"
@@ -52,6 +52,15 @@ module Datadog
52
52
  o.default false
53
53
  end
54
54
 
55
+ # NOTE: Marker set by the agentic onboarding tooling.
56
+ # Has no effect here — we just report its value in telemetry,
57
+ # so onboarding can be detected at runtime
58
+ option :agentic_onboarding do |o|
59
+ o.type :string
60
+ o.env "DD_APPSEC_AGENTIC_ONBOARDING"
61
+ o.default ""
62
+ end
63
+
55
64
  define_method(:instrument) do |integration_name|
56
65
  if enabled
57
66
  registered_integration = Datadog::AppSec::Contrib::Integration.registry[integration_name]
@@ -20,7 +20,7 @@ module Datadog
20
20
  module_function
21
21
 
22
22
  def patched?
23
- Patcher.instance_variable_get(:@patched)
23
+ !!Patcher.instance_variable_get(:@patched)
24
24
  end
25
25
 
26
26
  def target_version
@@ -12,7 +12,7 @@ module Datadog
12
12
  module_function
13
13
 
14
14
  def patched?
15
- Patcher.instance_variable_get(:@patched) # TODO: Patcher.flag_patched
15
+ !!Patcher.instance_variable_get(:@patched) # TODO: Patcher.flag_patched
16
16
  end
17
17
 
18
18
  def target_version
@@ -12,7 +12,8 @@ module Datadog
12
12
  # This should be inserted just below Rack::JSONBodyParser or
13
13
  # legacy Rack::PostBodyContentTypeParser from rack-contrib
14
14
  class RequestBodyMiddleware
15
- def initialize(app, opt = {})
15
+ # TODO: opt is never used, it can probably be safely removed
16
+ def initialize(app, opt = {}) # steep:ignore DifferentMethodParameterKind
16
17
  @app = app
17
18
  end
18
19
 
@@ -23,7 +24,7 @@ module Datadog
23
24
 
24
25
  # TODO: handle exceptions, except for @app.call
25
26
 
26
- http_response = nil
27
+ http_response = nil #: Rack::response?
27
28
  interrupt_params = catch(::Datadog::AppSec::Ext::INTERRUPT) do
28
29
  http_response, _request = Instrumentation.gateway.push("rack.request.body", Gateway::Request.new(env)) do
29
30
  @app.call(env)
@@ -34,7 +35,9 @@ module Datadog
34
35
 
35
36
  return AppSec::Response.from_interrupt_params(interrupt_params, env["HTTP_ACCEPT"]).to_rack if interrupt_params
36
37
 
37
- http_response
38
+ # Steep can't see that the catch block always populates http_response
39
+ # when no interrupt is thrown.
40
+ http_response #: Rack::response
38
41
  end
39
42
  end
40
43
  end
@@ -6,7 +6,8 @@ module Datadog
6
6
  module Rails
7
7
  # Rack middleware for AppSec on Rails
8
8
  class RequestMiddleware
9
- def initialize(app, opt = {})
9
+ # TODO: opt is never used, it can probably be safely removed
10
+ def initialize(app, opt = {}) # steep:ignore DifferentMethodParameterKind
10
11
  @app = app
11
12
  end
12
13
 
@@ -6,7 +6,8 @@ module Datadog
6
6
  module Sinatra
7
7
  # Rack middleware for AppSec on Sinatra
8
8
  class RequestMiddleware
9
- def initialize(app, opt = {})
9
+ # TODO: opt is never used, it can probably be safely removed
10
+ def initialize(app, opt = {}) # steep:ignore DifferentMethodParameterKind
10
11
  @app = app
11
12
  end
12
13
 
@@ -644,6 +644,13 @@ module Datadog
644
644
  o.env "DD_PROFILING_EXPERIMENTAL_USE_SYSTEM_DNS"
645
645
  o.default true
646
646
  end
647
+
648
+ # Whether the profiler samples include the class/module name in stack frames, like `Foo::Bar#baz`, or not (`baz`)
649
+ option :experimental_show_classes_enabled do |o|
650
+ o.type :bool
651
+ o.env "DD_PROFILING_EXPERIMENTAL_SHOW_CLASSES_ENABLED"
652
+ o.default false
653
+ end
647
654
  end
648
655
 
649
656
  # @public_api
@@ -792,55 +799,40 @@ module Datadog
792
799
  end
793
800
  end
794
801
 
795
- # The time provider used by Datadog. It must respect the interface of [Time](https://ruby-doc.org/core-3.0.1/Time.html).
796
- #
797
- # When testing, it can be helpful to use a different time provider.
798
- #
799
- # For [Timecop](https://rubygems.org/gems/timecop), for example, `->{ Time.now_without_mock_time }`
800
- # allows Datadog features to use the real wall time when time is frozen.
801
- #
802
- # @default `->{ Time.now }`
803
- # @return [Proc<Time>]
802
+ # Deprecated and no longer does anything.
803
+ # This gem already takes care to not be affected by timecop.
804
+ # DEV-3.0: Remove `time_now_provider` option
804
805
  option :time_now_provider do |o|
805
- o.default_proc { ::Time.now }
806
+ o.default_proc { Datadog::Core::Utils::Time.now }
806
807
  o.type :proc
807
-
808
- o.after_set do |time_provider|
809
- Core::Utils::Time.now_provider = time_provider
810
- end
811
-
812
- o.resetter do |_value|
813
- # TODO: Resetter needs access to the default value
814
- # TODO: to help reduce duplication.
815
- -> { ::Time.now }.tap do |default|
816
- Core::Utils::Time.now_provider = default
808
+ o.after_set do |_, _, precedence|
809
+ unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
810
+ unless defined?(Datadog::CI) # the datadog-ci gem needs to work with any datadog gem version and not warn
811
+ Core.log_deprecation(key: :time_now_provider) do
812
+ "The time_now_provider setting has been deprecated for removal " \
813
+ "and no longer does anything. Please remove it from your Datadog.configure block. " \
814
+ "The datadog gem always uses the real non-mocked time, even when the timecop gem monkey-patches Time."
815
+ end
816
+ end
817
817
  end
818
818
  end
819
819
  end
820
820
 
821
- # The monotonic clock time provider used by Datadog. This option is internal and is used by `datadog-ci`
822
- # gem to avoid traces' durations being skewed by timecop.
823
- #
824
- # It must respect the interface of [Datadog::Core::Utils::Time#get_time] method.
825
- #
826
- # For [Timecop](https://rubygems.org/gems/timecop), for example,
827
- # `->(unit = :float_second) { ::Process.clock_gettime_without_mock(Datadog::Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
828
- # allows Datadog features to use the real monotonic time when time is frozen with
829
- # `Timecop.mock_process_clock = true`.
830
- #
831
- # @default `->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
832
- # @return [Proc<Numeric>]
821
+ # Deprecated and no longer does anything.
822
+ # This gem already takes care to not be affected by timecop.
823
+ # DEV-3.0: Remove `get_time_provider` option
833
824
  option :get_time_provider do |o|
834
- o.default_proc { |unit = :float_second| ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }
825
+ o.default_proc { |unit = :float_second| Datadog::Core::Utils::Time.get_time(unit) }
835
826
  o.type :proc
836
-
837
- o.after_set do |get_time_provider|
838
- Core::Utils::Time.get_time_provider = get_time_provider
839
- end
840
-
841
- o.resetter do |_value|
842
- ->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }.tap do |default|
843
- Core::Utils::Time.get_time_provider = default
827
+ o.after_set do |_, _, precedence|
828
+ unless precedence == Datadog::Core::Configuration::Option::Precedence::DEFAULT
829
+ unless defined?(Datadog::CI) # the datadog-ci gem needs to work with any datadog gem version and not warn
830
+ Core.log_deprecation(key: :get_time_provider) do
831
+ "The get_time_provider setting has been deprecated for removal " \
832
+ "and no longer does anything. Please remove it from your Datadog.configure block. " \
833
+ "The datadog gem always uses the real non-mocked time, even when the timecop gem monkey-patches Process.clock_gettime."
834
+ end
835
+ end
844
836
  end
845
837
  end
846
838
  end
@@ -24,6 +24,7 @@ module Datadog
24
24
  "DD_API_SECURITY_REQUEST_SAMPLE_RATE",
25
25
  "DD_API_SECURITY_SAMPLE_DELAY",
26
26
  "DD_APM_TRACING_ENABLED",
27
+ "DD_APPSEC_AGENTIC_ONBOARDING",
27
28
  "DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING",
28
29
  "DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE",
29
30
  "DD_APPSEC_BODY_PARSING_SIZE_LIMIT",
@@ -60,6 +61,7 @@ module Datadog
60
61
  "DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE",
61
62
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
62
63
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED",
64
+ "DD_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED",
63
65
  "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
64
66
  "DD_EXTERNAL_ENV",
65
67
  "DD_FLAGGING_EVALUATION_COUNTS_ENABLED",
@@ -85,6 +87,7 @@ module Datadog
85
87
  "DD_PROFILING_EXPERIMENTAL_HEAP_ENABLED",
86
88
  "DD_PROFILING_EXPERIMENTAL_HEAP_SAMPLE_RATE",
87
89
  "DD_PROFILING_EXPERIMENTAL_HEAP_SIZE_ENABLED",
90
+ "DD_PROFILING_EXPERIMENTAL_SHOW_CLASSES_ENABLED",
88
91
  "DD_PROFILING_EXPERIMENTAL_USE_SYSTEM_DNS",
89
92
  "DD_PROFILING_GC_ENABLED",
90
93
  "DD_PROFILING_GVL_ENABLED",
@@ -12,7 +12,7 @@ module Datadog
12
12
  end
13
13
 
14
14
  def available?
15
- defined?(::GC) && ::GC.respond_to?(:stat)
15
+ !!(defined?(::GC) && ::GC.respond_to?(:stat))
16
16
  end
17
17
  end
18
18
  end
@@ -56,7 +56,7 @@ module Datadog
56
56
  end
57
57
 
58
58
  def available?
59
- defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
59
+ !!(defined?(::RubyVM) && ::RubyVM.respond_to?(:stat))
60
60
  end
61
61
  end
62
62
  end
@@ -59,9 +59,9 @@ module Datadog
59
59
  end
60
60
 
61
61
  def available?
62
- defined?(::RubyVM::YJIT) \
62
+ !!(defined?(::RubyVM::YJIT) \
63
63
  && ::RubyVM::YJIT.enabled? \
64
- && ::RubyVM::YJIT.respond_to?(:runtime_stats)
64
+ && ::RubyVM::YJIT.respond_to?(:runtime_stats))
65
65
  end
66
66
  end
67
67
  end
@@ -57,7 +57,7 @@ module Datadog
57
57
  end
58
58
 
59
59
  def reset?
60
- defined?(@reset) && !!@reset
60
+ !!(defined?(@reset) && @reset)
61
61
  end
62
62
  end
63
63
  end
@@ -6,6 +6,12 @@ module Datadog
6
6
  # Monkey patches `Kernel#fork` and similar functions, adding an `at_fork` callback mechanism which
7
7
  # is used to restart observability after the VM forks (e.g. in multiprocess Ruby apps).
8
8
  module AtForkMonkeyPatch
9
+ AT_FORK_BEFORE_BLOCKS = [] # rubocop:disable Style/MutableConstant -- Used to store blocks to run, mutable by design.
10
+ private_constant :AT_FORK_BEFORE_BLOCKS
11
+
12
+ AT_FORK_PARENT_BLOCKS = [] # rubocop:disable Style/MutableConstant -- Used to store blocks to run, mutable by design.
13
+ private_constant :AT_FORK_PARENT_BLOCKS
14
+
9
15
  AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant -- Used to store blocks to run, mutable by design.
10
16
  private_constant :AT_FORK_CHILD_BLOCKS
11
17
 
@@ -32,19 +38,41 @@ module Datadog
32
38
  end
33
39
 
34
40
  def self.run_at_fork_blocks(stage)
35
- raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
36
-
37
- AT_FORK_CHILD_BLOCKS.each(&:call)
41
+ blocks_for(stage).each(&:call)
38
42
  end
39
43
 
44
+ # Registers a block to run at the given fork +stage+ (+:before+,
45
+ # +:parent+, or +:child+).
46
+ #
47
+ # Returns the registered block so callers can keep a handle to it and
48
+ # later deregister it via {.remove_at_fork}.
40
49
  def self.at_fork(stage, &block)
41
- raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
42
50
  raise(ArgumentError, "Missing block argument") unless block
43
51
 
44
- AT_FORK_CHILD_BLOCKS << block
52
+ blocks_for(stage) << block
45
53
 
46
- true
54
+ block
55
+ end
56
+
57
+ # Deregisters a block previously registered with {.at_fork} for the given
58
+ # +stage+. It is a no-op (does not raise) when +block+ was never
59
+ # registered (or was already removed). Raises +ArgumentError+ for an
60
+ # unknown stage, matching the {.at_fork} contract.
61
+ def self.remove_at_fork(stage, block)
62
+ blocks_for(stage).delete(block)
63
+
64
+ nil
65
+ end
66
+
67
+ def self.blocks_for(stage)
68
+ case stage
69
+ when :before then AT_FORK_BEFORE_BLOCKS
70
+ when :parent then AT_FORK_PARENT_BLOCKS
71
+ when :child then AT_FORK_CHILD_BLOCKS
72
+ else raise(ArgumentError, "Unsupported stage #{stage}")
73
+ end
47
74
  end
75
+ private_class_method :blocks_for
48
76
 
49
77
  # Adds `at_fork` behavior; see parent module for details.
50
78
  module KernelMonkeyPatch
@@ -59,15 +87,29 @@ module Datadog
59
87
  end
60
88
  end
61
89
 
90
+ # Run pre-fork callbacks in the parent, just before forking.
91
+ AtForkMonkeyPatch.run_at_fork_blocks(:before)
92
+
62
93
  # Start fork
63
94
  # If a block is provided, use the wrapped version.
64
- result = child_block.nil? ? super : super(&child_block)
95
+ begin
96
+ result = child_block.nil? ? super : super(&child_block)
97
+ rescue Exception # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
98
+ # The fork failed and we are still in the parent; run `:parent` to
99
+ # restore any state the `:before` blocks set up, then re-raise.
100
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent)
101
+ raise
102
+ end
65
103
 
66
104
  # When fork gets called without a block, it returns twice:
67
105
  # If we're in the fork, result = nil: trigger child callbacks.
68
- # If we're in the parent, result = pid: we do nothing.
106
+ # If we're in the parent, result = pid: trigger parent callbacks.
69
107
  # (If it gets called with a block, it only returns on the parent)
70
- AtForkMonkeyPatch.run_at_fork_blocks(:child) if result.nil?
108
+ if result.nil?
109
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
110
+ else
111
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent)
112
+ end
71
113
 
72
114
  result
73
115
  end
@@ -78,9 +120,24 @@ module Datadog
78
120
  # Hook provided by Ruby 3.1+ for observability libraries that want to know about fork, see
79
121
  # https://github.com/ruby/ruby/pull/5017 and https://bugs.ruby-lang.org/issues/17795
80
122
  def _fork
81
- pid = super
123
+ AtForkMonkeyPatch.run_at_fork_blocks(:before)
124
+
125
+ begin
126
+ pid = super
127
+ rescue Exception # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
128
+ # The fork failed, so no child was created and we are still in the
129
+ # parent. The `:before` blocks already ran (and may hold resources,
130
+ # e.g. a locked mutex); run the `:parent` blocks so that state is
131
+ # restored, then re-raise.
132
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent)
133
+ raise
134
+ end
82
135
 
83
- AtForkMonkeyPatch.run_at_fork_blocks(:child) if pid == 0
136
+ if pid == 0
137
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
138
+ else
139
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent)
140
+ end
84
141
 
85
142
  pid
86
143
  end
@@ -89,8 +146,19 @@ module Datadog
89
146
  # keeps executing code in the child process, killing off the parent, thus effectively replacing it.
90
147
  # This is not covered by `_fork` and thus we have some extra code for it.
91
148
  def daemon(*args)
92
- result = super
149
+ AtForkMonkeyPatch.run_at_fork_blocks(:before)
150
+
151
+ begin
152
+ result = super
153
+ rescue Exception # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
154
+ # `daemon` failed, so the original process survives; run `:parent`
155
+ # to restore state the `:before` blocks set up, then re-raise.
156
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent)
157
+ raise
158
+ end
93
159
 
160
+ # `daemon` kills the parent, so there is no surviving parent to run
161
+ # `:parent` callbacks in; only the child continues executing.
94
162
  AtForkMonkeyPatch.run_at_fork_blocks(:child)
95
163
 
96
164
  result
@@ -5,18 +5,15 @@ module Datadog
5
5
  module Utils
6
6
  # Generates values from a consistent sequence
7
7
  class Sequence
8
- def initialize(seed = 0, &block)
8
+ def initialize(seed = 0)
9
9
  @seed = seed
10
10
  @current = seed
11
- @next_item = block
12
11
  end
13
12
 
14
13
  def next
15
- # Steep: https://github.com/soutaro/steep/issues/477
16
- # @type ivar @next_item: ^(::Integer) -> Integer
17
- next_item = @next_item ? @next_item.call(@current) : @current
14
+ current = @current
18
15
  @current += 1
19
- next_item
16
+ current
20
17
  end
21
18
 
22
19
  def reset!
@@ -5,7 +5,16 @@ module Datadog
5
5
  module Utils
6
6
  # Common database-related utility functions.
7
7
  module Time
8
- module_function
8
+ # timecop monkey-patches Time.now, Process.clock_gettime, etc:
9
+ # https://github.com/travisjeffery/timecop/blob/v0.9.11/lib/timecop/time_extensions.rb
10
+ # It keeps the original methods as aliases.
11
+ # We want the real not-mocked time here, so we use those aliases to the original method if present.
12
+ # If not present we assume these methods have not been monkey-patched and are the original.
13
+
14
+ original_clock_gettime_name = Process.respond_to?(:clock_gettime_without_mock) ? :clock_gettime_without_mock : :clock_gettime
15
+ define_singleton_method(:original_clock_gettime, &Process.method(original_clock_gettime_name))
16
+
17
+ MONOTONIC_CLOCK_ID = RUBY_PLATFORM.include?("darwin") ? Process::CLOCK_MONOTONIC_RAW : Process::CLOCK_MONOTONIC
9
18
 
10
19
  # Current monotonic time
11
20
  # On macOS, CLOCK_MONOTONIC only has microsecond precision,
@@ -13,71 +22,24 @@ module Datadog
13
22
  #
14
23
  # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
15
24
  # @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
16
- MONOTONIC_CLOCK_ID = RUBY_PLATFORM.include?("darwin") ? Process::CLOCK_MONOTONIC_RAW : Process::CLOCK_MONOTONIC
17
-
18
- def get_time(unit = :float_second)
19
- Process.clock_gettime(MONOTONIC_CLOCK_ID, unit)
25
+ def self.get_time(unit = :float_second)
26
+ original_clock_gettime(MONOTONIC_CLOCK_ID, unit)
20
27
  end
21
28
 
22
29
  # Current wall time.
23
30
  #
24
31
  # @return [Time] current time object
25
- def now
26
- ::Time.now
27
- end
28
-
29
- # Overrides the implementation of `#now
30
- # with the provided callable.
31
- #
32
- # Overriding the method `#now` instead of
33
- # indirectly calling `block` removes
34
- # one level of method call overhead.
35
- #
36
- # @param block [Proc] block that returns a `Time` object representing the current wall time
37
- def now_provider=(block)
38
- class << self
39
- # Avoid method redefinition warning.
40
- # `rescue nil` is added in case customers remove the method
41
- # themselves to squelch the warning.
42
- begin
43
- remove_method(:now)
44
- rescue
45
- nil
46
- end
47
- end
48
- define_singleton_method(:now, &block)
49
- end
50
-
51
- # Overrides the implementation of `#get_time
52
- # with the provided callable.
53
- #
54
- # Overriding the method `#get_time` instead of
55
- # indirectly calling `block` removes
56
- # one level of method call overhead.
57
- #
58
- # @param block [Proc] block that accepts unit and returns timestamp in the requested unit
59
- def get_time_provider=(block)
60
- class << self
61
- # Avoid method redefinition warning
62
- # `rescue nil` is added in case customers remove the method
63
- # themselves to squelch the warning.
64
- begin
65
- remove_method(:get_time)
66
- rescue
67
- nil
68
- end
69
- end
70
- define_singleton_method(:get_time, &block)
71
- end
32
+ original_time_now_name = ::Time.respond_to?(:now_without_mock_time) ? :now_without_mock_time : :now
33
+ define_singleton_method(:now, &::Time.method(original_time_now_name))
72
34
 
73
- def measure(unit = :float_second)
35
+ def self.measure(unit = :float_second)
74
36
  before = get_time(unit)
75
37
  yield
76
38
  after = get_time(unit)
77
39
  after - before
78
40
  end
79
41
 
80
- def as_utc_epoch_ns(time)
42
+ def self.as_utc_epoch_ns(time)
81
43
  # we use #to_r instead of #to_f because Float doesn't have enough precision to represent exact nanoseconds, see
82
44
  # https://rubyapi.org/3.0/o/time#method-i-to_f
83
45
  (time.to_r * 1_000_000_000).to_i