Skip to content

fix: collapse redundant stack trace lines in error logs - #1506

Open
aksingh737 wants to merge 2 commits into
dtinit:masterfrom
aksingh737:master
Open

fix: collapse redundant stack trace lines in error logs#1506
aksingh737 wants to merge 2 commits into
dtinit:masterfrom
aksingh737:master

Conversation

@aksingh737

Copy link
Copy Markdown
Collaborator

fix: collapse redundant stack trace lines in error logs
We get a lot of repeated log lines. This code change changes it so that the repeated lines are shows as a count.

For example instead of seeing something like
x
x
x
...

we see

x
repeated n times

aksingh737 and others added 2 commits July 21, 2026 21:10
We get a lot of repeated log lines. This code change changes it so that the repeated lines are shows as a count.

For example instead of seeing something like
x
x
x
...

we see

x
repeated n times

@alexeyqu alexeyqu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I understand the approach, but there are some caveats.

This PR reimplements Guava's Throwables.getStackTraceAsString(e) with a custom formatter. For a replacement like that to be safe, it needs to cover the same ground as what it's replacing:

  • Suppressed exceptions are silently dropped. ExceptionUtils.getStackTraceAsString only walks t.getCause(), never t.getSuppressed(). Guava's version delegates to Throwable.printStackTrace(), which prints Suppressed: ... sections (e.g. from try-with-resources). Was dropping that intentional, or an oversight?
  • A couple of call sites still use the old path, e.g. StackdriverMonitor.java:65 and GoogleCloudIdempotentImportExecutor.java:91 in extensions/cloud/portability-cloud-google/. Left as-is, we now have two divergent stack-trace formatters in the codebase, and this is a standing risk for future drift (both in the open-source DTP and in modules that can be added via @Inject).
  • No tests on ExceptionUtils itself -- nothing exercises the actual collapsing behavior (repeated frames, nested causes) to confirm the output is correct.

Given that, what about keeping Guava's Throwables.getStackTraceAsString(e) as-is and adding a small post-processing step that operates on the resulting string to collapse duplicate lines?
That way we inherit correct cause/suppressed/cycle handling for free from the JDK and Guava, and only own the one genuinely new piece of logic (collapsing consecutive repeats) -- which is also easy to unit test in isolation and drop into all the call sites uniformly. I think that'd be more durable long-term.

Let's discuss.

@aksingh737

aksingh737 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Those are great catches and suggestions. In regards to your question whether this was intentional or oversight - most of them were from oversight instead of being intentional.

I will refactor the code to do the below

  • Redesign ExceptionUtils.java: I will delete the custom trace-walking logic and restructure it to call Throwables.getStackTraceAsString(e) first, and then efficiently breaks it apart, collapsing consecutive matching lines, before sewing it cleanly back into a string without disrupting suppressed trace logic.

  • I will add ExceptionUtilsTest.java that tests the changes.

  • Update StackdriverMonitor.java and GoogleCloudIdempotentImportExecutor.java to use ExceptionUtils.

Please let me know if that sounds good and I will update the code accordingly.

@alexeyqu

Copy link
Copy Markdown
Collaborator

Sounds good @aksingh737.

One more thing worth checking: RetryMapping.matchesThrowableStack (line 115) also calls getStackTraceAsString today, but it feeds the result into retry-matching regexes rather than logging it.

If ExceptionUtils collapses duplicate lines there too, it could change what those regexes match against and silently affect retry behavior. Might be safest to leave that call site on the raw Guava output and only migrate the actual logging/display sites -- can you confirm that's excluded, or check whether it's safe to include?

My gut feeling is RetryMapping's regexes can differ per deployment, so it's safest to leave that call site on raw Guava output with a short comment explaining why it's intentionally excluded -- that way a future cleanup pass doesn't fold it in by accident.

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