<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Gantman</title>
    <description>The latest articles on DEV Community by Michael Gantman (@mgantman).</description>
    <link>https://dev.to/mgantman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3248011%2F37412a52-1486-41fc-829a-848f86ef94af.jpg</url>
      <title>DEV Community: Michael Gantman</title>
      <link>https://dev.to/mgantman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mgantman"/>
    <language>en</language>
    <item>
      <title>Zero-Code-Change Stacktrace Filtering for Spring Boot: An Infrastructure-Level Integration</title>
      <dc:creator>Michael Gantman</dc:creator>
      <pubDate>Wed, 22 Apr 2026 17:36:28 +0000</pubDate>
      <link>https://dev.to/mgantman/zero-code-change-stacktrace-filtering-for-spring-boot-an-infrastructure-level-integration-3fk5</link>
      <guid>https://dev.to/mgantman/zero-code-change-stacktrace-filtering-for-spring-boot-an-infrastructure-level-integration-3fk5</guid>
      <description>&lt;p&gt;This article is intended for &lt;strong&gt;software architects, senior backend engineers, and platform engineers&lt;/strong&gt; working on commercial Java systems where stacktraces are often dominated by framework and infrastructure noise. It is particularly relevant for teams using &lt;strong&gt;Spring Boot or other layered architectures&lt;/strong&gt; who want to improve diagnostic clarity &lt;strong&gt;without introducing changes to application code&lt;/strong&gt;.&lt;br&gt;
This article focuses on how to integrate the MgntUtils stacktrace filtering feature at the infrastructure level. The focus is not the filtering algorithm itself, but rather how to integrate it cleanly so it fits naturally into existing commercial systems and development workflows.&lt;/p&gt;
&lt;h3&gt;
  
  
  Brief feature Recap
&lt;/h3&gt;

&lt;p&gt;We are talking about the &lt;strong&gt;stacktrace filtering feature&lt;/strong&gt; — one of the most widely used utilities of the open-source MgntUtils library.&lt;/p&gt;

&lt;p&gt;A detailed technical deep-dive describing the algorithm and its behavior is available in my article “&lt;strong&gt;Java Stacktrace filtering utility&lt;/strong&gt;”, published on &lt;a href="https://www.linkedin.com/pulse/java-stacktrace-filtering-utility-michael-gantman-t003f/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://dzone.com/articles/filter-java-stacktrace-mgntutils" rel="noopener noreferrer"&gt;DZone&lt;/a&gt;. If you are interested in the internal mechanics, please refer to that piece. This article assumes you already know the value of the feature and want to know how to deploy it.)&lt;/p&gt;

&lt;p&gt;In short, the feature provides intelligent stacktrace filtering that removes framework-related noise (Spring, Hibernate, CGLIB proxies, reflection, servlet chains, etc.). It leaves a concise, easy-to-read stacktrace focused strictly on your application-level code, effectively eliminating the dreaded “stacktrace scroll of death” problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Integration Pain Point
&lt;/h3&gt;

&lt;p&gt;In the original article, the recommended usage pattern was to replace logging statements from:&lt;br&gt;
&lt;code&gt;logger.info("Message", e);&lt;/code&gt;&lt;br&gt;
to &lt;br&gt;
&lt;code&gt;logger.info("Message" + TextUtils.getStacktrace(e));&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;However, when a senior architect responsible for platform quality and long-term maintainability sees a recommendation like this, it immediately raises two major NO-GO flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Massive Refactoring Effort:&lt;/strong&gt; Replacing every logging statement across an existing commercial codebase is expensive, risky, and nearly impossible to justify to management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing Maintainability Cost:&lt;/strong&gt; Developers would need to remember to always use this special logging pattern. Inevitably, some log statements would be written incorrectly, leading to inconsistencies and reducing the value of the solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of these concerns, even a highly useful feature is often rejected purely due to integration friction.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Goal of This Article
&lt;/h3&gt;

&lt;p&gt;This article demonstrates an integration approach that resolves these concerns by moving the feature integration to the infrastructure level.&lt;/p&gt;

&lt;p&gt;The proposed solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires &lt;strong&gt;ZERO&lt;/strong&gt; changes to your existing application code.&lt;/li&gt;
&lt;li&gt;Preserves standard logging syntax (developers just log exactly as they always have).&lt;/li&gt;
&lt;li&gt;Introduces no additional cognitive burden on your team.&lt;/li&gt;
&lt;li&gt;Remains fully transparent to the application layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a bonus, we will also introduce a hot runtime toggle, allowing Admin or SRE users to enable or disable the filtering dynamically at runtime - without a redeployment or server restart.&lt;/p&gt;

&lt;p&gt;An integration example is demonstrated in the &lt;strong&gt;MgntUtilsUsage&lt;/strong&gt; project—a working, out-of-the-box Spring Boot Application with this full infrastructure integration implemented. The project is available as a &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;public GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Integration Steps
&lt;/h3&gt;

&lt;p&gt;For the purposes of this article, we will assume the commercial project is using either Logback (which is the default logging implementation for Spring Boot) or Log4j2. For both of those logging implementations, the proposed integration is roughly the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; Since the stacktrace filtering feature is provided by the MgntUtils library, you will first need to include this library in your project. Here are the Maven and Gradle coordinates:&lt;/p&gt;

&lt;p&gt;Maven:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.github.michaelgantman&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;MgntUtils&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.7.0.7&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"com.github.michaelgantman:MgntUtils:1.7.0.7"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Version 1.7.0.7 is the latest version at the time of writing. To ensure you have the latest release, please check the &lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;MgntUtils page on Maven Central&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write a Java class&lt;/strong&gt; that will handle any incoming exception using the MgntUtils stacktrace filtering utility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register this class&lt;/strong&gt; with the logging framework to act as a custom exception handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialize the relevant package prefixes&lt;/strong&gt; for the MgntUtils stacktrace filtering utility, so it knows which packages should be considered important (which controls how the stacktrace filtering is done).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Integration Steps for Logback
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Write the Logback Interceptor (The "Magic" Engine)
&lt;/h4&gt;

&lt;p&gt;Logback controls how exceptions are formatted via converters. We will create a custom converter that intercepts the exception, strips out the framework noise using MgntUtils, and returns the clean string.&lt;/p&gt;

&lt;p&gt;Create this class in your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.stamboot.config.logging&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mgnt.utils.TextUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.pattern.ThrowableProxyConverter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.spi.ILoggingEvent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.spi.IThrowableProxy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.spi.ThrowableProxy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Global Logback interceptor to filter all stack traces using MgntUtils.
 */&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ThrowableProxyConverter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ILoggingEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="nc"&gt;IThrowableProxy&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrowableProxy&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;

       &lt;span class="c1"&gt;// Extract the raw java.lang.Throwable from Logback's proxy object&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ThrowableProxy&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;rawException&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;ThrowableProxy&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getThrowable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
          &lt;span class="c1"&gt;//remove first new line and add new line at the end&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;TextUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStacktrace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;

       &lt;span class="c1"&gt;// Fallback: If the exception is serialized over a network,&lt;/span&gt;
       &lt;span class="c1"&gt;// fallback to standard Logback formatting.&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isCutTheBS&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setCutTheBS&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cutTheBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we added two methods here: &lt;code&gt;isCutTheBS()&lt;/code&gt; and &lt;code&gt;setCutTheBS()&lt;/code&gt;. Those methods are not required by the &lt;code&gt;ThrowableProxyConverter&lt;/code&gt; interface, but they will be used for a hot switch that will allow us to programmatically turn the filtering feature on and off without restarting the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Wire it into logback.xml (or logback-spring.xml)
&lt;/h4&gt;

&lt;p&gt;If you are starting a &lt;strong&gt;new project&lt;/strong&gt; (and not integrating this feature into an existing one), you just need to tell Logback to replace its default exception printing logic (usually denoted by &lt;code&gt;%ex&lt;/code&gt;) with your new custom converter. Create a new file named &lt;strong&gt;logback.xml&lt;/strong&gt; and place it in your classpath (if it is a Spring Boot project, place it in your &lt;strong&gt;src/main/resources&lt;/strong&gt; folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;conversionRule&lt;/span&gt; &lt;span class="na"&gt;conversionWord=&lt;/span&gt;&lt;span class="s"&gt;"ex"&lt;/span&gt; 
          &lt;span class="na"&gt;converterClass=&lt;/span&gt;&lt;span class="s"&gt;"com.example.stamboot.config.logging.MgntStackTraceConverter"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;appender&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"CONSOLE"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.ConsoleAppender"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;root&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"INFO"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"CONSOLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are integrating this feature into an &lt;strong&gt;existing project&lt;/strong&gt;, you most likely already have your Logback configuration in a &lt;strong&gt;logback.xml&lt;/strong&gt; or &lt;strong&gt;logback-spring.xml&lt;/strong&gt; file. In this case, you will need to do two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Add the &lt;code&gt;&amp;lt;conversionRule&amp;gt;&lt;/code&gt;&lt;/strong&gt; &lt;br&gt;
You must add this line near the top of the &lt;code&gt;&amp;lt;configuration&amp;gt;&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;conversionRule&lt;/span&gt; &lt;span class="na"&gt;conversionWord=&lt;/span&gt;&lt;span class="s"&gt;"ex"&lt;/span&gt; 
&lt;span class="na"&gt;converterClass=&lt;/span&gt;&lt;span class="s"&gt;"com.example.stamboot.config.logging.MgntStackTraceConverter"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;B. Update the &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; for all Appenders (The Invocation)&lt;/strong&gt; &lt;br&gt;
Just because you taught Logback the word &lt;code&gt;%ex&lt;/code&gt; doesn't mean the appenders are actually using it. You must check the &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; strings inside your existing &lt;code&gt;&amp;lt;appender&amp;gt;&lt;/code&gt; blocks and make sure they invoke your token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Spring Boot Gotcha:&lt;/strong&gt; By default, standard Logback uses &lt;code&gt;%ex&lt;/code&gt; to print exceptions. But if a project is using Spring Boot, developers often copy-paste Spring's default patterns, which use &lt;code&gt;%wEx&lt;/code&gt; (Whitespace Throwable Proxy) or &lt;code&gt;%xEx&lt;/code&gt; (Extended Throwable Proxy).&lt;/p&gt;

&lt;p&gt;If an existing appender looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd} [%thread] %-5level %logger{36} - %msg%n%wEx&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your filter will not run, because Logback is looking for the &lt;code&gt;%wEx&lt;/code&gt; logic, not your &lt;code&gt;%ex&lt;/code&gt; logic! They must change the end of their pattern to use the exact &lt;code&gt;conversionWord&lt;/code&gt; you defined in step 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd} [%thread] %-5level %logger{36} - %msg%n%ex&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Configure your relevant packages
&lt;/h4&gt;

&lt;p&gt;You will need to invoke the method &lt;code&gt;TextUtils.setRelevantPackage("com.example.stamboot.")&lt;/code&gt; somewhere in your project initialization to specify which packages should be considered important.&lt;/p&gt;

&lt;p&gt;Note that this method supports multiple prefixes. In my example, I used a single prefix ("com.example.stamboot.") since I took it from the &lt;strong&gt;MgntUtilsUsage&lt;/strong&gt; project - a working, out-of-the-box Spring Boot Application with this full infrastructure integration implemented. In that repo, all my code resides in packages that start with that prefix. You will need to set the correct prefix (or multiple prefixes) for your specific project.&lt;/p&gt;

