Skip to content

Support Spring framework integration#970

Open
zhelenskiy wants to merge 2 commits intodevelopfrom
zhelenskiy/spring
Open

Support Spring framework integration#970
zhelenskiy wants to merge 2 commits intodevelopfrom
zhelenskiy/spring

Conversation

@zhelenskiy
Copy link
Collaborator

Support the Spring framework by initiating recording from the program's startup and ending with its shutdown.
Additionally, address and resolve several minor bugs.

@zhelenskiy zhelenskiy requested a review from eupp March 1, 2026 17:41
@zhelenskiy zhelenskiy self-assigned this Mar 1, 2026
@zhelenskiy
Copy link
Collaborator Author

The problem is that such a prefix is added to every trace, so it is necessary to regenerate it in the tests.

image

val methodPool = DescriptorPool<MethodDescriptor>()
val fieldPool = DescriptorPool<FieldDescriptor>()
val variablePool = DescriptorPool<VariableDescriptor>()
private val anonymousThreadCounter = AtomicInteger(0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a separate counter for anonymous threads?
Why not just print "anonymous-thread-$id"?

Also, consider changing the thread name to "unknown-thread-$id".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To create unique names for displaying.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image Is it ok for us?

Comment on lines +398 to +400
className.startsWith("sun.rmi.") -> true
className.startsWith("javax.") -> true

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 are not spring-related.

Copy link
Collaborator Author

@zhelenskiy zhelenskiy Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, Spring classes are still shown, so none of the logic makes sense.


className.startsWith("org.apache.logging.") -> true
className.startsWith("org.apache.logging.log4j.") -> true
className.startsWith("org.apache.juli.logging.") -> true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move these to isRecognizedLoggingLibraryClass

)
(a.startsWith("set") && b.startsWith("get"))
|| (a.startsWith("get") && b.startsWith("set"))
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rollback

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unnecessary indentation change.
The original indentation keeps the same indentation for all disjuncts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it contradicts the official formatting guidelines.


// Special case: on-line setter and getter for same name can share this line
for (i in 0 ..< allMethods.size - 1) {
for (i in 0..<allMethods.size - 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rollback

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO 0<..N is harder to read, and it is a weird exception from other binary operators that are surrounded by spaces (e.g., you don't write 0<N, but 0 < N).

I know it's recommended formatting by Kotlin styleguide, but I found it inconvenient and inconsistent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, it can be converted to

Suggested change
for (i in 0..<allMethods.size - 1) {
for (i in allMethods.indices) {


if (isRecognizedUninstrumentedThirdPartyLibraryClass(className)) return true

if (isSpringRelatedClass(className)) return true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's exclude spring classes only for Trace Recorder for now.

if (isIntellijRuntimeAgentClass(className))
return true

if (isSpringRelatedClass(className))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's exclude spring classes only for Trace Recorder for now.

* Manages program-scoped trace recording for trace recorder mode.
* Recording starts at program startup and stops at shutdown via a shutdown hook.
*/
internal object ProgramScopedTraceRecorder {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just port this logic to Tracer.kt instead of adding a new class here.

I believe this logic is now shared and uniform for both Trace Recorder and Live Debugger, so it makes sense to move it there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also comment here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the answer is in this comment: #970 (comment)

Let's not start tracing for premain in case if class and method agent arguments were passed, let's keep the old behavior for this case (at least temporarily).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, should I exclude passing the class and method from the Idea plugin side?

// BinaryFileStream has a race condition where multiple threads can write descriptors
// in the wrong order due to shared ContextSavingState across threads.
val effectiveMode = when (mode) {
is TraceOutputMode.BinaryFileStream -> TraceOutputMode.BinaryFileDump()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's don't change mode silently. Moreover, binary file dump would not scale as it keeps whole trace in-memory.

If there is a but, then please create a YT issue describing the bug and a reproducer.

override val tracingEntryPointMethodVisitorProvider: TracingEntryPointMethodVisitorProvider
get() = ::TraceRecorderMethodTransformer
override val tracingEntryPointMethodVisitorProvider: TracingEntryPointMethodVisitorProvider?
get() = null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rollback.

I believe, at least for backward compatibility reasons, we want to preserve the old behavior for test-like configurations in IDEA plugin. For these configurations, the trace will be started from the test function. If you switch it to static tracing from main right now, it will bloat the trace. We need to fix this problem separately, only after that we can migrate to always starting tracing from main.

For transition period, you can add an option to javaagent arguments. Or check if class and method options were passed, and if not, start tracing from main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants