datadog 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +10 -22
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +148 -30
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +4 -2
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +89 -46
  8. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +580 -29
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +9 -1
  10. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +0 -27
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -4
  12. data/ext/datadog_profiling_native_extension/extconf.rb +38 -21
  13. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  14. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  15. data/ext/datadog_profiling_native_extension/heap_recorder.c +20 -6
  16. data/ext/datadog_profiling_native_extension/http_transport.c +38 -6
  17. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +52 -1
  18. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
  19. data/ext/datadog_profiling_native_extension/profiling.c +1 -1
  20. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  21. data/ext/libdatadog_api/crashtracker.c +20 -18
  22. data/ext/libdatadog_api/datadog_ruby_common.c +0 -27
  23. data/ext/libdatadog_api/datadog_ruby_common.h +0 -4
  24. data/ext/libdatadog_extconf_helpers.rb +1 -1
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  27. data/lib/datadog/appsec/component.rb +29 -8
  28. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  29. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  30. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  31. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  32. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +0 -14
  33. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +67 -31
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +18 -15
  35. data/lib/datadog/appsec/contrib/graphql/integration.rb +14 -1
  36. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +2 -5
  37. data/lib/datadog/appsec/event.rb +1 -1
  38. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  39. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  40. data/lib/datadog/appsec/processor.rb +36 -37
  41. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  42. data/lib/datadog/appsec/remote.rb +7 -3
  43. data/lib/datadog/appsec.rb +2 -2
  44. data/lib/datadog/core/configuration/components.rb +4 -3
  45. data/lib/datadog/core/configuration/settings.rb +84 -5
  46. data/lib/datadog/core/crashtracking/component.rb +1 -1
  47. data/lib/datadog/core/environment/execution.rb +5 -5
  48. data/lib/datadog/core/metrics/client.rb +7 -0
  49. data/lib/datadog/core/rate_limiter.rb +183 -0
  50. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  51. data/lib/datadog/core/remote/component.rb +4 -2
  52. data/lib/datadog/core/remote/negotiation.rb +4 -4
  53. data/lib/datadog/core/remote/tie.rb +2 -0
  54. data/lib/datadog/core/runtime/metrics.rb +1 -1
  55. data/lib/datadog/core/telemetry/component.rb +2 -0
  56. data/lib/datadog/core/telemetry/event.rb +12 -7
  57. data/lib/datadog/core/telemetry/logger.rb +51 -0
  58. data/lib/datadog/core/telemetry/logging.rb +50 -14
  59. data/lib/datadog/core/telemetry/request.rb +13 -1
  60. data/lib/datadog/core/utils/time.rb +12 -0
  61. data/lib/datadog/di/code_tracker.rb +168 -0
  62. data/lib/datadog/di/configuration/settings.rb +163 -0
  63. data/lib/datadog/di/configuration.rb +11 -0
  64. data/lib/datadog/di/error.rb +31 -0
  65. data/lib/datadog/di/extensions.rb +16 -0
  66. data/lib/datadog/di/probe.rb +133 -0
  67. data/lib/datadog/di/probe_builder.rb +41 -0
  68. data/lib/datadog/di/redactor.rb +188 -0
  69. data/lib/datadog/di/serializer.rb +193 -0
  70. data/lib/datadog/di.rb +14 -0
  71. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  72. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +12 -10
  73. data/lib/datadog/profiling/collectors/info.rb +12 -3
  74. data/lib/datadog/profiling/collectors/thread_context.rb +26 -0
  75. data/lib/datadog/profiling/component.rb +20 -4
  76. data/lib/datadog/profiling/http_transport.rb +6 -1
  77. data/lib/datadog/profiling/scheduler.rb +2 -0
  78. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  79. data/lib/datadog/single_step_instrument.rb +12 -0
  80. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  81. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  82. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  83. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  84. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  85. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  86. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  87. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +3 -1
  88. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  89. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  90. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  91. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  92. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  93. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  94. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  95. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  96. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +13 -9
  97. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +6 -3
  98. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  99. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  100. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  101. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  102. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  103. data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -2
  104. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  105. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  106. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  107. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  108. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  109. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  110. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  111. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  112. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  113. data/lib/datadog/tracing/metadata/ext.rb +2 -0
  114. data/lib/datadog/tracing/remote.rb +5 -2
  115. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  116. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  117. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  118. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  119. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  120. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  121. data/lib/datadog/tracing/trace_operation.rb +26 -2
  122. data/lib/datadog/tracing/tracer.rb +14 -12
  123. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  124. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  125. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  126. data/lib/datadog/tracing/workers.rb +1 -1
  127. data/lib/datadog/version.rb +1 -1
  128. metadata +25 -8
  129. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -5,6 +5,7 @@ require_relative 'event'
