-
Notifications
You must be signed in to change notification settings - Fork 374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DEBUG-3182 Rework DI loading #4239
Open
p-datadog
wants to merge
12
commits into
master
Choose a base branch
from
di-loading
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+202
−115
Open
Changes from 6 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
fa252bc
Rework DI loading
p 3579a0c
add note
p ad35c7d
note purpose of current_component
p d15e60a
move current_component
p f87e9b1
current_component will always be defined
p 31d7a9b
standard
p 4eeb858
types
p e80940a
Merge branch 'master' into di-loading
p-datadog 49a1728
integration test for DI loading
p f62fcb2
add exit status assertions to execution spec
p 5b1a9e0
typo fix
p 4cc2d79
rubocop
p File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# frozen_string_literal: true | ||
|
||
# This file is loaded by datadog/di/init.rb. | ||
# It contains just the global DI reference to the (normally one and only) | ||
# code tracker for the current process. | ||
# This file should not require the rest of DI, specifically none of the | ||
# contrib code that is meant to be loaded after third-party libraries | ||
# are loaded, and also none of the rest of datadog library which also | ||
# has contrib code in other products. | ||
|
||
require_relative 'code_tracker' | ||
|
||
module Datadog | ||
# Namespace for Datadog dynamic instrumentation. | ||
# | ||
# @api private | ||
module DI | ||
LOCK = Mutex.new | ||
|
||
class << self | ||
attr_reader :code_tracker | ||
|
||
# Activates code tracking. Normally this method should be called | ||
# when the application starts. If instrumenting third-party code, | ||
# code tracking needs to be enabled before the third-party libraries | ||
# are loaded. If you definitely will not be instrumenting | ||
# third-party libraries, activating tracking after third-party libraries | ||
# have been loaded may improve lookup performance. | ||
# | ||
# TODO test that activating tracker multiple times preserves | ||
# existing mappings in the registry | ||
def activate_tracking! | ||
(@code_tracker ||= CodeTracker.new).start | ||
end | ||
|
||
# Activates code tracking if possible. | ||
# | ||
# This method does nothing if invoked in an environment that does not | ||
# implement required trace points for code tracking (MRI Ruby < 2.6, | ||
# JRuby) and rescues any exceptions that may be raised by downstream | ||
# DI code. | ||
def activate_tracking | ||
# :script_compiled trace point was added in Ruby 2.6. | ||
return unless RUBY_VERSION >= '2.6' | ||
|
||
begin | ||
# Activate code tracking by default because line trace points will not work | ||
# without it. | ||
Datadog::DI.activate_tracking! | ||
rescue => exc | ||
if defined?(Datadog.logger) | ||
Datadog.logger.warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}") | ||
else | ||
# We do not have Datadog logger potentially because DI code tracker is | ||
# being loaded early in application boot process and the rest of datadog | ||
# wasn't loaded yet. Output to standard error. | ||
warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}") | ||
end | ||
end | ||
end | ||
|
||
# Deactivates code tracking. In normal usage of DI this method should | ||
# never be called, however it is used by DI's test suite to reset | ||
# state for individual tests. | ||
# | ||
# Note that deactivating tracking clears out the registry, losing | ||
# the ability to look up files that have been loaded into the process | ||
# already. | ||
def deactivate_tracking! | ||
code_tracker&.stop | ||
end | ||
|
||
# Returns whether code tracking is available. | ||
# This method should be used instead of querying #code_tracker | ||
# because the latter one may be nil. | ||
def code_tracking_active? | ||
code_tracker&.active? || false | ||
end | ||
|
||
# DI code tracker is instantiated globally before the regular set of | ||
# components is created, but the code tracker needs to call out to the | ||
# "current" DI component to perform instrumentation when application | ||
# code is loaded. Because this call may happen prior to Datadog | ||
# components having been initialized, we maintain the "current component" | ||
# which contains a reference to the most recently instantiated | ||
# DI::Component. This way, if a DI component hasn't been instantiated, | ||
# we do not try to reference Datadog.components. | ||
# In other words, this method exists so that we never attempt to call | ||
# Datadog.components from the code tracker. | ||
def current_component | ||
LOCK.synchronize do | ||
@current_components&.last | ||
end | ||
end | ||
|
||
# To avoid potential races with DI::Component being added and removed, | ||
# we maintain a list of the components. Normally the list should contain | ||
# either zero or one component depending on whether DI is enabled in | ||
# Datadog configuration. However, if a new instance of DI::Component | ||
# is created while the previous instance is still running, we are | ||
# guaranteed to not end up with no component when one is running. | ||
def add_current_component(component) | ||
LOCK.synchronize do | ||
@current_components ||= [] | ||
@current_components << component | ||
end | ||
end | ||
|
||
def remove_current_component(component) | ||
LOCK.synchronize do | ||
@current_components&.delete(component) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand your use-case well, you can maybe replace this with something along the lines of
This allows you to access the components without triggering initialization. This is something we use in for instance
Datadog::Tracing.correlation
to allow the API to be used, but not cause initialization as a side-effect.