&lt;p&gt;For a detailed discussion of this mechanism, see the &lt;a href="https://www.linkedin.com/pulse/java-stacktrace-filtering-utility-michael-gantman-t003f/" rel="noopener noreferrer"&gt;Java Stacktrace filtering utility&lt;/a&gt; article I mentioned above, as well as the Javadoc for the &lt;a href="https://michaelgantman.github.io/Mgnt/docs/com/mgnt/utils/TextUtils.html#getStacktrace-java.lang.Throwable-boolean-" rel="noopener noreferrer"&gt;getStacktrace()&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; If your project is a Spring Boot project, we will discuss exactly where to place this initialization code for maximum efficiency at the end of the article.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Steps for Log4J2
&lt;/h3&gt;

&lt;p&gt;To replace the default exception formatter in Log4j2 without XML, you just write the converter and annotate it. At compile time, Log4j2 finds it and wires it up automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Write the Log4j2 Plugin (The Converter)
&lt;/h4&gt;

&lt;p&gt;Instead of implementing an interface, you extend &lt;code&gt;ThrowablePatternConverter&lt;/code&gt; and tag it with Log4j2's &lt;code&gt;@Plugin&lt;/code&gt; and &lt;code&gt;@ConverterKeys&lt;/code&gt; annotations.&lt;/p&gt;

&lt;p&gt;By taking over the keys &lt;code&gt;{"ex", "throwable", "exception"}&lt;/code&gt;, you are telling Log4j2: "Anytime a layout pattern asks for an exception, use me instead of the default."&lt;/p&gt;

&lt;p&gt;So your class for Log4J2 option will look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.stamboot.config.logging&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mgnt.utils.TextUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.LogEvent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.config.Configuration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.config.plugins.Plugin&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.pattern.ConverterKeys&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.pattern.PatternConverter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.logging.log4j.core.pattern.ThrowablePatternConverter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Global Log4j2 interceptor to filter stack traces using MgntUtils.
 */&lt;/span&gt;
&lt;span class="nd"&gt;@Plugin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MgntStackTraceConverter"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PatternConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CATEGORY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ConverterKeys&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="s"&gt;"ex"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"throwable"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"exception"&lt;/span&gt; &lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Overrides default tokens&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ThrowablePatternConverter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Log4j2 plugins REQUIRE a static newInstance method to be instantiated!&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt; &lt;span class="nf"&gt;newInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MgntThrowable"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"throwable"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;LogEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;toAppendTo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;rawException&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawException&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Route the exception through MgntUtils!&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;cleanTrace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStacktrace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;//remove first new line and add new line at the end&lt;/span&gt;
            &lt;span class="n"&gt;toAppendTo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleanTrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isCutTheBS&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setCutTheBS&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cutTheBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Wire it into log4j2.xml (or log4j2-spring.xml)
&lt;/h4&gt;

&lt;p&gt;If you are starting a &lt;strong&gt;new project&lt;/strong&gt; with Log4j2, you will create a new &lt;strong&gt;log4j2.xml&lt;/strong&gt; file in your &lt;strong&gt;src/main/resources&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;Because your custom converter uses the &lt;code&gt;@Plugin&lt;/code&gt; and &lt;code&gt;@ConverterKeys&lt;/code&gt; annotations, Log4j2 absorbs the logic natively. You do not need a complex &lt;code&gt;&amp;lt;conversionRule&amp;gt;&lt;/code&gt; mapping like Logback. You simply need to provide the &lt;code&gt;packages&lt;/code&gt; attribute in the root configuration tag so Log4j2 knows where to scan for your class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Configuration&lt;/span&gt; &lt;span class="na"&gt;status=&lt;/span&gt;&lt;span class="s"&gt;"WARN"&lt;/span&gt; &lt;span class="na"&gt;packages=&lt;/span&gt;&lt;span class="s"&gt;"com.example.stamboot.config.logging"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Appenders&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Console&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Console"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"SYSTEM_OUT"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;PatternLayout&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n%ex&lt;span class="nt"&gt;&amp;lt;/Pattern&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/PatternLayout&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Console&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Appenders&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Loggers&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Root&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;AppenderRef&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"Console"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Root&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Loggers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are integrating this feature into an &lt;strong&gt;existing project&lt;/strong&gt;, you most likely already have a &lt;strong&gt;log4j2.xml&lt;/strong&gt; (or &lt;strong&gt;log4j2-spring.xml&lt;/strong&gt;) file. In this case, you only need to verify two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Add the &lt;code&gt;packages&lt;/code&gt; attribute to the root tag&lt;/strong&gt; &lt;br&gt;
Find your existing &lt;code&gt;&amp;lt;Configuration&amp;gt;&lt;/code&gt; tag at the very top of the file and add your package path to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Configuration&lt;/span&gt; &lt;span class="na"&gt;status=&lt;/span&gt;&lt;span class="s"&gt;"WARN"&lt;/span&gt; &lt;span class="na"&gt;packages=&lt;/span&gt;&lt;span class="s"&gt;"com.example.stamboot.config.logging"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;B. Update the &lt;code&gt;&amp;lt;Pattern&amp;gt;&lt;/code&gt; for all Appenders&lt;/strong&gt; &lt;br&gt;
Just like with Logback, you must check your existing &lt;code&gt;&amp;lt;Pattern&amp;gt;&lt;/code&gt; strings. If your project is a Spring Boot application, developers may have copied Spring's default patterns which use &lt;code&gt;%wEx&lt;/code&gt; or &lt;code&gt;%xEx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If an existing appender looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n%wEx&lt;span class="nt"&gt;&amp;lt;/Pattern&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your filter will not trigger! You must change the end of the pattern to use &lt;code&gt;%ex&lt;/code&gt; (or &lt;code&gt;%throwable&lt;/code&gt; or &lt;code&gt;%exception&lt;/code&gt;), which match the &lt;code&gt;@ConverterKey&lt;/code&gt;s defined in your Java class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n%ex&lt;span class="nt"&gt;&amp;lt;/Pattern&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Configure relevant packages
&lt;/h4&gt;

&lt;p&gt;This step is the same regardless of whether you use Logback, Log4j2, or any other logging framework in your project. You will need to invoke the method &lt;code&gt;TextUtils.setRelevantPackage("com.example.stamboot.")&lt;/code&gt; somewhere in your project initialization to specify which packages should be considered important. Earlier in the article, we promised to discuss in detail where to place this initialization code if your project is Spring Boot-based. So, as promised, here is the recommendation:&lt;/p&gt;

&lt;p&gt;If you are using a modern IoC framework like Spring Boot, you might be tempted to put this initialization inside a Spring component, perhaps using an &lt;code&gt;@EventListener(ContextRefreshedEvent.class)&lt;/code&gt; or a &lt;code&gt;@PostConstruct&lt;/code&gt; method. &lt;strong&gt;Do NOT do this&lt;/strong&gt;. If you rely on the Spring lifecycle to initialize your stacktrace filter, you create a massive logging blind spot during application startup. In Spring Boot, application startup isn't just server configuration - it is exactly when the framework uses reflection to instantiate and execute your code. Consider the two most common (and painful) startup crashes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;@PostConstruct&lt;/code&gt; Crash:&lt;/strong&gt; Imagine a developer writes a service that pre-loads cache data from the database the moment the bean is created. If &lt;code&gt;loadData()&lt;/code&gt; throws a &lt;code&gt;NullPointerException&lt;/code&gt; or &lt;code&gt;BadSqlGrammarException&lt;/code&gt;, the crash happens before the application context is fully ready. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Bad &lt;code&gt;@Configuration&lt;/code&gt; Bean:&lt;/strong&gt; If a configuration class manually instantiates a bean and throws an exception (e.g., trying to read a missing file or parse a bad JWT secret), Spring wraps your exception in a massive &lt;code&gt;BeanCreationException&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your prefixes are not set yet because you are waiting for an &lt;code&gt;ApplicationReadyEvent&lt;/code&gt;, you will get the dreaded 200-line "Scroll of Death" consisting almost entirely of &lt;code&gt;org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory&lt;/code&gt; noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution: The &lt;code&gt;main()&lt;/code&gt; method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To guarantee your filter is already configured and armed during the most chaotic part of the application lifecycle, you must move the initialization entirely outside of the Spring context. Place it in your pure Java &lt;code&gt;main()&lt;/code&gt; method, right before &lt;code&gt;SpringApplication.run(...)&lt;/code&gt; is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.stamboot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.SpringApplication&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.autoconfigure.SpringBootApplication&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mgnt.utils.TextUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StamBootApplication&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Arm the stacktrace filter FIRST&lt;/span&gt;
        &lt;span class="nc"&gt;TextUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRelevantPackage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.example.stamboot."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Start the Spring machinery&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StamBootApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, MgntUtils will see your application packages on the stack during a startup crash, filter out the 190 lines of Spring Boot factory noise, and point you exactly to the line in your &lt;code&gt;@PostConstruct&lt;/code&gt; or &lt;code&gt;@Configuration&lt;/code&gt; class that broke the deployment. &lt;/p&gt;

&lt;p&gt;On the other hand MgntUtils filtering feature has a built-in safety measure: if your entire stacktrace does NOT contain any lines that match your configured prefixes, the stacktrace is printed in full and not filtered at all. Because of this fail-safe, you do not risk losing important diagnostic information if a crash occurs purely within the framework and does not involve your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus Feature: Hot enable/disable filtering switch
&lt;/h3&gt;

&lt;p&gt;Earlier in the article we promised a hot runtime toggle, allowing Admin or SRE users to enable or disable the filtering dynamically at runtime - without a redeployment or server restart. So, here it is. &lt;/p&gt;

&lt;p&gt;If you look at our &lt;strong&gt;MgntStackTraceConverter&lt;/strong&gt; class implementation from earlier, we introduced two seemingly redundant methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isCutTheBS&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cutTheBS&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setCutTheBS&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cutTheBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cutTheBs&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These methods are the getter and setter for the &lt;code&gt;cutTheBS&lt;/code&gt; flag which in our &lt;strong&gt;MgntStackTraceConverter&lt;/strong&gt; implementation controls whether the stacktrace is filtered or not. These methods are our hooks. All we need now is to provide some user interface that will allow user to control this flag. &lt;/p&gt;

&lt;p&gt;In our working Spring Boot example (mentioned earlier and discussed below) we provide a REST controller that gives the user two APIs to see the current value of the flag and to change its setting.&lt;/p&gt;

&lt;p&gt;Here is the proposed controller implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.stamboot.config.logging&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.http.ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.GetMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RequestMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RequestParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RestController&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config/log/stacktrace"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggerConfigController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setFilteringFlag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCutTheBS&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;"Stacktrace filtering flag is set to: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCutTheBS&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/filter/status"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getFilteringFlag&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;"Stacktrace filtering flag current value is: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;MgntStackTraceConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCutTheBS&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By calling the provided URLs user can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the current status: &lt;a href="http://localhost:8080/config/log/stacktrace/filter/status" rel="noopener noreferrer"&gt;http://localhost:8080/config/log/stacktrace/filter/status&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Set the status to true or false: &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://localhost:8080/config/log/stacktrace?filter=true" rel="noopener noreferrer"&gt;http://localhost:8080/config/log/stacktrace?filter=true&lt;/a&gt;&lt;br&gt;
&lt;a href="http://localhost:8080/config/log/stacktrace?filter=false" rel="noopener noreferrer"&gt;http://localhost:8080/config/log/stacktrace?filter=false&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Using &lt;code&gt;@GetMapping&lt;/code&gt; here is a deliberate trade-off. By definition, GET must be safe (no side effects) and idempotent — it should never mutate state. Using it here is a clear violation of HTTP semantics, and in a real commercial implementation this endpoint should use &lt;code&gt;@PutMapping&lt;/code&gt; or &lt;code&gt;@PostMapping&lt;/code&gt;. However, for the purposes of this demo, GET allows you to trigger the toggle directly from a browser URL bar with no REST client required. The point this demo is meant to illustrate is the mechanism — that you can expose a runtime configuration toggle with no restart or redeployment — and not the choice of HTTP method.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;In our simple demo, these APIs are open to all. In a real-life commercial application, appropriate security measures (such as Spring Security) should be applied to restrict these endpoints to Admin and SRE users only.&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Repository with working example
&lt;/h3&gt;

&lt;p&gt;Here is the link to the repository with an out-of-the-box working example: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This repo contains a Spring Boot application that uses Logback for logging functionality, which is the Spring Boot default. We recommend you clone the repository and actually run the example. But regardless, let's look at the relevant parts: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Converter:&lt;/strong&gt; The &lt;strong&gt;MgntStackTraceConverter&lt;/strong&gt; class that contains the actual custom filtering logic (and shown above in this article) is located here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/config/logging/MgntStackTraceConverter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/config/logging/MgntStackTraceConverter.java&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Configuration:&lt;/strong&gt; The &lt;strong&gt;logback.xml&lt;/strong&gt; is here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/resources/logback.xml" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/resources/logback.xml&lt;/a&gt;. This configuration tells Logback to use custom logic for printing stacktraces provided in &lt;strong&gt;MgntStackTraceConverter&lt;/strong&gt; instead of its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Toggle API:&lt;/strong&gt; The &lt;strong&gt;LoggerConfigController&lt;/strong&gt; that provides hot on/off toggle for the feature (also shown above) is here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/config/logging/LoggerConfigController.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/config/logging/LoggerConfigController.java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Initialization:&lt;/strong&gt; Setting the relevant package prefix can be seen in &lt;strong&gt;StamBootApplication&lt;/strong&gt; (line 12) here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/StamBootApplication.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/StamBootApplication.java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Demo Endpoint:&lt;/strong&gt; And finally, the most interesting part - the controller that actually demonstrates the log output with filtered/unfiltered stacktrace (depending on the current toggle state) is here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LogFilteringDemoController.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LogFilteringDemoController.java&lt;/a&gt;. See method &lt;code&gt;logEndPoint()&lt;/code&gt;. Note that this method is exposed by API &lt;a href="http://localhost:8080/log" rel="noopener noreferrer"&gt;http://localhost:8080/log&lt;/a&gt;. The method throws an exception with 50% probability and returns a text message notifying the user if an exception was or was not thrown.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to run the example
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repo from &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;, open it in your IDE, and run it.&lt;/li&gt;
&lt;li&gt;Once it runs, hit the URL &lt;a href="http://localhost:8080/config/log/stacktrace/filter/status" rel="noopener noreferrer"&gt;http://localhost:8080/config/log/stacktrace/filter/status&lt;/a&gt; to check the current status of the toggle (It is true by default). &lt;/li&gt;
&lt;li&gt;Hit the URL &lt;a href="http://localhost:8080/log" rel="noopener noreferrer"&gt;http://localhost:8080/log&lt;/a&gt;. This URL causes a backend exception with 50% probability. It will either return a message "&lt;strong&gt;[Current Timestamp]: No Exception&lt;/strong&gt;" or "&lt;strong&gt;[Current Timestamp]: Exception occurred, check the logs&lt;/strong&gt;".&lt;/li&gt;
&lt;li&gt;If you got "No Exception," re-run the API (refresh your browser) until you get the message "Exception occurred, check the logs". Go to your IDE and see in the console that the exception is indeed printed by the logger and it is cleanly filtered. You will see the following output:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  c.e.s.c.LogFilteringDemoController - Raw Exception:
java.lang.Exception: Demo Exception
at com.example.stamboot.controller.LogFilteringDemoController.callHandler(LogFilteringDemoController.java:49)
at com.example.stamboot.controller.LogFilteringDemoController.logEndPoint(LogFilteringDemoController.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now run the API &lt;a href="http://localhost:8080/config/log/stacktrace?filter=false" rel="noopener noreferrer"&gt;http://localhost:8080/config/log/stacktrace?filter=false&lt;/a&gt; to switch off the filtering.&lt;/li&gt;
&lt;li&gt;Run &lt;a href="http://localhost:8080/log" rel="noopener noreferrer"&gt;http://localhost:8080/log&lt;/a&gt; again until you get "Exception occurred, check the logs" message. Go back to your IDE and see that the output now is the full "Scroll of Death"
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  c.e.s.c.LogFilteringDemoController - Raw Exception:
java.lang.Exception: Demo Exception
at com.example.stamboot.controller.LogFilteringDemoController.callHandler(LogFilteringDemoController.java:49)
at com.example.stamboot.controller.LogFilteringDemoController.logEndPoint(LogFilteringDemoController.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:750)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As demonstrated in this article, integrating the MgntUtils stacktrace filtering feature at the infrastructure level requires a small one-time effort and results in clean, focused stacktraces across your entire project — without any changes to your application code. Developers on your team will not need to learn any new logging patterns or conventions. The hot runtime toggle provides additional control for Admin and SRE users to manage the feature at runtime without a redeployment. The MgntUtilsUsage repository linked throughout this article contains a fully working example of all of this implemented out of the box.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Infrastructure for Extensible Multi-Stage Workflows Across Multiple Data Types</title>
      <dc:creator>Michael Gantman</dc:creator>
      <pubDate>Mon, 12 Jan 2026 23:45:11 +0000</pubDate>
      <link>https://dev.to/mgantman/infrastructure-for-extensible-multi-stage-workflows-across-multiple-data-types-79k</link>
      <guid>https://dev.to/mgantman/infrastructure-for-extensible-multi-stage-workflows-across-multiple-data-types-79k</guid>
      <description>&lt;h3 id="bf15"&gt;Introduction and Purpose of This Article&lt;/h3&gt;
&lt;p id="5f54"&gt;This is an in-depth technical article for Java developers. It presents a lightweight yet powerful architectural approach for implementing &lt;strong&gt;extensible, multi-stage processing workflows&lt;/strong&gt; that &lt;strong&gt;support multiple data types&lt;/strong&gt;, &lt;strong&gt;multiple processing stages&lt;/strong&gt;, and &lt;strong&gt;arbitrary future extensibility&lt;/strong&gt; — all &lt;strong&gt;without modifying existing code&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="8d7c"&gt;Before diving into the infrastructure, let’s first clearly define the problem using a simple, generic example.&lt;/p&gt;
&lt;h3 id="edeb"&gt;What Is a Multi-Stage Processing Workflow for Multiple Data Types?&lt;/h3&gt;
&lt;p id="e2c6"&gt;Assume you have input data files that need to be processed through several consecutive steps. For this example, let’s define the workflow stages as:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="8bd2"&gt;Pre-Processing&lt;/li&gt;
&lt;li id="153b"&gt;Processing&lt;/li&gt;
&lt;li id="1c1b"&gt;Post-Processing&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="8d96"&gt;This sequence forms a &lt;strong&gt;workflow&lt;/strong&gt;, where each stage performs its own transformation.&lt;/p&gt;
&lt;p id="e17c"&gt;Now assume that your system must support &lt;strong&gt;multiple data types&lt;/strong&gt;, where:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="aa1f"&gt;Each data type goes through the &lt;strong&gt;same sequence of stages&lt;/strong&gt;, but&lt;/li&gt;
&lt;li id="2756"&gt;Each data type requires &lt;strong&gt;its own type-specific implementation&lt;/strong&gt; for each stage&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="22fa"&gt;For example, suppose your input files may arrive in &lt;strong&gt;JSON &lt;/strong&gt;or &lt;strong&gt;XML &lt;/strong&gt;format. However:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="3a08"&gt;The system must be able to support &lt;strong&gt;an extension to an arbitrary number of formats&lt;/strong&gt; in the future&lt;/li&gt;
&lt;li id="9070"&gt;The system must also be able to extend or reorder the &lt;strong&gt;number of workflow stages&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="d639"&gt;This is precisely what the expression “&lt;strong&gt;multi-stage processing workflow for multiple data types&lt;/strong&gt;”&lt;em&gt; &lt;/em&gt;refers to.&lt;/p&gt;
&lt;p id="139e"&gt;Although JSON and XML are used here as simple illustrative examples, the pattern generalizes to virtually any domain. Some representative examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="c935"&gt;Image formats (BMP, JPEG, PNG)&lt;/li&gt;
&lt;li id="fa88"&gt;Sensor telemetry formats&lt;/li&gt;
&lt;li id="4c08"&gt;Message and protocol formats&lt;/li&gt;
&lt;li id="f5ad"&gt;Validation pipelines&lt;/li&gt;
&lt;li id="67d9"&gt;ETL stages&lt;/li&gt;
&lt;li id="5a90"&gt;Document generation&lt;/li&gt;
&lt;li id="000f"&gt;Many other processing domains&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="613e"&gt;The key properties of this pattern are:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="8d6a"&gt;
&lt;strong&gt;The number of stages is not fixed&lt;/strong&gt; — the workflow may contain any number of steps.&lt;/li&gt;
&lt;li id="6e10"&gt;
&lt;strong&gt;The number of supported data types is not fixed&lt;/strong&gt; — and adding a new type should &lt;strong&gt;NOT &lt;/strong&gt;require any changes to existing code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="3272"&gt;This is where the proposed solution becomes essential.&lt;/p&gt;
&lt;h3 id="e7da"&gt;Overall Solution and Demo Description&lt;/h3&gt;
&lt;p id="ed8d"&gt;The solution described in this article is based on the &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt; provided by the open-source &lt;em&gt;MgntUtils &lt;/em&gt;library, and demonstrated by a real-world example in the &lt;em&gt;MgntUtilsUsage &lt;/em&gt;repository.&lt;/p&gt;
&lt;p id="7295"&gt;&lt;em&gt;MgntUtils&lt;/em&gt; Resources&lt;/p&gt;
&lt;ul&gt;
&lt;li id="b0c8"&gt;GitHub: &lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt&lt;/a&gt;
&lt;/li&gt;
&lt;li id="a4ae"&gt;Maven Central: &lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils&lt;/a&gt;
&lt;/li&gt;
&lt;li id="f714"&gt;Javadoc: &lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;https://michaelgantman.github.io/Mgnt/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="f4f6"&gt;&lt;em&gt;MgntUtilsUsage &lt;/em&gt;Demo Repository&lt;/p&gt;
&lt;ul&gt;&lt;li id="98ac"&gt;
&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;p id="9089"&gt;This architectural approach emphasizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="12aa"&gt;
&lt;strong&gt;Zero-configuration extensibility&lt;/strong&gt; (The system is completely data-type-agnostic. New types are added simply by providing type-specific implementations for each stage — &lt;strong&gt;with no additional configuration or registration&lt;/strong&gt;.)&lt;/li&gt;
&lt;li id="1c9b"&gt;&lt;strong&gt;Automatic component discovery&lt;/strong&gt;&lt;/li&gt;
&lt;li id="2b30"&gt;
&lt;strong&gt;Clean separation of responsibilities&lt;/strong&gt; (Workflow orchestration is never aware of type-specific implementation details.)&lt;/li&gt;
&lt;li id="383c"&gt;&lt;strong&gt;Elimination of switch statements, large registries, and manual wiring&lt;/strong&gt;&lt;/li&gt;
&lt;li id="f03f"&gt;
&lt;strong&gt;Support for arbitrarily complex workflows&lt;/strong&gt; (Stages can be added, removed, or reordered.)&lt;/li&gt;
&lt;li id="70ea"&gt;
&lt;strong&gt;Strong error handling with discoverable valid types&lt;/strong&gt; (This is demonstrated in the demo. Although not an inherent part of the pattern itself, it is included in the demo to keep it real-world like.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ab0f"&gt;What is self-populating factory infrastructure?&lt;/h3&gt;
&lt;p id="2c05"&gt;Before diving into the solution details, it is important to briefly explain what a &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt; is, since this concept is the cornerstone of the proposed solution. At its core, this idea is an extension of the classic Factory pattern. In the traditional &lt;strong&gt;Factory pattern&lt;/strong&gt;, you typically have:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="ad66"&gt;An &lt;strong&gt;interface&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="b04c"&gt;Several &lt;strong&gt;concrete implementations&lt;/strong&gt; of that interface&lt;/li&gt;
&lt;li id="9a55"&gt;A &lt;strong&gt;factory &lt;/strong&gt;that returns a concrete implementation based on some key&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="570e"&gt;While this pattern is well-known and widely used, it usually requires &lt;strong&gt;explicit registration&lt;/strong&gt; of implementations inside the factory — often through configuration files, static initialization blocks, or manual wiring.&lt;/p&gt;
&lt;h3 id="f20a"&gt;The Self-Populating Factory Idea&lt;/h3&gt;
&lt;p id="7631"&gt;The key idea behind a &lt;strong&gt;self-populating factory&lt;/strong&gt; is that &lt;strong&gt;each concrete implementation is factory-aware&lt;/strong&gt; and is responsible for &lt;strong&gt;registering itself with the factory when it is instantiated&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="d880"&gt;In other words:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="17ff"&gt;The factory does &lt;strong&gt;not &lt;/strong&gt;need to know about concrete implementations&lt;/li&gt;
&lt;li id="5b7c"&gt;Concrete implementations insert themselves into the factory automatically&lt;/li&gt;
&lt;li id="bb0c"&gt;Factory population happens implicitly, not through external configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="e340"&gt;This behavior is exactly what gives the pattern its name — &lt;strong&gt;self-populating factory&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="7a4a"&gt;How MgntUtils Implements This Infrastructure&lt;/h3&gt;
&lt;p id="af01"&gt;The factory-awareness and automatic self-registration behavior are provided by the &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt; in the &lt;em&gt;MgntUtils &lt;/em&gt;library, located in the package:&lt;/p&gt;
&lt;p id="9e12"&gt;&lt;em&gt;com.mgnt.lyfecycle.management&lt;/em&gt;&lt;/p&gt;
&lt;p id="9d59"&gt;Package-level Javadoc: &lt;a href="https://michaelgantman.github.io/Mgnt/docs/com/mgnt/lifecycle/management/package-summary.html" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://michaelgantman.github.io/Mgnt/docs/com/mgnt/lifecycle/management/package-summary.html" rel="noopener noreferrer"&gt;https://michaelgantman.github.io/Mgnt/docs/com/mgnt/lifecycle/management/package-summary.html&lt;/a&gt;&lt;/p&gt;
&lt;p id="754f"&gt;This package contains only two core classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="eba7"&gt;
&lt;strong&gt;BaseEntityFactory &lt;/strong&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntityFactory.java" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntityFactory.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntityFactory.java&lt;/a&gt;
&lt;/li&gt;
&lt;li id="5642"&gt;
&lt;strong&gt;BaseEntity &lt;/strong&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntity.java" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntity.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/BaseEntity.java&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="74da"&gt;To use this infrastructure, you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="424b"&gt;Create a &lt;strong&gt;factory class&lt;/strong&gt; that extends &lt;strong&gt;BaseEntityFactory&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="0896"&gt;Ensure that all concrete implementations extend &lt;strong&gt;BaseEntity&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="f900"&gt;Since concrete implementations must contain some mandatory infrastructure-related code, it is recommended to:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="721a"&gt;Create an interface that will define what your implementations must do — a method signature (or a set of method signatures)&lt;/li&gt;
&lt;li id="d232"&gt;Create a &lt;strong&gt;single abstract base implementation&lt;/strong&gt; that implements your interface&lt;/li&gt;
&lt;li id="3326"&gt;Place all mandatory infrastructure code into that abstract base class&lt;/li&gt;
&lt;li id="ae64"&gt;Have all concrete implementations extend that abstract base class&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="c26e"&gt;Once this structure is in place, &lt;strong&gt;each concrete implementation instance automatically registers itself with its factory when its constructor is invoked.&lt;/strong&gt;&lt;/p&gt;
&lt;p id="1443"&gt;As a result:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="97d6"&gt;You never need to explicitly populate the factory&lt;/li&gt;
&lt;li id="9ebe"&gt;You do not need to worry about when or how the factory is populated&lt;/li&gt;
&lt;li id="5902"&gt;Factory population happens transparently and consistently&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="0181"&gt;After the concrete implementations are instantiated, you can retrieve them from the factory &lt;strong&gt;anywhere in your code&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="cdf1"&gt;Factory Key Options (A Crucial Design Point)&lt;/h3&gt;
&lt;p id="f5fd"&gt;There are two ways a concrete implementation can be registered in a factory:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="77f1"&gt;&lt;strong&gt;Using the concrete class name as the factory key&lt;/strong&gt;&lt;/li&gt;
&lt;li id="bca3"&gt;&lt;strong&gt;Using a custom, user-defined key&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="4f52"&gt;While the second option might initially appear as an insignificant feature, it is absolutely critical for the workflow design presented later in this article.&lt;/p&gt;
&lt;p id="417a"&gt;The ability to register implementations under &lt;strong&gt;custom keys&lt;/strong&gt;, rather than class names, enables multiple different factories to share the same set of keys (much like the namespace pattern). This is the foundation that enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="73aa"&gt;Clean multi-stage workflows&lt;/li&gt;
&lt;li id="d97f"&gt;Consistent type-based routing across multiple factories&lt;/li&gt;
&lt;li id="1318"&gt;Strong decoupling between workflow orchestration and implementation details&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="2ea8"&gt;This point will become especially important when we transition from the simple XML/JSON example to the full &lt;strong&gt;Letter Formatting workflow&lt;/strong&gt; in the &lt;em&gt;MgntUtilsUsage &lt;/em&gt;repository.&lt;/p&gt;
&lt;h3 id="2a32"&gt;Runnable Example in MgntUtils&lt;/h3&gt;
&lt;p id="db7e"&gt;The &lt;em&gt;MgntUtils &lt;/em&gt;library contains a clear, runnable example that demonstrates the self-populating factory mechanism. It can be found in the package:&lt;/p&gt;
&lt;p id="3234"&gt;&lt;em&gt;com.mgnt.lifecycle.management.example&lt;/em&gt;&lt;/p&gt;
&lt;p id="0476"&gt;Example package git repo link: &lt;a href="https://github.com/michaelgantman/Mgnt/tree/master/src/main/java/com/mgnt/lifecycle/management/example" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/tree/master/src/main/java/com/mgnt/lifecycle/management/example" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/tree/master/src/main/java/com/mgnt/lifecycle/management/example&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="9da7"&gt;Example Walkthrough&lt;/h3&gt;
&lt;p id="584a"&gt;The example defines an interface called &lt;strong&gt;InfoFormatter&lt;/strong&gt; with a single method:&lt;/p&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;formatMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="4c4d"&gt;&lt;strong&gt;InfoFormatter interface&lt;/strong&gt;: &lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/InfoFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/InfoFormatter.java&lt;/a&gt;&lt;/p&gt;
&lt;p id="19cc"&gt;As described earlier, there is a common abstract base class called &lt;strong&gt;BaseInfoFormatter&lt;/strong&gt;, which contains the mandatory infrastructure-related code:&lt;/p&gt;
&lt;p id="e553"&gt;&lt;strong&gt;BaseInfoFormatter&lt;/strong&gt; (mandatory code: lines 11–39): &lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/BaseInfoFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/BaseInfoFormatter.java&lt;/a&gt;&lt;/p&gt;
&lt;p id="af3d"&gt;Note that all concrete implementations extend this &lt;strong&gt;BaseInfoFormatter &lt;/strong&gt;class.&lt;/p&gt;
&lt;p id="6cf2"&gt;Two concrete implementations are provided:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="4f47"&gt;
&lt;strong&gt;JsonInfoFormatter &lt;/strong&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/implementations/JsonInfoFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/implementations/JsonInfoFormatter.java&lt;/a&gt;
&lt;/li&gt;
&lt;li id="da69"&gt;
&lt;strong&gt;XmlInfoFormatter &lt;/strong&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/implementations/XmlInfoFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/implementations/XmlInfoFormatter.java&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="00d1"&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="326c"&gt;Each concrete implementation registers itself in the factory&lt;/li&gt;
&lt;li id="a8fb"&gt;Registration uses &lt;strong&gt;custom keys&lt;/strong&gt;: “JSON” and “XML”&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="1dec"&gt;The &lt;strong&gt;UsageExample &lt;/strong&gt;class is a runnable class (has &lt;em&gt;main()&lt;/em&gt; method). So, if you downloaded the Mgnt git repository and want to run the example — just run UsageExample class as java application.&lt;/p&gt;
&lt;p id="959c"&gt;&lt;strong&gt;UsageExample&lt;/strong&gt;: &lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/UsageExample.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/UsageExample.java&lt;/a&gt;&lt;/p&gt;
&lt;p id="7c02"&gt;The main method looks very simple:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;printFormattedGreetings&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li id="18ae"&gt;method &lt;em&gt;init()&lt;/em&gt; sole purpose is to instantiate all concrete implementations. Once those constructors are executed, the factory is automatically populated by the infrastructure.&lt;/li&gt;
&lt;li id="e58e"&gt;Method &lt;em&gt;printFormattedGreetings()&lt;/em&gt; Uses &lt;strong&gt;InfoFormatterFactory&lt;/strong&gt; to retrieve implementations by their custom keys and run the concrete implementations methods. This demonstrates that no explicit factory population code exists.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id="5028"&gt;&lt;strong&gt;InfoFormatterFactory&lt;/strong&gt;: &lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/InfoFormatterFactory.java" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/InfoFormatterFactory.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt/blob/master/src/main/java/com/mgnt/lifecycle/management/example/InfoFormatterFactory.java&lt;/a&gt;&lt;/p&gt;
&lt;p id="4f28"&gt;&lt;strong&gt;Main point&lt;/strong&gt;: The factory is populated automatically by the infrastructure — without configuration files, manual registration, or wiring logic. The factory population is triggered by method &lt;em&gt;init()&lt;/em&gt; which in our case is a bootstrapping code that ensures that factory is initialized before it is used in business logic method &lt;em&gt;printFormattedGreetings()&lt;/em&gt;.&lt;/p&gt;
&lt;p id="dc01"&gt;This example provides a minimal but complete demonstration of the self-populating factory infrastructure.&lt;/p&gt;
&lt;p id="7e4e"&gt;&lt;strong&gt;To visualize the class structure described above, see the diagram below&lt;/strong&gt;. It shows how the &lt;em&gt;MgntUtils &lt;/em&gt;infrastructure classes (&lt;strong&gt;BaseEntity&lt;/strong&gt;, &lt;strong&gt;BaseEntityFactory&lt;/strong&gt;) interact with user-defined interfaces, base classes, and concrete implementations to enable automatic factory population.&lt;/p&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AF5vlHAvkKeBD_TRv" width="800" height="496"&gt;&lt;p id="c937"&gt;&lt;strong&gt;An important note&lt;/strong&gt;: For this mechanism to work, concrete implementations must actually be &lt;strong&gt;instantiated &lt;/strong&gt;— that is, their constructors must be invoked. Since self-registration happens during object construction, a class that is never instantiated will never register itself with the factory. (See the description of &lt;em&gt;init() &lt;/em&gt;method above)&lt;/p&gt;
&lt;p id="e6b1"&gt;This requirement makes frameworks that use the &lt;strong&gt;Inversion of Control (IoC)&lt;/strong&gt; pattern — such as &lt;strong&gt;Spring &lt;/strong&gt;and &lt;strong&gt;Spring Boot&lt;/strong&gt; — particularly well suited for this utility. In such frameworks, concrete implementations can be instantiated automatically as part of the application startup process. In the case of Spring Boot, this typically means declaring the implementations as Spring beans with &lt;strong&gt;lazy initialization disabled&lt;/strong&gt;, ensuring they are eagerly created during context initialization. You can see the example of this in declaration of one of the concrete implementations in &lt;em&gt;MgntUtilsUsage &lt;/em&gt;library, Here is the link to &lt;strong&gt;CustomerLetterPreFomatter &lt;/strong&gt;class: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/preformat/implementations/CustomerLetterPreFormatter.java" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/preformat/implementations/CustomerLetterPreFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/preformat/implementations/CustomerLetterPreFormatter.java&lt;/a&gt;&lt;/p&gt;
&lt;p id="3b30"&gt;See lines 8–10 they look like&lt;/p&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt; 
&lt;span class="nd"&gt;@Lazy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; 
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomerLetterPreFormatter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseLetterPreFormatter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As a result, factory population becomes a natural side effect of application startup, requiring no additional wiring or bootstrap code.&lt;/p&gt;
&lt;p id="6fba"&gt;&lt;strong&gt;Important detail&lt;/strong&gt;: Note annotation &lt;code&gt;@Lazy(false)&lt;/code&gt;. This turns off lazy mode and ensuring that this bean is instantiated at SpringBoot application startup as opposed to the first time it is requested. Without it it won’t be initialized and won’t register in the factory. And so, the search in the factory for that bean will fail.&lt;/p&gt;
&lt;p id="67d3"&gt;Also note that while SpringBoot or any other IoC frameworks fit very well with self-populating factory pattern they are not required. For non-IoC environments you will need to write your own simple bootstrap code — just like method &lt;em&gt;init()&lt;/em&gt; in the example above.&lt;/p&gt;
&lt;p id="d8a6"&gt;In the next section, we will build directly on this foundation and show how the same mechanism enables a &lt;strong&gt;clean, extensible, multi-stage workflow architecture&lt;/strong&gt; in a real-world system using the &lt;em&gt;MgntUtilsUsage &lt;/em&gt;repository.&lt;/p&gt;
&lt;h3 id="1801"&gt;Complete multi-stage workflow architecture example&lt;/h3&gt;
&lt;p id="e61c"&gt;Let’s first define the concrete task for which the following example demonstrates the proposed solution.&lt;/p&gt;
&lt;p id="905b"&gt;Assume that you need to develop a system responsible for &lt;strong&gt;formatting corporate letters&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="03c5"&gt;Input Requirements&lt;/h3&gt;
&lt;p id="833c"&gt;The system receives:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="4bb8"&gt;
&lt;strong&gt;Letter content&lt;/strong&gt; — a plain text string&lt;/li&gt;
&lt;li id="4145"&gt;
&lt;strong&gt;Letter type &lt;/strong&gt;— one of a predefined set of values, for example: LEGAL, INTERNAL, CUSTOMER&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2e41"&gt;Processing Requirements&lt;/h3&gt;
&lt;p id="2913"&gt;The system must format the letter using &lt;strong&gt;three distinct processing stages&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="e569"&gt;
&lt;strong&gt;Pre-formatting&lt;/strong&gt; Adds a type-dependent greeting that appears before the letter content.&lt;/li&gt;
&lt;li id="49f3"&gt;
&lt;strong&gt;Formatting &lt;/strong&gt;Adds a type-dependent signature that appears after the letter content.&lt;/li&gt;
&lt;li id="b89c"&gt;
&lt;strong&gt;Post-formatting&lt;/strong&gt; Adds a type-dependent letter header that appears above the greeting and content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="d7c9"&gt;Each stage performs an independent transformation and is unaware of the internal logic of other stages.&lt;/p&gt;
&lt;h3 id="5a9b"&gt;Extensibility Requirements&lt;/h3&gt;
&lt;p id="cf2f"&gt;The architecture must satisfy the following extensibility constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="4a63"&gt;
&lt;strong&gt;Type extensibility&lt;/strong&gt; The system must support adding an arbitrary number of new letter types without modifying existing code.&lt;/li&gt;
&lt;li id="e1cc"&gt;
&lt;strong&gt;Stage extensibility&lt;/strong&gt; The number of processing stages must not be fixed. Stages can be added, removed, or reordered.&lt;/li&gt;
&lt;li id="71be"&gt;
&lt;strong&gt;Conditional workflow support&lt;/strong&gt; The next stage may be determined based on: The result of a previous stage and External conditions or metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="373a"&gt;These requirements rule out hardcoded workflows and require a fully dynamic solution.&lt;/p&gt;
&lt;p id="3ef2"&gt;Let’s run the demo and show some examples:&lt;/p&gt;
&lt;p id="fce2"&gt;How to Run the demo project&lt;/p&gt;
&lt;ol&gt;
&lt;li id="67b7"&gt;Clone &lt;em&gt;MgntUtilsUsage &lt;/em&gt;git repo. Repo link: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt; (Clone URL: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage.git" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage.git" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage.git&lt;/a&gt;).&lt;/li&gt;
&lt;li id="df02"&gt;Open the project in your IDE and run class&lt;em&gt; com.example.stamboot.StamBootApplication.java &lt;/em&gt;as Java application&lt;em&gt;.&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p id="bc06"&gt;Running example requests:&lt;/p&gt;
&lt;p id="792e"&gt;It is assumed that when you start your application the listening port is 8080.&lt;/p&gt;
&lt;p id="06af"&gt;In your browser run the following link:&lt;/p&gt;


&lt;p&gt;&lt;a href="http://localhost:8080/letter?content=Hello%20world&amp;amp;type=CUSTOMER" rel="noopener noreferrer"&gt;http://localhost:8080/letter?content=Hello world&amp;amp;type=CUSTOMER&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p id="70fc"&gt;For the letter content “Hello world” and type “CUSTOMER” the result should be:&lt;/p&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-12-20 Customer communication

Dear customer,
Hello world

Best regards,
Your Service provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="806f"&gt;In the example above&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The line “&lt;em&gt;2025–12–20 Customer communication&lt;/em&gt;” is added by post-formatting stage&lt;/li&gt;
&lt;li&gt;The line “&lt;em&gt;Dear customer&lt;/em&gt;,” is added by pre-formatting stage&lt;/li&gt;
&lt;li&gt;The 2 lines “&lt;em&gt;Best regards, Your Service provider&lt;/em&gt;” are added by formatting stage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same content but with the type “INTERNAL” &lt;br&gt;
(Link: &lt;a href="http://localhost:8080/letter?content=Hello%20world&amp;amp;type=INTERNAL" rel="noopener noreferrer"&gt;http://localhost:8080/letter?content=Hello world&amp;amp;type=INTERNAL&lt;/a&gt;)&lt;br&gt;
 Should yield the following result:&lt;/p&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-12-20 Internal (Confidential) communication

Dear employees,
Hello world

Senior Management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="bee0"&gt;This task definition captures a realistic business requirement while remaining simple enough to clearly demonstrate how a &lt;strong&gt;self-populating, multi-stage workflow architecture&lt;/strong&gt; can be applied in practice.&lt;/p&gt;
&lt;p id="a18f"&gt;In the next section, we will walk through how this task is implemented using the &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt; provided by the open-source &lt;em&gt;MgntUtils &lt;/em&gt;library, showing how multiple factories, shared type keys, and staged processing work together to produce the final result.&lt;/p&gt;
&lt;h3 id="6f0b"&gt;Example resources&lt;/h3&gt;
&lt;p id="6e1e"&gt;The complete, runnable example discussed in this section can be found in the &lt;em&gt;MgntUtilsUsage &lt;/em&gt;Git repository: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;&lt;/p&gt;
&lt;p id="c476"&gt;The most important files and packages are listed below.&lt;/p&gt;
&lt;ol&gt;
&lt;li id="d8ef"&gt;Project Configuration pom.xml &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/pom.xml" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/pom.xml&lt;/a&gt; Note that the &lt;em&gt;MgntUtils &lt;/em&gt;library is declared as a dependency on lines 30–34. The entire example is built on top of the &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt; provided by this open-source library.&lt;/li&gt;
&lt;li id="c2f9"&gt;API Entry Point &lt;em&gt;LetterFormattingController&lt;/em&gt; class &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LetterFormattingController.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LetterFormattingController.java&lt;/a&gt; This class serves as the &lt;strong&gt;entry point&lt;/strong&gt; for the example. It exposes a &lt;strong&gt;GET &lt;/strong&gt;endpoint that demonstrates the complete multi-stage formatting workflow.For simplicity and demonstration purposes only: both &lt;strong&gt;letter content&lt;/strong&gt; and &lt;strong&gt;Letter type&lt;/strong&gt; are passed as a &lt;strong&gt;URL parameter&lt;/strong&gt;. This approach is used strictly for &lt;strong&gt;example convenience&lt;/strong&gt;. In a real-world implementation, passing letter content via URL parameters would not be practical or advisable. A production-ready design would typically use:&lt;p&gt;
- A &lt;b&gt;POST&lt;/b&gt; request&lt;br&gt;
- A request body (for example, JSON)&lt;br&gt;
- Proper validation and payload size handling &lt;br&gt;The choice of a &lt;b&gt;GET&lt;/b&gt; method here keeps the example easy to invoke and focus remains on the workflow architecture, rather than on REST API design details.&lt;/p&gt;


&lt;/li&gt;
&lt;li id="5842"&gt;Letter Formatting Implementation package &lt;em&gt;com.example.stamboot.letterformatting&lt;/em&gt; &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="6aba"&gt;The Core Design Idea&lt;/h3&gt;
&lt;p id="9cf8"&gt;Conceptually, the system behaves as a lightweight workflow engine:&lt;/p&gt;
&lt;p id="a6d0"&gt;&lt;strong&gt;Each processing stage is implemented as an independent, self-populating factory&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="2606"&gt;For each workflow stage, we create:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="fdeb"&gt;&lt;strong&gt;A dedicated factory&lt;/strong&gt;&lt;/li&gt;
&lt;li id="9863"&gt;A &lt;strong&gt;set of concrete implementations&lt;/strong&gt;, one per letter type&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="efea"&gt;Specifically, for the three stages defined earlier, the solution introduces the following factories:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="9876"&gt;LetterPreFormatterFactory&lt;/li&gt;
&lt;li id="e256"&gt;LetterFormatterFactory&lt;/li&gt;
&lt;li id="b115"&gt;LetterPostFormatterFactory&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="4e1e"&gt;Each factory contains &lt;strong&gt;three concrete implementations&lt;/strong&gt;, one for each supported letter type:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="bd60"&gt;LEGAL&lt;/li&gt;
&lt;li id="23a8"&gt;INTERNAL&lt;/li&gt;
&lt;li id="9c7b"&gt;CUSTOMER&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="c3f9"&gt;The Crucial Design Insight: Shared Custom Keys&lt;/h3&gt;
&lt;p id="480b"&gt;As explained earlier in the discussion of the &lt;strong&gt;self-populating factory infrastructure&lt;/strong&gt;, concrete implementations can be registered in a factory using &lt;strong&gt;custom keys&lt;/strong&gt;, rather than class names.&lt;/p&gt;
&lt;p id="c6d2"&gt;This capability is the &lt;strong&gt;cornerstone of the entire workflow design&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="5746"&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="347b"&gt;All factories use &lt;strong&gt;exactly the same set of custom keys&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="6a9a"&gt;Each key corresponds to a &lt;strong&gt;letter type&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="da59"&gt;The keys are identical across all factories: LEGAL, INTERNAL, CUSTOMER&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="daf7"&gt;As a result:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="39f8"&gt;Each factory is completely &lt;strong&gt;independent&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="d0c3"&gt;No factory is aware of any other factory&lt;/li&gt;
&lt;li id="d125"&gt;No stage contains logic to translate or map types&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="f726"&gt;Yet all factories implicitly conform to the &lt;strong&gt;same convention&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="6e08"&gt;How This Enables a Clean Workflow&lt;/h3&gt;
&lt;p id="4fd5"&gt;As the workflow progresses from stage to stage, the code simply retrieves the appropriate implementation for the current letter type from the corresponding factory. For &lt;strong&gt;every stage and every factory&lt;/strong&gt;, the retrieval logic is always the same:&lt;/p&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LETTER_TYPE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="f7c6"&gt;There is:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="f305"&gt;No conditional logic&lt;/li&gt;
&lt;li id="85e2"&gt;No switch statements&lt;/li&gt;
&lt;li id="0b8f"&gt;No type mapping code&lt;/li&gt;
&lt;li id="975e"&gt;No coupling between stages&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="06e6"&gt;Each stage:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="e61b"&gt;Knows only its own responsibility&lt;/li&gt;
&lt;li id="a06f"&gt;Retrieves the correct implementation using the shared key convention&lt;/li&gt;
&lt;li id="4ea0"&gt;Produces its output independently&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="deaf"&gt;This design allows the workflow to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="08f2"&gt;
&lt;strong&gt;Type-extensible&lt;/strong&gt; (new letter types can be added)&lt;/li&gt;
&lt;li id="e9e9"&gt;
&lt;strong&gt;Stage-extensible&lt;/strong&gt; (new stages can be introduced)&lt;/li&gt;
&lt;li id="902f"&gt;
&lt;strong&gt;Order-flexible&lt;/strong&gt; (stages can be reordered or conditionally chained)&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="3a1c"&gt;— all without modifying existing code.&lt;/p&gt;
&lt;h3 id="d786"&gt;Architectural Diagram of the Multi-Stage Letter Formatting System&lt;/h3&gt;
&lt;p id="c043"&gt;The diagram below presents the architectural structure of the multi-stage letter formatting system. Each processing stage is implemented as an independent self-populating factory with its own interface, abstract base class, and concrete implementations. All factories share an identical set of custom keys corresponding to letter types, which allows the workflow to progress through stages without any coupling between them.&lt;/p&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AyEYKlaPIyXc5FYcn" width="800" height="413"&gt;&lt;h3 id="60f9"&gt;The workflow&lt;/h3&gt;
&lt;p id="84df"&gt;Let’s now examine how the workflow is executed at runtime. There are three main components involved:&lt;/p&gt;
&lt;ol&gt;
&lt;li id="ab0d"&gt;LetterFormattingController &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LetterFormattingController.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/controller/LetterFormattingController.java&lt;/a&gt;
&lt;/li&gt;
&lt;li id="5a5c"&gt;LetterFormattingService &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/service/LetterFormattingService.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/service/LetterFormattingService.java&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ea10"&gt;LetterFormattingManager &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p id="2313"&gt;The following diagram illustrates the runtime workflow and interaction between the main components involved in letter formatting. It shows how an incoming request propagates through the controller and service layers into the business logic manager, and how the manager orchestrates the execution of individual processing stages using self-populating factories.&lt;/p&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AaCMmz1xnyA85_-sI" width="800" height="231"&gt;&lt;p id="9668"&gt;&lt;strong&gt;LetterFormattingController &lt;/strong&gt;is the entry point of the application. It exposes a GET HTTP endpoint that receives letter content and letter type as request parameters (this is done purely for example convenience and is not intended as a real-world API design).&lt;/p&gt;
&lt;p id="af6b"&gt;Upon receiving a request, the controller delegates the formatting task to &lt;strong&gt;LetterFormattingService &lt;/strong&gt;by invoking&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;letterFormattingService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="a579"&gt;(see line 35).&lt;/p&gt;
&lt;p id="0c73"&gt;The controller receives the formatted letter as a result, logs it, and builds an HTTP response containing the formatted output. If an error occurs, the exception is caught, an appropriate log message is written, and an HTTP error response is returned.&lt;/p&gt;
&lt;p id="3527"&gt;&lt;strong&gt;LetterFormattingService &lt;/strong&gt;performs two responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="ddd8"&gt;It validates the letter type against the predefined set of supported types and throws an exception if the type is invalid.&lt;/li&gt;
&lt;li id="0ee8"&gt;It delegates the actual formatting workflow execution to &lt;strong&gt;LetterFormattingManager&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="e20b"&gt;This separation keeps validation and orchestration concerns outside of the controller while keeping business flow logic out of the service layer.&lt;/p&gt;
&lt;p id="2e1e"&gt;&lt;strong&gt;LetterFormattingManager &lt;/strong&gt;is the core business logic engine. It is responsible for determining &lt;strong&gt;which stages are executed and in what order&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="b60c"&gt;In this example, the workflow is unconditional and consists of three sequential stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li id="0c7b"&gt;Pre-formatting&lt;/li&gt;
&lt;li id="a505"&gt;Formatting&lt;/li&gt;
&lt;li id="4c28"&gt;Post-Formatting&lt;/li&gt;
&lt;/ol&gt;
&lt;p id="b1bd"&gt;However, this logic can be arbitrarily complex. Stages may be reordered, skipped, or conditionally executed based on the result of previous stages or other business rules. Crucially, this logic is implemented &lt;strong&gt;without any knowledge of concrete implementations&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="bf45"&gt;For each stage, the manager retrieves a concrete implementation from the corresponding factory using the letter type and invokes the stage-specific processing method.&lt;/p&gt;
&lt;p id="76b1"&gt;The most important architectural point here is that &lt;strong&gt;all stage factories share the same identical set of keys&lt;/strong&gt;, representing the supported letter types. This guarantees that for any stage, retrieving a concrete implementation is always a simple and uniform operation:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="c91f"&gt;As a result, stages remain completely decoupled from each other, while the workflow logic remains independent of both the number of stages and the number of supported letter types.&lt;/p&gt;
&lt;h3 id="7641"&gt;Type and Stage Extensibility and Conditional Workflow&lt;/h3&gt;
&lt;p id="ab56"&gt;In this section, we will finally discuss the repeatedly mentioned benefits of the proposed infrastructure.&lt;/p&gt;
&lt;p id="f7a7"&gt;At the end of “&lt;strong&gt;The Core Design Idea&lt;/strong&gt;” section, we stated that this design allows the framework to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="0e4b"&gt;
&lt;strong&gt;Type-extensible&lt;/strong&gt; (new letter types can be added)&lt;/li&gt;
&lt;li id="499e"&gt;
&lt;strong&gt;Stage-extensible&lt;/strong&gt; (new processing stages can be introduced)&lt;/li&gt;
&lt;li id="550d"&gt;
&lt;strong&gt;Order-flexible&lt;/strong&gt; (stages can be reordered or conditionally chained)&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="2686"&gt;— all without modifying existing code.&lt;/p&gt;
&lt;p id="3f3e"&gt;Let’s now demonstrate how this is achieved, point by point.&lt;/p&gt;
&lt;h3 id="463b"&gt;Conditional Workflow Logic&lt;/h3&gt;
&lt;p id="88da"&gt;Here we will discuss how processing stages can be reordered or conditionally executed based on the results of previous stages or any other runtime conditions.&lt;/p&gt;
&lt;p id="af9b"&gt;As stated earlier, the business logic engine is implemented in &lt;strong&gt;com.example.stamboot.letterformatting.bl.LetterFormattingManager&lt;/strong&gt; (see source code here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java&lt;/a&gt;)&lt;/p&gt;
&lt;p id="516d"&gt;The workflow is controlled by the method:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;handleLetterFormatting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DocumentType&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;preformattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preformatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preformattedContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;postFormattedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postFormatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formattedContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;postFormattedString&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; 
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p id="dfc5"&gt;(see lines 15–20 in the class source)&lt;/p&gt;
&lt;p id="bfc9"&gt;As shown above, the stages are executed in a simple, unconditional sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="a658"&gt;
&lt;strong&gt;Pre-formatting&lt;/strong&gt; (pre-appends a greeting to the letter content)&lt;/li&gt;
&lt;li id="f6cf"&gt;
&lt;strong&gt;Formatting &lt;/strong&gt;(post-appends a signature)&lt;/li&gt;
&lt;li id="716b"&gt;
&lt;strong&gt;Post-formatting&lt;/strong&gt; (pre-appends a header)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="7e10"&gt;Reordering Stages&lt;/h3&gt;
&lt;p id="8109"&gt;Let’s start with a simple unconditional modification: &lt;strong&gt;changing the execution order&lt;/strong&gt; by switching the formatting and post-formatting stages.&lt;/p&gt;
&lt;p id="2fa5"&gt;Originally, the flow was:&lt;/p&gt;
&lt;blockquote id="0434"&gt;Pre-append greeting → Post-append signature → Pre-append header&lt;/blockquote&gt;
&lt;p id="270a"&gt;After reordering, the flow becomes:&lt;/p&gt;
&lt;blockquote id="39f2"&gt;Pre-append greeting → Pre-append header → Post-append signature&lt;/blockquote&gt;
&lt;p id="995c"&gt;To achieve this, we only need to modify the&lt;em&gt; handleLetterFormatting()&lt;/em&gt; method as follows:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;handleLetterFormatting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DocumentType&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;preformattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preformatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;postFormattedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postFormatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preformattedContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postFormattedString&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;formattedContent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; 
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is &lt;strong&gt;the only change required&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="c041"&gt;In this particular example, because pre-formatting and post-formatting both prepend content and formatting appends content, changing their order does not affect the final output. However, if you run it in debug mode and inspect the intermediate values, you will clearly see that the &lt;strong&gt;order of stage execution has changed&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="ac34"&gt;This trivial example demonstrates an important architectural point: &lt;strong&gt;stage execution order can be changed without modifying any stage implementations&lt;/strong&gt;, and without any awareness of how many implementations (letter types) exist.&lt;/p&gt;
&lt;h3 id="5672"&gt;Conditional Workflow&lt;/h3&gt;
&lt;p id="824f"&gt;Now let’s introduce conditional logic.The core point remains the same: &lt;strong&gt;workflow control logic is decoupled from the number of supported types and from their implementation details&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="b433"&gt;Assume that we want to perform a document modification &lt;strong&gt;only if the content does not already conform to the expected standard&lt;/strong&gt;.&lt;/p&gt;
&lt;p id="548c"&gt;For example, the pre-formatting stage prepends a greeting. Let’s modify the workflow so that this stage is executed &lt;strong&gt;only if the original letter content does not already contain a greeting&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Introducing Letter Metadata
&lt;/h3&gt;

&lt;p&gt;We introduce a simple metadata class in the package &lt;em&gt;com.example.stamboot.letterformatting.bl&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LetterMetadata&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;headerPresent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; 
 &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;greetingPresent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
 &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;signaturePresent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="176e"&gt;(Getter and setter methods are omitted for brevity.)&lt;/p&gt;
&lt;p id="578b"&gt;Next, we introduce a helper class &lt;strong&gt;LetterInspector &lt;/strong&gt;with a single static method:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;LetterMetadata&lt;/span&gt; &lt;span class="nf"&gt;inspectLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;letterContent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="ac7e"&gt;The implementation is not shown here, as it is not relevant to the example. Conceptually, this method analyzes the letter content and returns metadata describing which structural elements are already present.&lt;/p&gt;
&lt;h3 id="2fa8"&gt;Conditional Stage Execution&lt;/h3&gt;
&lt;p id="92af"&gt;We now modify the business logic method as follows:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;handleLetterFormatting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DocumentType&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;LetterMetadata&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LetterInspector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspectLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isGreetingPresent&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preformatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LetterInspector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspectLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSignaturePresent&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LetterInspector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspectLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isHeaderPresent&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postFormatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="2a74"&gt;With this our flow looks like this:&lt;/p&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2Aa5ykrzIWoP9cjjcw" width="800" height="91"&gt;&lt;p id="c67e"&gt;With this approach, each stage is executed &lt;strong&gt;only if required&lt;/strong&gt;, based on:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="d491"&gt;The current content state&lt;/li&gt;
&lt;li id="bc3c"&gt;The result of previously executed stages&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="cd4b"&gt;The workflow dynamically adapts at runtime while remaining fully deterministic and centrally controlled.&lt;/p&gt;
&lt;h3 id="cc92"&gt;Architectural Implications&lt;/h3&gt;
&lt;p id="fe01"&gt;This example demonstrates that:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="d903"&gt;Conditional workflow logic resides entirely in &lt;strong&gt;LetterFormattingManager&lt;/strong&gt;
&lt;/li&gt;
&lt;li id="d8c5"&gt;Stage implementations remain completely unaware of the workflow&lt;/li&gt;
&lt;li id="4819"&gt;Factories and their registered implementations are untouched&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fbb3"&gt;Type Extensibility&lt;/h3&gt;
&lt;p id="f79e"&gt;This section will demonstrate how to add a new letter type (or more generically new concrete implementation) to the framework. Throughout this article I referred to 3 letter types in our example progect (&lt;em&gt;MgntUtilsUsage &lt;/em&gt;repo: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;). The types are: &lt;strong&gt;LEGAL&lt;/strong&gt;,&lt;strong&gt; INTERNAL&lt;/strong&gt;,&lt;strong&gt; CUSTOMER&lt;/strong&gt;. However, if you look at the repo it comes with 4 letter types where &lt;strong&gt;FINANCE &lt;/strong&gt;is the additional, not mentioned type. The truth is that as I developed my project I originally had only 3 types, and added the 4th (&lt;strong&gt;FINANCE&lt;/strong&gt;) at a later stage to showcase how the new type could be added. So, I will simply list here all the changes that were made to add the additional letter type. Here they are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The enum &lt;strong&gt;com.example.stamboot.letterformatting.DocumentType&lt;/strong&gt; (&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/DocumentType.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/DocumentType.java&lt;/a&gt;) was modified. The additional enum value &lt;strong&gt;FINANCE&lt;/strong&gt; was added (See Line #7 in the source code) Here is the modified enum:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;DocumentType&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="no"&gt;LEGAL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
 &lt;span class="no"&gt;INTERNAL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;CUSTOMER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;FINANCE&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//This is the newly added line&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="2ffd"&gt;2. For each stage a new concrete implementation class was added.&lt;/p&gt;
&lt;p id="426a"&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;: Each new implementation MUST use the newly added &lt;strong&gt;FINANCE &lt;/strong&gt;Letter type value as a custom key for registration in its stage factory via the superclass constructor invocation:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DocumentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FINANCE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="75f6"&gt;- For pre-formatting stage the new class is: &lt;strong&gt;com.example.stamboot.letterformatting.preformat.implementations.FinancialDocumentPreformatter&lt;/strong&gt; (See source code here: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/preformat/implementations/FinancialDocumentPreformatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/preformat/implementations/FinancialDocumentPreformatter.java&lt;/a&gt; See line 13 — the registration with &lt;strong&gt;FINANCE &lt;/strong&gt;key)&lt;/p&gt;
&lt;p id="0188"&gt;- For formatting stage the new class is: &lt;strong&gt;com.example.stamboot.letterformatting.format.implementations.FinancialDocumentFormatter&lt;/strong&gt; (Source code: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/format/implementations/FinancialDocumentFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/format/implementations/FinancialDocumentFormatter.java&lt;/a&gt; See line 13 — the registration with &lt;strong&gt;FINANCE &lt;/strong&gt;key)&lt;/p&gt;
&lt;p id="78a4"&gt;- For post-formatting stage the new class is: c&lt;strong&gt;om.example.stamboot.letterformatting.postformat.implementations.FinancialDocumentPostFormatter&lt;/strong&gt; (Source code: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/postformat/implementations/FinancialDocumentPostFormatter.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/postformat/implementations/FinancialDocumentPostFormatter.java&lt;/a&gt; See line 15 — the registration with &lt;strong&gt;FINANCE &lt;/strong&gt;key)&lt;/p&gt;
&lt;p id="e79d"&gt;This is it! No change anywhere else, just add new implementations for each stage and it is automatically picked up, registered and ready to use.&lt;/p&gt;
&lt;h3 id="ed00"&gt;Stage Extensibility&lt;/h3&gt;
&lt;p id="0dc0"&gt;Adding a new stage is a little bit more work than adding a new type, but it is still very structured and isolated modification that does not involve changing pre-existing structure. Back in the “Complete multi-stage workflow architecture example” section of this article in the “The Core Design Idea” paragraph we stated the following:&lt;/p&gt;
&lt;blockquote id="700d"&gt;&lt;em&gt;Each processing stage is implemented as an independent, self-populating factory.&lt;/em&gt;&lt;/blockquote&gt;
&lt;p id="7457"&gt;So, adding another stage is just creating another self-populating factory set. In the same section on the next paragraph “Architectural Diagram of the Multi-Stage Letter Formatting System” there is a diagram showing 3 independent sets of self-populating factories. If you look at the code in our MgntUtilsUsage repository each such set is implemented in a separate package:&lt;/p&gt;
&lt;ul&gt;
&lt;li id="0265"&gt;com.example.stamboot.letterformatting.preformat (see &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/preformat" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/preformat&lt;/a&gt;)&lt;/li&gt;
&lt;li id="9fb1"&gt;com.example.stamboot.letterformatting.format (see &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/format" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/format&lt;/a&gt;)&lt;/li&gt;
&lt;li id="df17"&gt;com.example.stamboot.letterformatting.postformat (see &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/postformat" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/tree/main/src/main/java/com/example/stamboot/letterformatting/postformat&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="7a70"&gt;So, to add a new stage we just need to create another package with self-populating factory set that mimics the same structure as the 3 existing ones. Let’s demonstrate this with a concrete example by introducing a new &lt;strong&gt;ps-formatting&lt;/strong&gt; stage that appends a ‘P.S.’ section after the signature. In order to create this stage, we will&lt;/p&gt;
&lt;ol&gt;&lt;li id="7108"&gt;Create a new package&lt;em&gt;: com.example.stamboot.letterformatting.psformat &lt;/em&gt;In this package we will create the following structure:&lt;/li&gt;&lt;/ol&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AIHhy36qZeDbClelY" width="617" height="1106"&gt;&lt;p id="5a45"&gt;2. Create a factory class &lt;strong&gt;com.example.stamboot.letterformatting.psformat.LetterPsFormatterFactory&lt;/strong&gt;. The code is omitted as it is practically identical to factories in the other 3 stages.&lt;/p&gt;
&lt;p id="6e5f"&gt;3. Create &lt;strong&gt;com.example.stamboot.letterformatting.psformat.LetterPsFormatter&lt;/strong&gt; interface:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;LetterPsFormatter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;psFormatDocument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="d52e"&gt;4. Create &lt;strong&gt;com.example.stamboot.letterformatting.psformat.BaseLetterPsFormatter&lt;/strong&gt; abstract class&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseLetterPsFormatter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BaseLetterPsFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;LetterPsFormatter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;FACTORY_TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nc"&gt;BaseLetterPsFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSimpleName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FACTORY_TYPE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LetterPsFormatterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFactoryInstance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BaseLetterPsFormatter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FACTORY_TYPE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BaseLetterPsFormatter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;customName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FACTORY_TYPE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="9efa"&gt;5. Create a new package &lt;em&gt;com.example.stamboot.letterformatting.psformat. implementations&lt;/em&gt;&lt;/p&gt;
&lt;p id="f5cd"&gt;In this package create 4 concrete implementation classes — one per each letter type. &lt;br&gt;&lt;strong&gt;Important note&lt;/strong&gt;: just like in “Type Extensibility” section Each new implementation MUST use appropriate Letter type value as a custom key for registration in its stage factory via the superclass constructor invocation. For example for &lt;strong&gt;FINANCE &lt;/strong&gt;implementation use this invocation.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DocumentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FINANCE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="79fc"&gt;We will show just one class and leave the rest of the implementations for readers as they are very similar as you can see in previous stages implementations. We will show the implementation for &lt;strong&gt;FINANCE&lt;/strong&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.stamboot.letterformatting.psformat.implementations&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//import statements are omitted&lt;/span&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@Lazy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FinancialDocumentPsFormatter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseLetterPsFormatter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FinancialDocumentPsFormatter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DocumentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FINANCE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;psFormatDocument&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;“\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;nP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;comes&lt;/span&gt; &lt;span class="no"&gt;P&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p id="3f66"&gt;6. Now we are done with adding the &lt;strong&gt;ps-formatting&lt;/strong&gt; stage and we just need to use it. So, let’s assume that we want to add this stage as the last one after &lt;strong&gt;post-formatting stage&lt;/strong&gt;. So we will need to modify our Business Logic. So we go to our &lt;strong&gt;LetterFormattingManager &lt;/strong&gt;class (&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage/blob/main/src/main/java/com/example/stamboot/letterformatting/bl/LetterFormattingManager.java&lt;/a&gt;) and modify the &lt;em&gt;handleLetterFormatting &lt;/em&gt;method as follows&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;handleLetterFormatting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DocumentType&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;preformattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preformatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formattedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preformattedContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;postFormattedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postFormatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formattedContent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;psFormattedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psFormatLetter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postFormattedString&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;psFormattedString&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p id="2958"&gt;There are two simple private helper methods that need to be added but they are omitted here as they are trivial.&lt;/p&gt;
&lt;p id="d769"&gt;So, this is it. This demonstrates that stage extensibility is achieved by composition rather than modification: new stages are introduced as independent factory sets, and the workflow evolves explicitly through business logic without impacting existing stages or implementations.&lt;/p&gt;
&lt;p id="8bfa"&gt;Unlike type extensibility, adding a new stage necessarily requires modifying the business logic, because the workflow must explicitly decide when and where the new stage is executed.&lt;/p&gt;
&lt;h3 id="07ea"&gt;In Conclusion&lt;/h3&gt;
&lt;p id="e9b0"&gt;In this article, we examined a lightweight yet powerful infrastructure for building &lt;strong&gt;extensible multi-stage workflows across multiple data types&lt;/strong&gt;. Using the self-populating factory mechanism provided by the &lt;em&gt;MgntUtils &lt;/em&gt;library, we demonstrated how a system can be designed to support:&lt;/p&gt;
&lt;ul&gt;

&lt;li id="7cff"&gt;Adding new data types without modifying existing code&lt;/li&gt;

&lt;li id="5729"&gt;Adding new processing stages in an isolated and predictable manner&lt;/li&gt;

&lt;li id="78d4"&gt;Reordering and conditionally executing stages based on runtime logic&lt;/li&gt;

&lt;li id="782b"&gt;Centralized workflow control that remains completely decoupled from implementation details&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="7dca"&gt;The key architectural insight is the combination of &lt;strong&gt;independent self-populating factories&lt;/strong&gt;, a &lt;strong&gt;shared key convention&lt;/strong&gt;, and a &lt;strong&gt;centralized workflow manager&lt;/strong&gt;. This approach eliminates the need for switch statements, manual registries, and intrusive configuration while keeping the system easy to extend and reason about.&lt;/p&gt;
&lt;p id="0f86"&gt;Although the examples in this article focus on letter formatting, the same design applies to a wide range of domains, including validation pipelines, ETL workflows, document generation systems, and other multi-step processing engines.&lt;/p&gt;
&lt;p id="e5e6"&gt;If you find this approach useful, consider exploring and using the &lt;em&gt;MgntUtils &lt;/em&gt;open-source library in your own projects:&lt;/p&gt;
&lt;ul&gt;

&lt;li id="0aa1"&gt;

&lt;strong&gt;GitHub repository&lt;/strong&gt;: &lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/Mgnt&lt;/a&gt;
&lt;/li&gt;

&lt;li id="1a23"&gt;

&lt;strong&gt;Maven Central&lt;/strong&gt;: &lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils&lt;/a&gt;
&lt;/li&gt;

&lt;li id="1934"&gt;

&lt;strong&gt;Javadoc&lt;/strong&gt;: &lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;https://michaelgantman.github.io/Mgnt/docs/&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="5b85"&gt;The complete runnable example discussed in this article is available here:&lt;/p&gt;
&lt;ul&gt;&lt;li id="fa37"&gt;

&lt;strong&gt;MgntUtilsUsage example project&lt;/strong&gt;: &lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/michaelgantman/MgntUtilsUsage" rel="noopener noreferrer"&gt;https://github.com/michaelgantman/MgntUtilsUsage&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;p id="52e5"&gt;Feedback, suggestions, and contributions are welcome — feel free to share your experience, open issues, or contribute improvements to help evolve the library further.&lt;/p&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;

</description>
      <category>architecture</category>
      <category>java</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Java Stacktrace filtering utility.</title>
      <dc:creator>Michael Gantman</dc:creator>
      <pubDate>Thu, 05 Jun 2025 20:32:32 +0000</pubDate>
      <link>https://dev.to/mgantman/java-stacktrace-filtering-utility-1c1i</link>
      <guid>https://dev.to/mgantman/java-stacktrace-filtering-utility-1c1i</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction: Problem definition and sugested solution idea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article is a a technical article for Java developers that suggest a solution for a major pain point of analyzing very long stacktraces searching for meaningful information in a pile of frameworks related stacktrace lines. The core idea of the solution is to provide a capability to intelligently filter out irrelevant parts of the stacktrace without losing important and meaningful information. The benefits are two-fold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Making stacktrace much easier to read and analyze, making it more clear and concise&lt;/li&gt;
&lt;li&gt;Making stacktrace much shorter and saving space &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stacktrace is a lifesaver when debugging or trying to figure out what went wrong in your application. However, when working with logs on the server side you can come across huge stacktrace that contains the longest useless tail of various frameworks and Application Server related packages. And somewhere in this pile, there are several lines of a relevant trace and they may be in different segments separated by useless information. It becomes a nightmare to search for a relevant stuff. Here is a link that describes the same problem with real-life examples (not for the fainthearted :)) &lt;a href="https://dzone.com/articles/filtering-stack-trace-hell" rel="noopener noreferrer"&gt;https://dzone.com/articles/filtering-stack-trace-hell&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite the obvious value of this capability, the Java ecosystem offers very few, if any, libraries with built-in support for stacktrace filtering out of the box. Developers often resort to writing custom code or regex filters to parse and shorten stacktraces—an ad-hoc and fragile solution that’s hard to maintain and reuse. Some logging frameworks such as Log4J and Logback might provide basic filtering options based on log levels or format, but they don't typically allow for the granular control over stack trace &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the solution works and how to use it&lt;/strong&gt;&lt;br&gt;
The Utility is provided as part of Open Source Java library called MgntUtils. It is available on &lt;a href="https://central.sonatype.com/artifact/com.github.michaelgantman/MgntUtils" rel="noopener noreferrer"&gt;Maven Central&lt;/a&gt; as well as on &lt;a href="https://github.com/michaelgantman/Mgnt" rel="noopener noreferrer"&gt;Github&lt;/a&gt; (including source code and Javadoc). Here is a direct link to &lt;a href="https://michaelgantman.github.io/Mgnt/docs/" rel="noopener noreferrer"&gt;Javadoc&lt;/a&gt;. The solution implementation is provided in class TextUtils in method getStacktrace() with several overridden signatures. Here is the direct Javadoc link to &lt;a href="https://michaelgantman.github.io/Mgnt/docs/com/mgnt/utils/TextUtils.html#getStacktrace-java.lang.Throwable-boolean-java.lang.String...-" rel="noopener noreferrer"&gt;getStacktrace()&lt;/a&gt; method with detailed explanation of the functionality.&lt;/p&gt;

&lt;p&gt;So the solution is that user can set a relevant package prefix (or multiple prefixes srting with MgntUtils library version 1.7.0.0) of the packages that are relevant. The stacktrace filtering will work based on the provided prefixes in the following way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The error message is always printed.&lt;/li&gt;
&lt;li&gt;The first lines of the stacktrace are always printed as well until the first line matching one of the prefixes is found.&lt;/li&gt;
&lt;li&gt;Once the first line matching one of the prefixes is found this and all the following lines that ARE matching one of the prefixes will be printed&lt;/li&gt;
&lt;li&gt;Once the first line that is NOT matching any of the prefixes is found - this first non-matching line is printed but all the following non-matching lines are replaced with single line ". . ."&lt;/li&gt;
&lt;li&gt;If at some point another line matching one of the prefixes is found this and all the following matching lines will be printed. and now the logic just keep looping between points 4 and 5&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stacktrace could consist of several parts such as the main section, "Caused by" section and "Supressed" Section. Each part is filtered as a separate section according to the logic described above.&lt;/p&gt;

&lt;p&gt;Also, the same utility (starting from version 1.5.0.3) has method getStacktrace() that takes CharSequence interface instead of Throwable and thus allows to filter and shorten stacktrace stored as a string the same way as a stacktrace extracted from Throwable. So, essentially stacktraces could be filtered "on the fly" at run time or later on from any text source such as a log. (Just to clarify - the utility does not support parsing and modifying the entire log file. It supports filtering just a stacktrace that as passed as a String. So if anyone wants to filter exceptions in a log file they would have to parse the log file and extract stacktrace(s) as separate strings and then can use this utility to filter each individual stacktrace).&lt;/p&gt;

&lt;p&gt;Here is a usage example. Note that the first parameter of &lt;strong&gt;&lt;em&gt;getStacktrace()&lt;/em&gt;&lt;/strong&gt; method in this example is Throwable. Let's say your company's code always resides in packages that start with "com.plain.*" So you set such a prefix and do this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;logger.info(TextUtils.getStacktrace(e,true,"com.plain."));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this will filter out all the useless parts of the trace according to the logic described above leaving you with very concise stacktrace. Also, user can pre-set the prefix (or multiple prefixes) and then just use the convenience method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TextUtils.getStacktrace(e);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will do the same. To preset the prefix just use the method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TextUtils.setRelevantPackage("com.plain.");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Method &lt;strong&gt;&lt;em&gt;setRelevantPackage()&lt;/em&gt;&lt;/strong&gt; supports setting multiple prefixes, so you can use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TextUtils.setRelevantPackage("com.plain.", "com.encrypted.");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you would like to pre-set this value by configuration then starting with the library version 1.1.0.1 you can set Environment Variable "&lt;strong&gt;MGNT_RELEVANT_PACKAGE&lt;/strong&gt;" or System Property "&lt;strong&gt;mgnt.relevant.package&lt;/strong&gt;" to value "com.plain." and the property will be set to that value without you invoking method TextUtils.setRelevantPackage("com.plain."); explicitly in your code. Note that System property value would take precedence over the environment variable if both were set. Just a reminder that with System property you can add it in your command line using &lt;strong&gt;-D&lt;/strong&gt; flag:&lt;/p&gt;

&lt;p&gt;"&lt;strong&gt;-Dmgnt.relevant.package=com.plain.&lt;/strong&gt;" &lt;/p&gt;

&lt;p&gt;Note that System property value would take precedence over environment variable if both are set. &lt;strong&gt;IMOPRTANT:&lt;/strong&gt; Note that for both environment variable and system property if multiple prefixes needed to be set than list them one after another separated &lt;strong&gt;by semicolon (;) For Example:&lt;/strong&gt; "com.plain.;com.encrypted."&lt;/p&gt;

&lt;p&gt;There is also a flexibility here: If you do have pre-set prefixes but for some particular case you would wish to filter according to different set of prefixes you can use the method signature that takes prefixes as parameter and it will override the globally pre-set prefixes just for this time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;logger.info(TextUtils.getStacktrace(e,true,"org.alternative."));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of filtered vs unfiltered stacktrace: you will get a following filtered stacktrace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.plain.BookListNotFoundException: Internal access error
at com.plain.BookService.listBooks()
at com.plain.BookService$$FastClassByCGLIB$$e7645040.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke()
...
at com.plain.LoggingAspect.logging()
at sun.reflect.NativeMethodAccessorImpl.invoke0()
...
at com.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks()
at com.plain.web.BookController.listBooks()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of unfiltered version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.plain.BookListNotFoundException: Internal access error
at com.plain.BookService.listBooks()
at com.plain.BookService$$FastClassByCGLIB$$e7645040.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke()
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed()
at com.plain.LoggingAspect.logging()
at sun.reflect.NativeMethodAccessorImpl.invoke0()
at sun.reflect.NativeMethodAccessorImpl.invoke()
at sun.reflect.DelegatingMethodAccessorImpl.invoke()
at java.lang.reflect.Method.invoke()
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs()
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod()
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.interceptor.AbstractTraceInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke()
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept()
at com.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks()
at com.plain.web.BookController.listBooks()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In Conclusion&lt;/strong&gt;&lt;br&gt;
The MgntUtils library is written and maintained by me. If you require any support or have any question or would like a short demo you can contact me through &lt;a href="https://www.linkedin.com/in/michael-gantman-9661521/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; - send me a message or request connection, or send me email directly at &lt;a href="mailto:michael_gantman@yahoo.com"&gt;michael_gantman@yahoo.com&lt;/a&gt;. I will do my best to respond&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>logging</category>
      <category>stacktrace</category>
    </item>
  </channel>
</rss>