5
5
  require_relative 'http/transport'
6
6
  require_relative 'metrics_manager'
7
7
  require_relative 'worker'
8
+ require_relative 'logging'
8
9
 
9
10
  require_relative '../configuration/ext'
10
11
  require_relative '../utils/forking'
@@ -17,6 +18,7 @@ module Datadog
17
18
  attr_reader :enabled
18
19
 
19
20
  include Core::Utils::Forking
21
+ include Telemetry::Logging
20
22
 
21
23
  def self.build(settings, agent_settings, logger)
22
24
  enabled = settings.telemetry.enabled
@@ -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
@@ -338,7 +341,6 @@ module Datadog
338
341
  class Log < Base
339
342
  LEVELS = {
340
343
  error: 'ERROR',
341
- debug: 'DEBUG',
342
344
  warn: 'WARN',
343
345
  }.freeze
344
346
 
@@ -346,19 +348,22 @@ module Datadog
346
348
  'logs'
347
349
  end
348
350
 
349
- def initialize(message:, level:)
351
+ def initialize(message:, level:, stack_trace: nil)
350
352
  super()
351
353
  @message = message
354
+ @stack_trace = stack_trace
352
355
  @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
353
356
  end
354
357
 
355
358
  def payload
356
359
  {
357
- logs: [{
358
- message: @message,
359
- level: @level,
360
- # More optional fields to be added here...
361
- }]
360
+ logs: [
361
+ {
362
+ message: @message,
363
+ level: @level,
364
+ stack_trace: @stack_trace,
365
+ }.compact
366
+ ]
362
367
  }
363
368
  end
364
369
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Telemetry
6
+ # === INTERNAL USAGE ONLY ===
7
+ #
8
+ # Report telemetry logs via delegating to the telemetry component instance via mutex.
9
+ #
10
+ # IMPORTANT: Invoking this method during the lifecycle of component initialization will
11
+ # be no-op instead.
12
+ #
13
+ # For developer using this module:
14
+ # read: lib/datadog/core/telemetry/logging.rb
15
+ module Logger
16
+ class << self
17
+ def report(exception, level: :error, description: nil)
18
+ instance&.report(exception, level: level, description: description)
19
+ end
20
+
21
+ def error(description)
22
+ instance&.error(description)
23
+ end
24
+
25
+ private
26
+
27
+ def instance
28
+ # Component initialization uses a mutex to avoid having concurrent initialization.
29
+ # Trying to access the telemetry component during initialization (specifically:
30
+ # from the thread that's actually doing the initialization) would cause a deadlock,
31
+ # since accessing the components would try to recursively lock the mutex.
32
+ #
33
+ # To work around this, we use allow_initialization: false to avoid triggering this issue.
34
+ #
35
+ # The downside is: this leaves us unable to report telemetry during component initialization.
36
+ components = Datadog.send(:components, allow_initialization: false)
37
+
38
+ if components && components.telemetry
39
+ components.telemetry
40
+ else
41
+ Datadog.logger.warn(
42
+ 'Failed to send telemetry before components initialization or within components lifecycle'
43
+ )
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -2,32 +2,68 @@
2
2
 
3
3
  require_relative 'event'
4
4
 
5
+ require 'pathname'
6
+
5
7
  module Datadog
6
8
  module Core
7
9
  module Telemetry
8
- # Logging interface for sending telemetry logs.
10
+ # === INTERNAL USAGE ONLY ===
11
+ #
12
+ # Logging interface for sending telemetry logs... so we can fix them.
13
+ #
14
+ # For developer using this module:
15
+ # - MUST NOT provide any sensitive information (PII)
16
+ # - SHOULD reduce the data cardinality for batching/aggregation
17
+ #
18
+ # Before using it, ask yourself:
19
+ # - Do we need to know about this (ie. internal error or client error)?
20
+ # - How severe/critical is this error? (ie. error, warning, fatal)
21
+ # - What information needed to make it actionable?
9
22
  #
10
- # Reporting internal error so that we can fix them.
11
- # IMPORTANT: Make sure to not log any sensitive information.
12
23
  module Logging
13
- module_function
24
+ # Extract datadog stack trace from the exception
25
+ module DatadogStackTrace
26
+ GEM_ROOT = Pathname.new("#{__dir__}/../../../..").cleanpath.to_s
27
+
28
+ def self.from(exception)
29
+ backtrace = exception.backtrace
30
+
31
+ return unless backtrace
32
+ return if backtrace.empty?
33
+
34
+ stack_trace = +''
35
+ backtrace.each do |line|
36
+ stack_trace << if line.start_with?(GEM_ROOT)
37
+ line[GEM_ROOT.length..-1] || ''
38
+ else
39
+ 'REDACTED'
40
+ end
41
+ stack_trace << ','
42
+ end
14
43
 
15
- def report(exception, level:)
44
+ stack_trace.chomp(',')
45
+ end
46
+ end
47
+
48
+ def report(exception, level: :error, description: nil)
16
49
  # Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
17
- message = exception.class.name || exception.class.inspect
50
+ message = +''
51
+ message << (exception.class.name || exception.class.inspect)
52
+ message << ':' << description if description
18
53
 
19
54
  event = Event::Log.new(
20
55
  message: message,
21
- level: level
56
+ level: level,
57
+ stack_trace: DatadogStackTrace.from(exception)
22
58
  )
23
59
 
24
- if (telemetry = Datadog.send(:components).telemetry)
25
- telemetry.log!(event)
26
- else
27
- Datadog.logger.debug do
28
- "Attempting to send telemetry log when telemetry component is not ready: #{message}"
29
- end
30
- end
60
+ log!(event)
61
+ end
62
+
63
+ def error(description)
64
+ event = Event::Log.new(message: description, level: :error)
65
+
66
+ log!(event)
31
67
  end
32
68
  end
33
69
  end
@@ -31,6 +31,18 @@ module Datadog
31
31
 
32
32
  def application
33
33
  config = Datadog.configuration
34
+
35
+ tracer_version = Core::Environment::Identity.gem_datadog_version_semver2
36
+
37
+ # We need some to distinguish datadog-ci gem versions
38
+ # when examining telemetry metrics emitted from the datadog-ci gem.
39
+ #
40
+ # This code checks that Datadog::CI is loaded and ci mode is enabled and adds
41
+ # "-ci-X.Y.Z" suffix to the tracer version.
42
+ if defined?(::Datadog::CI::VERSION) && config.respond_to?(:ci) && config.ci.enabled
43
+ tracer_version = "#{tracer_version}-ci-#{::Datadog::CI::VERSION::STRING}"
44
+ end
45
+
34
46
  {
35
47
  env: config.env,
36
48
  language_name: Core::Environment::Ext::LANG,
@@ -39,7 +51,7 @@ module Datadog
39
51
  runtime_version: Core::Environment::Ext::ENGINE_VERSION,
40
52
  service_name: config.service,
41
53
  service_version: config.version,
42
- tracer_version: Core::Environment::Identity.gem_datadog_version_semver2
54
+ tracer_version: tracer_version
43
55
  }
44
56
  end
45
57
 
@@ -34,6 +34,18 @@ module Datadog
34
34
  define_singleton_method(:now, &block)
35
35
  end
36
36
 
37
+ # Overrides the implementation of `#get_time
38
+ # with the provided callable.
39
+ #
40
+ # Overriding the method `#get_time` instead of
41
+ # indirectly calling `block` removes
42
+ # one level of method call overhead.
43
+ #
44
+ # @param block [Proc] block that accepts unit and returns timestamp in the requested unit
45
+ def get_time_provider=(block)
46
+ define_singleton_method(:get_time, &block)
47
+ end
48
+
37
49
  def measure(unit = :float_second)
38
50
  before = get_time(unit)
