datadog 2.35.0 → 2.36.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +68 -31
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +37 -18
- data/ext/datadog_profiling_native_extension/collectors_stack.h +8 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +434 -300
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +9 -7
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +7 -8
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -12
- data/ext/datadog_profiling_native_extension/extconf.rb +2 -2
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
- data/ext/datadog_profiling_native_extension/heap_recorder.c +44 -26
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +14 -35
- data/ext/datadog_profiling_native_extension/profiling.c +41 -4
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +33 -34
- data/ext/datadog_profiling_native_extension/stack_recorder.c +24 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
- data/ext/libdatadog_api/datadog_ruby_common.c +7 -8
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -12
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/route_extractor.rb +6 -0
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/configuration.rb +7 -0
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +37 -4
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +64 -19
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -0
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +41 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +20 -7
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +92 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +33 -0
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +17 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +20 -3
- data/lib/datadog/appsec/default_header_tags.rb +10 -6
- data/lib/datadog/core/configuration/components.rb +1 -0
- data/lib/datadog/core/configuration/settings.rb +1 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
- data/lib/datadog/core/remote/component.rb +1 -1
- data/lib/datadog/core/telemetry/event/app_started.rb +0 -21
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/forking.rb +3 -1
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +3 -1
- data/lib/datadog/core.rb +3 -0
- data/lib/datadog/di/base.rb +4 -1
- data/lib/datadog/di/component.rb +1 -1
- data/lib/datadog/error_tracking/collector.rb +2 -1
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/kit/tracing/method_tracer.rb +4 -1
- data/lib/datadog/opentelemetry/sdk/propagator.rb +9 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -0
- data/lib/datadog/profiling/component.rb +13 -15
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/symbol_database/component.rb +306 -98
- data/lib/datadog/symbol_database/extractor.rb +223 -84
- data/lib/datadog/tracing/configuration/ext.rb +13 -0
- data/lib/datadog/tracing/configuration/settings.rb +17 -0
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -0
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc.rb +1 -0
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +3 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +1 -0
- data/lib/datadog/tracing/distributed/propagation.rb +33 -1
- data/lib/datadog/tracing/distributed/trace_context.rb +11 -2
- data/lib/datadog/tracing/trace_digest.rb +7 -0
- data/lib/datadog/tracing/trace_operation.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +1 -0
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +4 -1
- metadata +8 -5
|
@@ -79,7 +79,7 @@ module Datadog
|
|
|
79
79
|
# Cached unbound Module#singleton_class? — dispatched explicitly so user classes
|
|
80
80
|
# that define their own `singleton_class?` (e.g. with required arguments) cannot
|
|
81
81
|
# intercept the predicate and cause the module to be silently dropped from
|
|
82
|
-
# extract_all. Cached at load time because
|
|
82
|
+
# extract_all. Cached at load time because build_per_file_index iterates
|
|
83
83
|
# ObjectSpace.each_object(Module) over tens of thousands of modules.
|
|
84
84
|
MODULE_SINGLETON_CLASS_PRED = Module.instance_method(:singleton_class?)
|
|
85
85
|
private_constant :MODULE_SINGLETON_CLASS_PRED
|
|
@@ -134,21 +134,65 @@ module Datadog
|
|
|
134
134
|
# Returns an array of FILE scopes with proper FQN-based nesting.
|
|
135
135
|
#
|
|
136
136
|
# Two-pass algorithm:
|
|
137
|
-
# Pass 1:
|
|
138
|
-
#
|
|
137
|
+
# Pass 1 (`build_per_file_index`): iterate ObjectSpace once, building
|
|
138
|
+
# `{ file_path => [[mod_name, mod, [method_name_symbol, ...]], ...] }`.
|
|
139
|
+
# Stores Symbol method names + Module refs only; no UnboundMethod retention
|
|
140
|
+
# between passes.
|
|
141
|
+
# Pass 2 (`build_file_scope`): for each file in the index, resolve
|
|
142
|
+
# UnboundMethods just-in-time, build the nested MODULE/CLASS scope tree from
|
|
143
|
+
# FQN splitting, and produce one FILE Scope. The per-file working set is
|
|
144
|
+
# released as soon as the FILE scope is yielded (or accumulated into the
|
|
145
|
+
# returned Array, in legacy mode).
|
|
139
146
|
#
|
|
140
147
|
# This is the production path used by Component. Methods are split by source file,
|
|
141
148
|
# so a class reopened across two files produces two FILE scopes, each with only
|
|
142
149
|
# the methods defined in that file.
|
|
143
150
|
#
|
|
144
|
-
#
|
|
151
|
+
# Memory profile (with a block):
|
|
152
|
+
# - Pass 1 builds a per-file index containing only Symbol method names plus
|
|
153
|
+
# Module references. No UnboundMethod objects are retained between passes.
|
|
154
|
+
# - Pass 2 processes one file at a time. The peak per file is bounded by the
|
|
155
|
+
# number of methods that live in that one file across all its modules
|
|
156
|
+
# (typical Rails: tens of methods; pathological case: a single very large
|
|
157
|
+
# source file). Once a FILE scope is yielded and the caller stops referencing
|
|
158
|
+
# it, the entire per-file working set becomes garbage.
|
|
159
|
+
#
|
|
160
|
+
# This is O(largest_file + batch_buffer), not O(total_classes).
|
|
161
|
+
#
|
|
162
|
+
# Without a block, returns the full `Array<Scope>` (legacy form, used by specs).
|
|
163
|
+
# The Array itself still scales with the number of files, so block form is the
|
|
164
|
+
# one to use for production memory bounds.
|
|
165
|
+
#
|
|
166
|
+
# @yieldparam scope [Scope] FILE scope for one source file
|
|
167
|
+
# @return [Array<Scope>, nil] Array of FILE scopes when called without a block; nil when a block is given
|
|
145
168
|
def extract_all
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
169
|
+
index = build_per_file_index
|
|
170
|
+
|
|
171
|
+
if block_given?
|
|
172
|
+
# Drain the index destructively so each per-file entry becomes eligible for
|
|
173
|
+
# collection as soon as its FILE scope is yielded and consumed. Hash#shift
|
|
174
|
+
# returns [key, value] on a non-empty hash and nil when empty, so the
|
|
175
|
+
# `while (pair = ...)` form is the drain. Indexing pair[0]/pair[1] rather
|
|
176
|
+
# than destructuring avoids introducing names into method scope that would
|
|
177
|
+
# then shadow the else-branch's block parameters on Ruby 2.5/2.6.
|
|
178
|
+
while (pair = index.shift)
|
|
179
|
+
scope = build_file_scope(pair[0], pair[1])
|
|
180
|
+
yield scope if scope
|
|
181
|
+
end
|
|
182
|
+
nil
|
|
183
|
+
else
|
|
184
|
+
# Legacy non-block form for specs. No memory bound — the full Array is
|
|
185
|
+
# materialized.
|
|
186
|
+
result = []
|
|
187
|
+
index.each do |path, file_entries|
|
|
188
|
+
scope = build_file_scope(path, file_entries)
|
|
189
|
+
result << scope if scope
|
|
190
|
+
end
|
|
191
|
+
result
|
|
192
|
+
end
|
|
149
193
|
rescue => e
|
|
150
194
|
@logger.debug { "symdb: error in extract_all: #{e.class}: #{e.message}" }
|
|
151
|
-
[]
|
|
195
|
+
block_given? ? nil : []
|
|
152
196
|
end
|
|
153
197
|
|
|
154
198
|
private
|
|
@@ -165,6 +209,58 @@ module Datadog
|
|
|
165
209
|
nil
|
|
166
210
|
end
|
|
167
211
|
|
|
212
|
+
# Verify that mod_name still resolves to mod through Ruby's constant
|
|
213
|
+
# table. Returns false when a Class/Module has been detached from its
|
|
214
|
+
# constant (via remove_const) but still carries the cached Module#name —
|
|
215
|
+
# see build_per_file_index for the failure mode this protects.
|
|
216
|
+
#
|
|
217
|
+
# Walks the namespace path segment-by-segment. For each segment:
|
|
218
|
+
# 1. Check for a pending autoload directly on the current namespace.
|
|
219
|
+
# If present, const_get would trigger it — loading customer code as
|
|
220
|
+
# a side effect of symbol extraction and raising LoadError if the
|
|
221
|
+
# target file is missing (LoadError is ScriptError, not StandardError,
|
|
222
|
+
# and would propagate past the outer rescue in
|
|
223
|
+
# build_per_file_index). Return false instead.
|
|
224
|
+
# 2. Otherwise, require the constant to be directly defined on this
|
|
225
|
+
# namespace (const_defined?(sym, false)) and descend via
|
|
226
|
+
# const_get(sym, false). The direct-only lookup means an ancestor's
|
|
227
|
+
# pending autoload at the same name does not affect the result: a
|
|
228
|
+
# subclass with its own binding resolves through the binding, an
|
|
229
|
+
# inherited autoload triggers nothing.
|
|
230
|
+
#
|
|
231
|
+
# Ruby 2.7+ adds an inherit parameter to Module#autoload? —
|
|
232
|
+
# `current.autoload?(sym, false)` cleanly asks "is there an autoload
|
|
233
|
+
# registered directly on this namespace?". On Ruby 2.5 and 2.6 that
|
|
234
|
+
# parameter doesn't exist (per Ruby 2.7.0 NEWS), so the fallback
|
|
235
|
+
# branch uses `current.autoload?(sym)` which also reports inherited
|
|
236
|
+
# autoloads. The consequence on 2.5/2.6: a subclass binding whose
|
|
237
|
+
# name collides with an ancestor's pending autoload is conservatively
|
|
238
|
+
# treated as stale and dropped from extraction. The minimum supported
|
|
239
|
+
# Ruby is 2.5 per lib/datadog/version.rb, so this fallback ships.
|
|
240
|
+
# @param mod_name [String]
|
|
241
|
+
# @param mod [Module]
|
|
242
|
+
# @return [Boolean]
|
|
243
|
+
def resolves_to_same_module?(mod_name, mod)
|
|
244
|
+
current = Object
|
|
245
|
+
mod_name.split('::').each do |seg|
|
|
246
|
+
sym = seg.to_sym
|
|
247
|
+
pending_autoload = if RubyVersion.is?('>= 2.7')
|
|
248
|
+
current.autoload?(sym, false)
|
|
249
|
+
else
|
|
250
|
+
current.autoload?(sym)
|
|
251
|
+
end
|
|
252
|
+
return false if pending_autoload
|
|
253
|
+
return false unless current.const_defined?(sym, false)
|
|
254
|
+
current = current.const_get(sym, false)
|
|
255
|
+
end
|
|
256
|
+
current.equal?(mod)
|
|
257
|
+
rescue NameError, ArgumentError
|
|
258
|
+
# Expected "no" outcome for stale/detached classes — the whole point
|
|
259
|
+
# of this predicate. Per the rescue convention in this file's header
|
|
260
|
+
# comment: inner per-item rescues are expected failures, no logging.
|
|
261
|
+
false
|
|
262
|
+
end
|
|
263
|
+
|
|
168
264
|
# Check if module is from user code (not gems or stdlib)
|
|
169
265
|
# @param mod [Module] The module to check
|
|
170
266
|
# @return [Boolean] true if user code
|
|
@@ -614,7 +710,7 @@ module Datadog
|
|
|
614
710
|
|
|
615
711
|
# ── extract_all helpers ──────────────────────────────────────────────
|
|
616
712
|
|
|
617
|
-
# Sleep between chunks of modules processed in
|
|
713
|
+
# Sleep between chunks of modules processed in build_per_file_index so
|
|
618
714
|
# request-handling threads have guaranteed CPU time while extraction is in
|
|
619
715
|
# flight. Unlike Thread.pass (which only offers the GVL among runnable
|
|
620
716
|
# threads and leaves the extractor immediately re-runnable), sleep removes
|
|
@@ -629,10 +725,19 @@ module Datadog
|
|
|
629
725
|
SLEEP_SECONDS = 0.001
|
|
630
726
|
private_constant :SLEEP_EVERY_N_MODULES, :SLEEP_SECONDS
|
|
631
727
|
|
|
632
|
-
# Pass 1
|
|
633
|
-
#
|
|
634
|
-
|
|
635
|
-
|
|
728
|
+
# Pass 1 (memory-bounded form): build a per-file index of
|
|
729
|
+
# `{ file_path => [[mod_name, mod, [method_name_symbol, ...]], ...] }`.
|
|
730
|
+
#
|
|
731
|
+
# Stores Symbol method names plus Module references only — no UnboundMethod
|
|
732
|
+
# objects retained between passes. UnboundMethods created here (to read
|
|
733
|
+
# `source_location`) become garbage as the inner loop ends.
|
|
734
|
+
#
|
|
735
|
+
# The Module references are pointer-sized and the modules are already kept
|
|
736
|
+
# alive in ObjectSpace, so adding them to the index costs no extra retention.
|
|
737
|
+
#
|
|
738
|
+
# @return [Hash{String=>Array<Array(String, Module, Array<Symbol>)>}]
|
|
739
|
+
def build_per_file_index
|
|
740
|
+
index = {}
|
|
636
741
|
seen = 0
|
|
637
742
|
|
|
638
743
|
ObjectSpace.each_object(Module) do |mod|
|
|
@@ -650,84 +755,119 @@ module Datadog
|
|
|
650
755
|
|
|
651
756
|
mod_name = safe_mod_name(mod)
|
|
652
757
|
next unless mod_name
|
|
758
|
+
next unless resolves_to_same_module?(mod_name, mod)
|
|
653
759
|
next unless user_code_module?(mod)
|
|
654
760
|
|
|
655
|
-
|
|
761
|
+
file_to_names = collect_method_names_by_file(mod)
|
|
656
762
|
|
|
657
|
-
#
|
|
658
|
-
#
|
|
659
|
-
if
|
|
763
|
+
# Namespace-only modules (no own methods) — use find_source_file as the
|
|
764
|
+
# canonical file so the FILE scope still gets a MODULE entry.
|
|
765
|
+
if file_to_names.empty?
|
|
660
766
|
source_file = find_source_file(mod)
|
|
661
|
-
|
|
767
|
+
file_to_names[source_file] = [] if source_file
|
|
662
768
|
end
|
|
663
769
|
|
|
664
|
-
next if
|
|
770
|
+
next if file_to_names.empty?
|
|
665
771
|
|
|
666
|
-
|
|
772
|
+
file_to_names.each do |file_path, method_names|
|
|
773
|
+
(index[file_path] ||= []) << [mod_name, mod, method_names]
|
|
774
|
+
end
|
|
667
775
|
rescue => e
|
|
668
|
-
@logger.debug { "symdb: error
|
|
776
|
+
@logger.debug { "symdb: error indexing #{mod_name || '<unknown>'}: #{e.class}: #{e.message}" }
|
|
669
777
|
end
|
|
670
778
|
|
|
671
|
-
|
|
779
|
+
index
|
|
672
780
|
end
|
|
673
781
|
|
|
674
|
-
#
|
|
675
|
-
#
|
|
676
|
-
#
|
|
677
|
-
|
|
782
|
+
# For a single module, return `{ file_path => [method_name_symbol, ...] }`.
|
|
783
|
+
# Stores only the method-name symbols and their file paths — UnboundMethod
|
|
784
|
+
# objects allocated to read `source_location` are not retained between
|
|
785
|
+
# passes, so they can be GC'd as soon as the inner loop ends.
|
|
786
|
+
def collect_method_names_by_file(mod)
|
|
678
787
|
result = Hash.new { |h, k| h[k] = [] } # steep:ignore
|
|
679
788
|
|
|
680
|
-
#
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
@logger.debug { "symdb: error grouping method #{method_name}: #{e.class}: #{e.message}" }
|
|
789
|
+
# Module#instance_methods(false) already returns both public and protected
|
|
790
|
+
# methods, so iterating it plus private_instance_methods covers all three
|
|
791
|
+
# visibilities without an intermediate merged array.
|
|
792
|
+
[mod.instance_methods(false), mod.private_instance_methods(false)].each do |method_names|
|
|
793
|
+
method_names.each do |method_name|
|
|
794
|
+
method = mod.instance_method(method_name)
|
|
795
|
+
loc = method.source_location
|
|
796
|
+
next unless loc
|
|
797
|
+
next unless user_code_path?(loc[0])
|
|
798
|
+
|
|
799
|
+
result[loc[0]] << method_name
|
|
800
|
+
rescue => e
|
|
801
|
+
@logger.debug { "symdb: error indexing method #{method_name}: #{e.class}: #{e.message}" }
|
|
802
|
+
end
|
|
695
803
|
end
|
|
696
804
|
|
|
697
805
|
result
|
|
698
806
|
rescue => e
|
|
699
|
-
@logger.debug { "symdb: error
|
|
807
|
+
@logger.debug { "symdb: error indexing methods: #{e.class}: #{e.message}" }
|
|
700
808
|
{}
|
|
701
809
|
end
|
|
702
810
|
|
|
703
|
-
# Pass 2:
|
|
704
|
-
#
|
|
811
|
+
# Pass 2: build the FILE scope for one source file by walking just the modules
|
|
812
|
+
# that contribute methods to it. Resolves UnboundMethods just-in-time per
|
|
813
|
+
# method; the per-method scratch is collected by GC as each module's loop body
|
|
814
|
+
# ends. The returned Scope is the only thing the caller needs to keep alive
|
|
815
|
+
# — once the caller drops it, the entire per-file working set is collectable.
|
|
705
816
|
#
|
|
706
|
-
#
|
|
707
|
-
#
|
|
708
|
-
# @
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
817
|
+
# @param file_path [String]
|
|
818
|
+
# @param entries [Array<Array(String, Module, Array<Symbol>)>] tuples produced by build_per_file_index
|
|
819
|
+
# @return [Scope, nil] FILE scope, or nil if nothing extractable
|
|
820
|
+
def build_file_scope(file_path, entries)
|
|
821
|
+
return nil if entries.empty?
|
|
822
|
+
|
|
823
|
+
root = {
|
|
824
|
+
name: file_path, type: 'FILE', children: {},
|
|
825
|
+
methods: [], mod: nil, source_file: file_path, fqn: nil,
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
# Sort by FQN depth so parent namespaces are placed before children.
|
|
829
|
+
sorted = entries.sort_by { |(mod_name, _, _)| mod_name.count(':') }
|
|
830
|
+
|
|
831
|
+
sorted.each do |mod_name, mod, method_names|
|
|
832
|
+
# Resolve UnboundMethods for this (mod, file) just-in-time. These objects
|
|
833
|
+
# live only as long as the tree node holds them; they are released when
|
|
834
|
+
# convert_tree_to_scope finishes building the file's Scope.
|
|
835
|
+
method_infos = Core::Utils::EnumerableCompat.filter_map(method_names) do |name|
|
|
836
|
+
method = mod.instance_method(name)
|
|
837
|
+
# Pass 1 (build_per_file_index) recorded this method under file_path.
|
|
838
|
+
# If the method has been redefined in another file between the two
|
|
839
|
+
# passes (e.g. a class reopened during a Rails reload while extract_all
|
|
840
|
+
# is iterating), the resolved UnboundMethod's source_location now
|
|
841
|
+
# points elsewhere. Drop the stale entry — the hot-load TracePoint
|
|
842
|
+
# enqueues the redefined class and the next debounce window extracts
|
|
843
|
+
# it under the new file_path.
|
|
844
|
+
loc = method.source_location
|
|
845
|
+
next nil unless loc && loc[0] == file_path
|
|
846
|
+
{name: name, method: method, type: :instance}
|
|
847
|
+
rescue => e
|
|
848
|
+
@logger.debug { "symdb: error resolving #{mod_name}##{name}: #{e.class}: #{e.message}" }
|
|
849
|
+
nil
|
|
725
850
|
end
|
|
851
|
+
|
|
852
|
+
# If Pass 1 recorded methods for this module but every one of them has
|
|
853
|
+
# moved out of file_path between the passes, drop the entry — otherwise
|
|
854
|
+
# the FILE scope would carry an empty CLASS/MODULE node at a location
|
|
855
|
+
# the module no longer lives in.
|
|
856
|
+
next if method_names.any? && method_infos.empty?
|
|
857
|
+
|
|
858
|
+
parts = mod_name.split('::')
|
|
859
|
+
place_in_tree(root, parts, mod, mod_name, method_infos, file_path)
|
|
726
860
|
rescue => e
|
|
727
|
-
@logger.debug { "symdb: error
|
|
861
|
+
@logger.debug { "symdb: error placing #{mod_name} in tree: #{e.class}: #{e.message}" }
|
|
728
862
|
end
|
|
729
863
|
|
|
730
|
-
|
|
864
|
+
# steep:ignore:start
|
|
865
|
+
# Steep widens root[:children] to the union of all value types declared in
|
|
866
|
+
# the literal (String | Hash | Array | nil), losing the Hash narrowing.
|
|
867
|
+
return nil if root[:children].empty?
|
|
868
|
+
# steep:ignore:end
|
|
869
|
+
|
|
870
|
+
convert_tree_to_scope(file_path, root)
|
|
731
871
|
end
|
|
732
872
|
|
|
733
873
|
# Place a module/class in the file tree at the correct nesting depth.
|
|
@@ -784,25 +924,24 @@ module Datadog
|
|
|
784
924
|
'MODULE'
|
|
785
925
|
end
|
|
786
926
|
|
|
787
|
-
# Convert
|
|
788
|
-
# @param
|
|
789
|
-
# @
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
end
|
|
927
|
+
# Convert a single file tree (built by build_file_scope) to a FILE Scope.
|
|
928
|
+
# @param file_path [String] Source file path
|
|
929
|
+
# @param root [Hash] Tree node from build_file_scope
|
|
930
|
+
# @return [Scope] FILE scope
|
|
931
|
+
def convert_tree_to_scope(file_path, root)
|
|
932
|
+
file_hash = FileHash.compute(file_path, logger: @logger)
|
|
933
|
+
lang = {}
|
|
934
|
+
lang[:file_hash] = file_hash if file_hash
|
|
935
|
+
|
|
936
|
+
Scope.new(
|
|
937
|
+
scope_type: 'FILE',
|
|
938
|
+
name: file_path,
|
|
939
|
+
source_file: file_path,
|
|
940
|
+
start_line: UNKNOWN_MIN_LINE,
|
|
941
|
+
end_line: UNKNOWN_MAX_LINE,
|
|
942
|
+
language_specifics: lang,
|
|
943
|
+
scopes: root[:children].values.map { |child| convert_node_to_scope(child) },
|
|
944
|
+
)
|
|
806
945
|
end
|
|
807
946
|
|
|
808
947
|
# Convert a single hash node to a Scope object (recursive).
|
|
@@ -62,6 +62,19 @@ module Datadog
|
|
|
62
62
|
|
|
63
63
|
ENV_PROPAGATION_STYLE_EXTRACT = 'DD_TRACE_PROPAGATION_STYLE_EXTRACT'
|
|
64
64
|
|
|
65
|
+
PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE = 'continue'
|
|
66
|
+
PROPAGATION_BEHAVIOR_EXTRACT_RESTART = 'restart'
|
|
67
|
+
PROPAGATION_BEHAVIOR_EXTRACT_IGNORE = 'ignore'
|
|
68
|
+
|
|
69
|
+
PROPAGATION_BEHAVIOR_EXTRACT_SUPPORTED = [
|
|
70
|
+
PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE,
|
|
71
|
+
PROPAGATION_BEHAVIOR_EXTRACT_RESTART,
|
|
72
|
+
PROPAGATION_BEHAVIOR_EXTRACT_IGNORE,
|
|
73
|
+
].freeze
|
|
74
|
+
|
|
75
|
+
# Behavior applied to a distributed-trace context extracted from incoming requests.
|
|
76
|
+
ENV_PROPAGATION_BEHAVIOR_EXTRACT = 'DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT'
|
|
77
|
+
|
|
65
78
|
# A no-op propagator. Compatible with OpenTelemetry's `none` propagator.
|
|
66
79
|
# @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#get_otel__propagators
|
|
67
80
|
PROPAGATION_STYLE_NONE = 'none'
|
|
@@ -117,6 +117,23 @@ module Datadog
|
|
|
117
117
|
end
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
+
# Behavior applied to a distributed-trace context extracted from incoming requests.
|
|
121
|
+
option :propagation_behavior_extract do |o|
|
|
122
|
+
o.env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_BEHAVIOR_EXTRACT
|
|
123
|
+
o.default Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE
|
|
124
|
+
o.type :string
|
|
125
|
+
o.env_parser do |value|
|
|
126
|
+
value = value&.downcase
|
|
127
|
+
if Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_SUPPORTED.include?(value)
|
|
128
|
+
value
|
|
129
|
+
else
|
|
130
|
+
Datadog.logger.warn("Unsupported propagation behavior extract: #{value.inspect}. " \
|
|
131
|
+
"Set to default value (#{Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE}).")
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
120
137
|
# Strictly stop at the first successfully serialized style.
|
|
121
138
|
# This prevents the tracer from enriching the extracted context with information from
|
|
122
139
|
# other valid propagations styles present in the request.
|
|
@@ -101,6 +101,13 @@ module Datadog
|
|
|
101
101
|
# shrink a Hash using an "st_table" back to an "ar_table")
|
|
102
102
|
@cache = Hash[*1..20]
|
|
103
103
|
@cache.clear
|
|
104
|
+
# Workaround for the segfault from https://github.com/DataDog/dd-trace-rb/issues/5718#issuecomment-4421844775.
|
|
105
|
+
# It crashes on a simple {Hash} lookup, {Hash#key?}, called from `@cache.fetch(value)`.
|
|
106
|
+
# Using an identity-based {Hash} avoids {Hash#key?} calls.
|
|
107
|
+
# We should attempt to remove this workaround when we only support Ruby 4+,
|
|
108
|
+
# as large change around the crash site was done in that version (https://github.com/ruby/ruby/pull/14039/changes#diff-884a5a8a369ef1b4c7597e00aa65974cec8c5f54f25f03ad5d24848f64892869R1743),
|
|
109
|
+
# where `RClass.cc_table` (the NULL dereferenced pointer) became a GC-managed object,
|
|
110
|
+
@cache.compare_by_identity
|
|
104
111
|
end
|
|
105
112
|
|
|
106
113
|
# (see Resolver#resolve)
|
|
@@ -20,6 +20,7 @@ module Datadog
|
|
|
20
20
|
def initialize(
|
|
21
21
|
propagation_style_inject:,
|
|
22
22
|
propagation_style_extract:,
|
|
23
|
+
propagation_behavior_extract:,
|
|
23
24
|
propagation_extract_first:
|
|
24
25
|
)
|
|
25
26
|
super(
|
|
@@ -38,6 +39,7 @@ module Datadog
|
|
|
38
39
|
},
|
|
39
40
|
propagation_style_inject: propagation_style_inject,
|
|
40
41
|
propagation_style_extract: propagation_style_extract,
|
|
42
|
+
propagation_behavior_extract: propagation_behavior_extract,
|
|
41
43
|
propagation_extract_first: propagation_extract_first
|
|
42
44
|
)
|
|
43
45
|
end
|
|
@@ -36,6 +36,7 @@ module Datadog
|
|
|
36
36
|
@propagation = GRPC::Distributed::Propagation.new(
|
|
37
37
|
propagation_style_inject: tracing.propagation_style_inject,
|
|
38
38
|
propagation_style_extract: tracing.propagation_style_extract,
|
|
39
|
+
propagation_behavior_extract: tracing.propagation_behavior_extract,
|
|
39
40
|
propagation_extract_first: tracing.propagation_extract_first
|
|
40
41
|
)
|
|
41
42
|
end
|
|
@@ -19,6 +19,7 @@ module Datadog
|
|
|
19
19
|
def initialize(
|
|
20
20
|
propagation_style_inject:,
|
|
21
21
|
propagation_style_extract:,
|
|
22
|
+
propagation_behavior_extract:,
|
|
22
23
|
propagation_extract_first:
|
|
23
24
|
)
|
|
24
25
|
super(
|
|
@@ -37,6 +38,7 @@ module Datadog
|
|
|
37
38
|
},
|
|
38
39
|
propagation_style_inject: propagation_style_inject,
|
|
39
40
|
propagation_style_extract: propagation_style_extract,
|
|
41
|
+
propagation_behavior_extract: propagation_behavior_extract,
|
|
40
42
|
propagation_extract_first: propagation_extract_first
|
|
41
43
|
)
|
|
42
44
|
end
|
|
@@ -36,6 +36,7 @@ module Datadog
|
|
|
36
36
|
@propagation = HTTP::Distributed::Propagation.new(
|
|
37
37
|
propagation_style_inject: tracing.propagation_style_inject,
|
|
38
38
|
propagation_style_extract: tracing.propagation_style_extract,
|
|
39
|
+
propagation_behavior_extract: tracing.propagation_behavior_extract,
|
|
39
40
|
propagation_extract_first: tracing.propagation_extract_first
|
|
40
41
|
)
|
|
41
42
|
end
|
|
@@ -19,6 +19,7 @@ module Datadog
|
|
|
19
19
|
def initialize(
|
|
20
20
|
propagation_style_inject:,
|
|
21
21
|
propagation_style_extract:,
|
|
22
|
+
propagation_behavior_extract:,
|
|
22
23
|
propagation_extract_first:
|
|
23
24
|
)
|
|
24
25
|
super(
|
|
@@ -37,6 +38,7 @@ module Datadog
|
|
|
37
38
|
},
|
|
38
39
|
propagation_style_inject: propagation_style_inject,
|
|
39
40
|
propagation_style_extract: propagation_style_extract,
|
|
41
|
+
propagation_behavior_extract: propagation_behavior_extract,
|
|
40
42
|
propagation_extract_first: propagation_extract_first
|
|
41
43
|
)
|
|
42
44
|
end
|
|
@@ -28,6 +28,7 @@ module Datadog
|
|
|
28
28
|
@propagation = Karafka::Distributed::Propagation.new(
|
|
29
29
|
propagation_style_inject: tracing.propagation_style_inject,
|
|
30
30
|
propagation_style_extract: tracing.propagation_style_extract,
|
|
31
|
+
propagation_behavior_extract: tracing.propagation_behavior_extract,
|
|
31
32
|
propagation_extract_first: tracing.propagation_extract_first
|
|
32
33
|
)
|
|
33
34
|
end
|
|
@@ -268,7 +268,9 @@ module Datadog
|
|
|
268
268
|
# 1. resource renaming is enabled
|
|
269
269
|
# 2. AppSec is enabled and resource renaming is disabled (by default, not explicitly)
|
|
270
270
|
if Datadog.configuration.tracing.resource_renaming.enabled ||
|
|
271
|
-
Datadog.configuration.appsec
|
|
271
|
+
Datadog.configuration.respond_to?(:appsec) &&
|
|
272
|
+
Datadog.configuration.appsec&.enabled &&
|
|
273
|
+
Datadog.configuration.tracing.resource_renaming.options[:enabled].default_precedence?
|
|
272
274
|
request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ENDPOINT, value)
|
|
273
275
|
end
|
|
274
276
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../../ruby_version'
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module Tracing
|
|
5
7
|
module Contrib
|
|
@@ -48,7 +50,7 @@ module Datadog
|
|
|
48
50
|
nil
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
if
|
|
53
|
+
if RubyVersion.is?('>= 2.6')
|
|
52
54
|
def split(path, pattern = nil, &block)
|
|
53
55
|
path.split(pattern, &block)
|
|
54
56
|
end
|
|
@@ -20,6 +20,7 @@ module Datadog
|
|
|
20
20
|
def initialize(
|
|
21
21
|
propagation_style_inject:,
|
|
22
22
|
propagation_style_extract:,
|
|
23
|
+
propagation_behavior_extract:,
|
|
23
24
|
propagation_extract_first:
|
|
24
25
|
)
|
|
25
26
|
super(
|
|
@@ -38,6 +39,7 @@ module Datadog
|
|
|
38
39
|
},
|
|
39
40
|
propagation_style_inject: propagation_style_inject,
|
|
40
41
|
propagation_style_extract: propagation_style_extract,
|
|
42
|
+
propagation_behavior_extract: propagation_behavior_extract,
|
|
41
43
|
propagation_extract_first: propagation_extract_first
|
|
42
44
|
)
|
|
43
45
|
end
|
|
@@ -28,6 +28,7 @@ module Datadog
|
|
|
28
28
|
@propagation = Sidekiq::Distributed::Propagation.new(
|
|
29
29
|
propagation_style_inject: tracing.propagation_style_inject,
|
|
30
30
|
propagation_style_extract: tracing.propagation_style_extract,
|
|
31
|
+
propagation_behavior_extract: tracing.propagation_behavior_extract,
|
|
31
32
|
propagation_extract_first: tracing.propagation_extract_first
|
|
32
33
|
)
|
|
33
34
|
end
|
|
@@ -19,6 +19,7 @@ module Datadog
|
|
|
19
19
|
def initialize(
|
|
20
20
|
propagation_style_inject:,
|
|
21
21
|
propagation_style_extract:,
|
|
22
|
+
propagation_behavior_extract:,
|
|
22
23
|
propagation_extract_first:
|
|
23
24
|
)
|
|
24
25
|
super(
|
|
@@ -37,6 +38,7 @@ module Datadog
|
|
|
37
38
|
},
|
|
38
39
|
propagation_style_inject: propagation_style_inject,
|
|
39
40
|
propagation_style_extract: propagation_style_extract,
|
|
41
|
+
propagation_behavior_extract: propagation_behavior_extract,
|
|
40
42
|
propagation_extract_first: propagation_extract_first
|
|
41
43
|
)
|
|
42
44
|
end
|
|
@@ -32,6 +32,7 @@ module Datadog
|
|
|
32
32
|
@propagation = WaterDrop::Distributed::Propagation.new(
|
|
33
33
|
propagation_style_inject: tracing.propagation_style_inject,
|
|
34
34
|
propagation_style_extract: tracing.propagation_style_extract,
|
|
35
|
+
propagation_behavior_extract: tracing.propagation_behavior_extract,
|
|
35
36
|
propagation_extract_first: tracing.propagation_extract_first
|
|
36
37
|
)
|
|
37
38
|
end
|