39
51
  yield
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Tracks loaded Ruby code by source file and maintains a map from
6
+ # source file to the loaded code (instruction sequences).
7
+ # Also arranges for code in the loaded files to be instrumented by
8
+ # line probes that have already been received by the library.
9
+ #
10
+ # The loaded code is used to target line trace points when installing
11
+ # line probes which dramatically improves efficiency of line trace points.
12
+ #
13
+ # Note that, since most files will only be loaded one time (via the
14
+ # "require" mechanism), the code tracker needs to be global and not be
15
+ # recreated when the DI component is created.
16
+ #
17
+ # @api private
18
+ class CodeTracker
19
+ def initialize
20
+ @registry = {}
21
+ @trace_point_lock = Mutex.new
22
+ @registry_lock = Mutex.new
23
+ @compiled_trace_point = nil
24
+ end
25
+
26
+ # Starts tracking loaded code.
27
+ #
28
+ # This method should generally be called early in application boot
29
+ # process, because any code loaded before code tracking is enabled
30
+ # will not be instrumentable via line probes.
31
+ #
32
+ # Normally tracking should remain active for the lifetime of the
33
+ # process and would not be ever stopped.
34
+ def start
35
+ trace_point_lock.synchronize do
36
+ # If this code tracker is already running, we can do nothing or
37
+ # restart it (by disabling the trace point and recreating it).
38
+ # It is likely that some applications will attempt to activate
39
+ # DI more than once where the intention is to just activate DI;
40
+ # do not break such applications by clearing out the registry.
41
+ # For now, until there is a use case for recreating the trace point,
42
+ # do nothing if the code tracker has already started.
43
+ return if @compiled_trace_point
44
+
45
+ # Note: .trace enables the trace point.
46
+ @compiled_trace_point = TracePoint.trace(:script_compiled) do |tp|
47
+ # Useful attributes of the trace point object here:
48
+ # .instruction_sequence
49
+ # .instruction_sequence.path (either absolute file path for
50
+ # loaded or required code, or for eval'd code, if filename
51
+ # is specified as argument to eval, then this is the provided
52
+ # filename, otherwise this is a synthesized
53
+ # "(eval at <definition-file>:<line>)" string)
54
+ # .instruction_sequence.absolute_path (absolute file path when
55
+ # load or require are used to load code, nil for eval'd code
56
+ # regardless of whether filename was specified as an argument
57
+ # to eval on ruby 3.1+, same as path for eval'd code on ruby 3.0
58
+ # and lower)
59
+ # .method_id
60
+ # .path (refers to the code location that called the require/eval/etc.,
61
+ # not where the loaded code is; use .path on the instruction sequence
62
+ # to obtain the location of the compiled code)
63
+ # .eval_script
64
+ #
65
+ # For now just map the path to the instruction sequence.
66
+ path = tp.instruction_sequence.absolute_path
67
+ # Do not store mapping for eval'd code, since there is no way
68
+ # to target such code from dynamic instrumentation UI.
69
+ # eval'd code always sets tp.eval_script.
70
+ # When tp.eval_script is nil, code is either 'load'ed or 'require'd.
71
+ # steep, of course, complains about indexing with +path+
72
+ # without checking that it is not nil, so here, maybe there is
73
+ # some situation where path would in fact be nil and
74
+ # steep would end up saving the day.
75
+ if path && !tp.eval_script
76
+ registry_lock.synchronize do
77
+ registry[path] = tp.instruction_sequence
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # Returns whether this code tracker has been activated and is
85
+ # tracking.
86
+ def active?
87
+ trace_point_lock.synchronize do
88
+ !!@compiled_trace_point
89
+ end
90
+ end
91
+
92
+ # Returns an array of RubVM::InstructionSequence (i.e. the compiled code)
93
+ # for the provided path.
94
+ #
95
+ # The argument can be a full path to a Ruby source code file or a
96
+ # suffix (basename + one or more directories preceding the basename).
97
+ # The idea with suffix matches is that file paths are likely to
98
+ # be different between development and production environments and
99
+ # the source control system uses relative paths and doesn't have
100
+ # absolute paths at all.
101
+ #
102
+ # Suffix matches are not guaranteed to be correct, meaning there may
103
+ # be multiple files with the same basename and they may all match a
104
+ # given suffix. In such cases, this method will return all matching
105
+ # paths (and all of these paths will be attempted to be instrumented
106
+ # by upstream code).
107
+ #
108
+ # If the suffix matches one of the paths completely (which requires it
109
+ # to be an absolute path), only the exactly matching path is returned.
110
+ # Otherwise all known paths that end in the suffix are returned.
111
+ # If no paths match, an empty array is returned.
112
+ def iseqs_for_path(suffix)
113
+ registry_lock.synchronize do
114
+ exact = registry[suffix]
115
+ return [exact] if exact
116
+
117
+ inexact = []
118
+ registry.each do |path, iseq|
119
+ # Exact match is not possible here, meaning any matching path
120
+ # has to be longer than the suffix. Require full component matches,
121
+ # meaning either the first character of the suffix is a slash
122
+ # or the previous character in the path is a slash.
123
+ # For now only check for forward slashes for Unix-like OSes;
124
+ # backslash is a legitimate character of a file name in Unix
125
+ # therefore simply permitting forward or back slash is not
126
+ # sufficient, we need to perform an OS check to know which
127
+ # path separator to use.
128
+ if path.length > suffix.length && path.end_with?(suffix)
129
+ previous_char = path[path.length - suffix.length - 1]
130
+ inexact << iseq if previous_char == "/" || suffix[0] == "/"
131
+ end
132
+ end
133
+ inexact
134
+ end
135
+ end
136
+
137
+ # Stops tracking code that is being loaded.
138
+ #
139
+ # This method should ordinarily never be called - if a file is loaded
140
+ # when code tracking is not active, this file will not be instrumentable
141
+ # by line probes.
142
+ #
143
+ # This method is intended for test suite use only, where multiple
144
+ # code tracker instances are created, to fully clean up the old instances.
145
+ def stop
146
+ # Permit multiple stop calls.
147
+ trace_point_lock.synchronize do
148
+ @compiled_trace_point&.disable
149
+ # Clear the instance variable so that the trace point may be
150
+ # reinstated in the future.
151
+ @compiled_trace_point = nil
152
+ end
153
+ registry_lock.synchronize do
154
+ registry.clear
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ # Mapping from paths of loaded files to RubyVM::InstructionSequence
161
+ # objects representing compiled code of those files.
162
+ attr_reader :registry
163
+
164
+ attr_reader :trace_point_lock
165
+ attr_reader :registry_lock
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ module Configuration
6
+ # Settings
7
+ module Settings
8
+ def self.extended(base)
9
+ base = base.singleton_class unless base.is_a?(Class)
10
+ add_settings!(base)
11
+ end
12
+
13
+ def self.add_settings!(base)
14
+ base.class_eval do
15
+ # The setting has "internal" prefix to prevent it from being
16
+ # prematurely turned on by customers.
17
+ settings :dynamic_instrumentation do
18
+ option :enabled do |o|
19
+ o.type :bool
20
+ # The environment variable has an "internal" prefix so that
21
+ # any customers that have the "proper" environment variable
22
+ # turned on (i.e. DD_DYNAMIC_INSTRUMENTATION_ENABLED)
23
+ # do not enable Ruby DI until the latter is ready for
24
+ # customer testing.
25
+ o.env "DD_DYNAMIC_INSTRUMENTATION_ENABLED"
26
+ o.default false
27
+ end
28
+
29
+ # This option instructs dynamic instrumentation to use
30
+ # untargeted trace points when installing line probes and
31
+ # code tracking is not active.
32
+ # WARNING: untargeted trace points carry a massive performance
33
+ # penalty for the entire file in which a line probe is placed.
34
+ #
35
+ # If this option is set to false, which is the default,
36
+ # dynamic instrumentation will add probes that reference
37
+ # unknown files to the list of pending probes, and when
38
+ # the respective files are loaded, the line probes will be
39
+ # installed using targeted trace points. If the file in
40
+ # question is already loaded when the probe is received
41
+ # (for example, it is in a third-party library loaded during
42
+ # application boot), and code tracking was not active when
43
+ # the file was loaded, such files will not be instrumentable
44
+ # via line probes.
45
+ #
46
+ # If this option is set to true
47
+ #
48
+ # activated, DI will in
49
+ # activated or because the files being targeted have beenIf true and code tracking is not enabled, dynamic instrumentation
50
+ # will use untargeted trace points.
51
+ # If false and code tracking is not enabled, dynamic
52
+ # instrumentation will not instrument any files loaded
53
+ # WARNING: these trace points will greatly degrade performance
54
+ # of all code in the instrumented files.
55
+ option :untargeted_trace_points do |o|
56
+ o.type :bool
57
+ o.default false
58
+ end
59
+
60
+ # If true, all of the catch-all rescue blocks in DI
61
+ # will propagate the exceptions onward.
62
+ # WARNING: for internal Datadog use only - this will break
63
+ # the DI product and potentially the library in general in
64
+ # a multitude of ways, cause resource leakage, permanent
65
+ # performance decreases, etc.
66
+ option :propagate_all_exceptions do |o|
67
+ o.type :bool
68
+ o.default false
69
+ end
70
+
71
+ # An array of variable and key names to redact in addition to
72
+ # the built-in list of identifiers.
73
+ #
74
+ # The names will be normalized by removing the following
75
+ # symbols: _, -, @, $, and then matched to the complete
76
+ # variable or key name while ignoring the case.
77
+ # For example, specifying pass_word will match password and
78
+ # PASSWORD, and specifying PASSWORD will match pass_word.
79
+ # Note that, while the at sign (@) is used in Ruby to refer
80
+ # to instance variables, it does not have any significance
81
+ # for this setting (and is removed before matching identifiers).
82
+ option :redacted_identifiers do |o|
83
+ o.env "DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS"
84
+ o.env_parser do |value|
85
+ value&.split(",")&.map(&:strip)
86
+ end
87
+
88
+ o.type :array
89
+ o.default []
90
+ end
91
+
92
+ # An array of class names, values of which will be redacted from
93
+ # dynamic instrumentation snapshots. Example: FooClass.
94
+ # If a name is suffixed by '*', it becomes a wildcard and
95
+ # instances of any class whose name begins with the specified
96
+ # prefix will be redacted (example: Foo*).
97
+ #
98
+ # The names must all be fully-qualified, if any prefix of a
99
+ # class name is configured to be redacted, the value will be
100
+ # subject to redaction. For example, if Foo* is in the
101
+ # redacted class name list, instances of Foo, FooBar,
102
+ # Foo::Bar are all subject to redaction, but Bar::Foo will
103
+ # not be subject to redaction.
104
+ #
105
+ # Leading double-colon is permitted but has no effect,
106
+ # because the names are always considered to be fully-qualified.
107
+ # For example, adding ::Foo to the list will redact instances
108
+ # of Foo.
109
+ #
110
+ # Trailing colons should not be used because they will trigger
111
+ # exact match behavior but Ruby class names do not have
112
+ # trailing colons. For example, Foo:: will not cause anything
113
+ # to be redacted. Use Foo::* to redact all classes under
114
+ # the Foo module.
115
+ option :redacted_type_names do |o|
116
+ o.env "DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES"
117
+ o.env_parser do |value|
118
+ value&.split(",")&.map(&:strip)
119
+ end
120
+
121
+ o.type :array
122
+ o.default []
123
+ end
124
+
125
+ # Maximum number of object or collection traversals that
126
+ # will be permitted when serializing captured values.
127
+ option :max_capture_depth do |o|
128
+ o.type :int
129
+ o.default 3
130
+ end
131
+
132
+ # Maximum number of collection (Array and Hash) elements
133
+ # that will be captured. Arrays and hashes that have more
134
+ # elements will be truncated to this many elements.
135
+ option :max_capture_collection_size do |o|
136
+ o.type :int
137
+ o.default 100
138
+ end
139
+
140
+ # Strings longer than this length will be truncated to this
141
+ # length in dynamic instrumentation snapshots.
142
+ #
143
+ # Note that while all values are stringified during
144
+ # serialization, only values which are originally instances
145
+ # of the String class are subject to this length limit.
146
+ option :max_capture_string_length do |o|
147
+ o.type :int
148
+ o.default 255
149
+ end
150
+
151
+ # Maximim number of attributes that will be captured for
152
+ # a single non-primitive value.
153
+ option :max_capture_attribute_count do |o|
154
+ o.type :int
155
+ o.default 20
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Configuration for DI
6
+ module Configuration
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative "configuration/settings"
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Base class for Dynamic Instrumentation exceptions.
6
+ #
7
+ # None of these exceptions should be propagated out of DI to user
8
+ # applications, therefore these exceptions are not considered to be
9
+ # part of the public API of the library.
10
+ #
11
+ # @api private
12
+ class Error < StandardError
13
+ # Probe does not contain a line number (i.e., is not a line probe).
14
+ class MissingLineNumber < Error
15
+ end
16
+
17
+ # Failed to communicate to the local Datadog agent (e.g. to send
18
+ # probe status or a snapshot).
19
+ class AgentCommunicationError < Error
20
+ end
21
+
22
+ # Attempting to instrument a method or file which does not exist.
23
+ #
24
+ # This could be due to the code that is referenced in the probe
25
+ # having not been loaded yet, or due to the probe referencing code
26
+ # that does not in fact exist anywhere (e.g. due to a misspelling).
27
+ class DITargetNotDefined < Error
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../core/configuration"
4
+ require_relative "configuration"
5
+
6
+ module Datadog
7
+ module DI
8
+ # Extends Datadog tracing with DI features
9
+ module Extensions
10
+ # Inject DI into global objects.
11
+ def self.activate!
12
+ Core::Configuration::Settings.extend(Configuration::Settings)
13
+ end
14
+ end
15
+ end
16
+ end