<?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>Forem: Tarun Telang</title>
    <description>The latest articles on Forem by Tarun Telang (@ttelang).</description>
    <link>https://forem.com/ttelang</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%2F1208272%2Fbd2ec804-bc75-42a4-a38f-0647ba67c657.jpeg</url>
      <title>Forem: Tarun Telang</title>
      <link>https://forem.com/ttelang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ttelang"/>
    <language>en</language>
    <item>
      <title>How to Eliminate Logging Boilerplate in Java with CDI Interceptors</title>
      <dc:creator>Tarun Telang</dc:creator>
      <pubDate>Tue, 10 Mar 2026 05:53:00 +0000</pubDate>
      <link>https://forem.com/ttelang/how-to-eliminate-logging-boilerplate-in-java-with-cdi-interceptors-2khj</link>
      <guid>https://forem.com/ttelang/how-to-eliminate-logging-boilerplate-in-java-with-cdi-interceptors-2khj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Logging is essential for monitoring, debugging, and performance analysis. &lt;/p&gt;

&lt;p&gt;However, manually adding log statements to every method creates several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Clutter:&lt;/strong&gt; Business logic becomes obscured by logging code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistency:&lt;/strong&gt; Different developers log in different ways&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance Burden:&lt;/strong&gt; Changing log format requires updating every method&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incomplete Implementation:&lt;/strong&gt; Easy to forget important details like execution time, exception handling, or parameter logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CDI Interceptors&lt;/strong&gt; solve these problems by applying the &lt;strong&gt;Aspect-Oriented Programming (AOP)&lt;/strong&gt; principle, separating cross-cutting concerns from business logic.&lt;/p&gt;

&lt;p&gt;With CDI interceptors, you can add logging to any method with a single annotation without requiring any manual coding for logging!&lt;/p&gt;

&lt;p&gt;In this article, we’ll build a production-ready logging interceptor that automatically captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Method entry with parameter values&lt;/li&gt;
&lt;li&gt;Method exit with return values&lt;/li&gt;
&lt;li&gt;Execution time in milliseconds&lt;/li&gt;
&lt;li&gt;Exception details with execution time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Are CDI Interceptors?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CDI (Contexts and Dependency Injection) Interceptors&lt;/strong&gt; allow you to intercept method calls and execute code before and after the target method runs. They’re useful for cross-cutting concerns like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt;: Automatic method tracing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Authorization checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction Management&lt;/strong&gt;: Begin/commit transactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Monitoring&lt;/strong&gt;: Method execution timing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: Cache method results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt;: Pre/post-condition checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Interceptors Work
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client calls method
          │
          ▼
   ┌─────────────────┐
   │  @Logged        │ ◄──Annotation triggers interceptor
   │  PaymentService │
   └────────┬────────┘
            │
            ▼
   ┌─────────────────────────┐
   │  LoggingInterceptor     │ ◄── Intercepts the call
   │  @AroundInvoke          │
   │                         │
   │  1. Log entry + params  │
   │  2. Start timer         │
   │  3. context.proceed()   │ ◄── Execute actual method
   │  4. Log exit + duration │
   │  5. Return result       │
   └─────────────────────────┘
            │
            ▼
      Return to client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Create the Interceptor Binding Annotation
&lt;/h3&gt;

&lt;p&gt;First, we define a custom annotation that will mark which methods/classes should be logged.&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;jakartaee.cdi.tutorial.store.payment.interceptor&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;jakarta.interceptor.InterceptorBinding&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;java.lang.annotation.ElementType&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;java.lang.annotation.Retention&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;java.lang.annotation.RetentionPolicy&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;java.lang.annotation.Target&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Interceptor binding annotation for automatic method logging.
 *
 * Apply this annotation to methods or classes to automatically log:
 * - Method entry with parameters
 * - Method exit with return value
 * - Execution time in milliseconds
 * - Any exceptions thrown
 *
 * Usage:
 *   @Logged  // On a class - logs all public methods
 *   public class PaymentService { ... }
 *
 *   @Logged  // On a method - logs only this method
 *   public void processPayment(...) { ... }
 */&lt;/span&gt;
&lt;span class="nd"&gt;@InterceptorBinding&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="nd"&gt;@Target&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;              &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
         &lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;METHOD&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;     &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;Logged&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&gt;&lt;p&gt;The &lt;code&gt;@InterceptorBinding&lt;/code&gt; marks this as an interceptor binding annotation (not just a regular annotation).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;@Target&lt;/code&gt; can be applied to classes (&lt;code&gt;TYPE&lt;/code&gt;) or methods (&lt;code&gt;METHOD&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@Retention(RUNTIME)&lt;/code&gt; - Must be available at runtime for CDI to process it&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Common Mistake:&lt;/strong&gt; Forgetting @InterceptorBinding will cause CDI to ignore the annotation, and no logging will occur!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Implement the Logging Interceptor
&lt;/h3&gt;

&lt;p&gt;Now we create the interceptor that performs the actual logging.&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;jakartaee.cdi.tutorial.store.payment.interceptor&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;jakarta.annotation.Priority&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;jakarta.interceptor.AroundInvoke&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;jakarta.interceptor.Interceptor&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;jakarta.interceptor.InvocationContext&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;java.util.Arrays&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;java.util.logging.Level&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;java.util.logging.Logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Logging interceptor that automatically logs method entry, exit, and execution time.
 *
 * This interceptor is applied to any method or class annotated with @Logged.
 */&lt;/span&gt;
&lt;span class="nd"&gt;@Logged&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="nd"&gt;@Interceptor&lt;/span&gt;                               &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Priority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Interceptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Priority&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;LoggingInterceptor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@AroundInvoke&lt;/span&gt;                          &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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;Object&lt;/span&gt; &lt;span class="nf"&gt;logMethodCall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InvocationContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Get method metadata&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTarget&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&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="n"&gt;methodName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethod&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&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;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// Log method entry with parameters&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Entering method: {0}.{1} with parameters: {2}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Arrays&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="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getParameters&lt;/span&gt;&lt;span class="o"&gt;())});&lt;/span&gt;
        &lt;span class="c1"&gt;// Start timing&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Execute the actual method&lt;/span&gt;
            &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// Calculate execution time&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;executionTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Log successful exit&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Exiting method: {0}.{1}, execution time: {2}ms, result: {3}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;executionTime&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Log exceptions with execution time&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;executionTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEVERE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Exception in method: {0}.{1}, execution time: {2}ms, exception: {3}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;executionTime&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()});&lt;/span&gt;
            &lt;span class="c1"&gt;// Re-throw the exception to preserve normal error handling&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;@Logged&lt;/code&gt; interceptor MUST be annotated with the binding annotation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@Interceptor&lt;/code&gt; marks this class as an interceptor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@Priority&lt;/code&gt; defines execution order when multiple interceptors exist (required in Jakarta EE 10)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@AroundInvoke&lt;/code&gt;: The method that wraps the target method call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Uses the target class’s logger (better for filtering logs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;context.proceed()&lt;/code&gt;: executes the actual business method&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return the original method’s result&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Re-throw exceptions to maintain normal error flow&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Key Implementation Details
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Why &lt;code&gt;final&lt;/code&gt; variables?
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;final&lt;/code&gt; keyword ensures that values don’t change during execution, which helps the Java Virtual Machine (JVM) optimize the code.&lt;/p&gt;

&lt;h5&gt;
  
  
  Why &lt;code&gt;Arrays.toString()&lt;/code&gt;?
&lt;/h5&gt;

&lt;p&gt;Converting parameters to strings prevents calling &lt;code&gt;toString()&lt;/code&gt; on &lt;code&gt;null&lt;/code&gt; objects, which would cause &lt;code&gt;NullPointerException&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Why create a logger per method?
&lt;/h5&gt;

&lt;p&gt;Using the target class’s &lt;code&gt;logger(Logger.getLogger(className))&lt;/code&gt; allows filtering logs by package or class in production logging configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure CDI to Enable the Interceptor
&lt;/h3&gt;

&lt;p&gt;CDI requires explicit registration of interceptors in &lt;em&gt;beans.xml&lt;/em&gt;.&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;beans&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"https://jakarta.ee/xml/ns/jakartaee"&lt;/span&gt;
       &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
       &lt;span class="na"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"https://jakarta.ee/xml/ns/jakartaee
                           https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"&lt;/span&gt;
       &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"4.0"&lt;/span&gt;
       &lt;span class="na"&gt;bean-discovery-mode=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  (1)
    &lt;span class="nt"&gt;&amp;lt;interceptors&amp;gt;&lt;/span&gt;  (2)
        &lt;span class="nt"&gt;&amp;lt;class&amp;gt;&lt;/span&gt;jakartaee.cdi.tutorial.store.payment.interceptor.LoggingInterceptor&lt;span class="nt"&gt;&amp;lt;/class&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/interceptors&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;bean-discovery-mode="all"&lt;/code&gt; - Scan all classes for CDI beans&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Register the interceptor (order matters if you have multiple interceptors)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Common Mistake: Forgetting to add the interceptor to &lt;em&gt;beans.xml&lt;/em&gt; is the #1 reason why interceptors don’t work! The application will compile and run, but no logging will occur.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Use the &lt;code&gt;@Logged&lt;/code&gt; Annotation
&lt;/h3&gt;

&lt;p&gt;Now you can apply automatic logging to your services!&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: Apply to Entire Class
&lt;/h4&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;jakartaee.cdi.tutorial.store.payment.service&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;jakartaee.cdi.tutorial.store.payment.interceptor.Logged&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;jakarta.enterprise.context.ApplicationScoped&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;jakarta.inject.Inject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Payment service with automatic logging on ALL public methods.
 * No manual logging code needed!
 */&lt;/span&gt;
&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="nd"&gt;@Logged&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;-- All public methods automatically logged&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;PaymentService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;IdempotencyService&lt;/span&gt; &lt;span class="n"&gt;idempotencyService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Automatically logged: entry, parameters, exit, result, duration&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentDetails&lt;/span&gt; &lt;span class="n"&gt;paymentDetails&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Pure business logic - no logging code!&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;validatePaymentDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentDetails&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="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Simulate payment processing&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Also automatically logged&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;validatePaymentDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentDetails&lt;/span&gt; &lt;span class="n"&gt;details&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;details&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;&amp;amp;&amp;amp;&lt;/span&gt;
               &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCardNumber&lt;/span&gt;&lt;span class="o"&gt;()&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;&amp;amp;&amp;amp;&lt;/span&gt;
               &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;()&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Option 2: Apply to Specific Methods
&lt;/h4&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;jakartaee.cdi.tutorial.store.payment.resource&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;jakartaee.cdi.tutorial.store.payment.interceptor.Logged&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;jakarta.enterprise.context.ApplicationScoped&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;jakarta.inject.Inject&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;jakarta.ws.rs.*&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;jakarta.ws.rs.core.MediaType&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;jakarta.ws.rs.core.Response&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/payments"&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;PaymentResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Only this method is logged&lt;/span&gt;
    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/process"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Consumes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Logged&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;-- Selective logging&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentDetails&lt;/span&gt; &lt;span class="n"&gt;paymentDetails&lt;/span&gt;&lt;span class="o"&gt;)&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;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentDetails&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;success&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;Response&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{\"status\": \"success\"}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{\"status\": \"failed\"}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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="c1"&gt;// This method is NOT logged (no @Logged annotation)&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&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;Response&lt;/span&gt; &lt;span class="nf"&gt;health&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;Response&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;"UP"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&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;h2&gt;
  
  
  Testing the Logging Interceptor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start the Application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;cd payment
mvn liberty:dev
The application will start on http://localhost:9080
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Make a Test Request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:9080/payment/api/payments/process &lt;span class="se"&gt;\&lt;/span&gt;


  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "cardNumber": "4111111111111111",
    "cardHolderName": "John Doe",
    "expiryDate": "12/25",
    "securityCode": "123",
    "amount": 99.99
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Observe the Automatic Logs
&lt;/h3&gt;

&lt;p&gt;You should see output as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO: Entering method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.processPayment
      with parameters: [PaymentDetails(cardNumber=4111111111111111, cardHolderName=John Doe,
      expiryDate=12/25, securityCode=123, amount=99.99)]
INFO: Entering method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.validatePaymentDetails
      with parameters: [PaymentDetails(cardNumber=4111111111111111, ...)]
INFO: Exiting method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.validatePaymentDetails,
      execution time: 2ms, result: true
INFO: Exiting method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.processPayment,
      execution time: 45ms, result: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No manual logging code in the business methods&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nested method calls are tracked (validatePaymentDetails called from processPayment)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execution time automatically measured&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parameters and results captured&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clean separation between business logic and logging&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test Exception Handling
&lt;/h3&gt;

&lt;p&gt;Trigger a validation error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:9080/payment/api/payments/process &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "cardNumber": null,
    "amount": 99.99
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Log output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO: Entering method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.processPayment
      with parameters: [PaymentDetails(cardNumber=null, amount=99.99)]
SEVERE: Exception in method: jakartaee.cdi.tutorial.store.payment.service.PaymentService.processPayment,
        execution time: 12ms, exception: Card number cannot be null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World Benefits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before: Manual Logging (Problems)&lt;/strong&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="nd"&gt;@ApplicationScoped&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;PaymentService&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;Logger&lt;/span&gt; &lt;span class="no"&gt;LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentService&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;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentDetails&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entering processPayment with: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← Clutter&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;                  &lt;span class="c1"&gt;// ← Clutter&lt;/span&gt;
        &lt;span class="k"&gt;try&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;validatePaymentDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Validation failed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;                  &lt;span class="c1"&gt;// ← Clutter&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&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;result&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="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting processPayment: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;      &lt;span class="c1"&gt;// ← Clutter&lt;/span&gt;
                       &lt;span class="s"&gt;", duration: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"ms"&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;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;severe&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="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;         &lt;span class="c1"&gt;// ← Clutter&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;7 lines of logging code vs. 4 lines of business logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy to forget timing or exception logging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inconsistent log formats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hard to change logging strategy globally&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After: Interceptor-Based Logging (Clean!)&lt;/strong&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="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="nd"&gt;@Logged&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;-- One annotation replaces all logging code!&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;PaymentService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Pure business logic - zero logging clutter&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentDetails&lt;/span&gt; &lt;span class="n"&gt;details&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;validatePaymentDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&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="kc"&gt;false&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="kc"&gt;true&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;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;100% business logic, 0% logging code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consistent logging automatically&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change logging globally by modifying interceptor once&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better testability (test business logic without logging)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CDI Proxies Limitation:&lt;/strong&gt; Interceptors only work on CDI-managed beans invoked through proxies. Direct calls within the same class (e.g., &lt;code&gt;this.method()&lt;/code&gt;) won’t trigger interceptors. Use &lt;code&gt;@Inject&lt;/code&gt; to reference the bean if you need interception on internal calls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;CDI (Contexts and Dependency Injection) interceptors provide a powerful, clean way to implement automatic method logging in Jakarta EE (Enterprise Edition) and MicroProfile applications. By using interceptors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separate concerns&lt;/strong&gt;: Business logic stays clean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduce boilerplate&lt;/strong&gt;: One annotation replaces multiple lines of code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensure consistency&lt;/strong&gt;: All methods logged the same way&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy maintenance&lt;/strong&gt;: Change logging in one place&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better testability&lt;/strong&gt;: Test business logic without logging noise&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance tracking&lt;/strong&gt;: Automatic execution time measurement&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create binding annotation with @InterceptorBinding&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement interceptor with @AroundInvoke method&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Register in &lt;em&gt;beans.xml&lt;/em&gt;: don’t forget this step!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply &lt;code&gt;@Logged&lt;/code&gt; to classes or methods&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;final&lt;/code&gt; variables for thread safety&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always re-throw exceptions to preserve error flow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remember CDI limitations: only injected beans, only public methods&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;p&gt;To extend this pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add performance alerting for slow methods&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement sensitive data masking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrate with OpenTelemetry for distributed tracing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add request ID correlation across microservices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create audit interceptors for compliance tracking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build security interceptors for authorization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clean code through separation of concerns!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jakarta.ee/specifications/interceptors/2.2/" rel="noopener noreferrer"&gt;Jakarta Interceptors Specification&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jakarta.ee/specifications/cdi/4.1/" rel="noopener noreferrer"&gt;Jakarta CDI Specification&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;This article was originally published on Medium and is being shared here on dev.to to reach a broader developer audience.&lt;/p&gt;

&lt;p&gt;If you prefer reading on Medium or would like to support my writing there, you can find the original article here:&lt;br&gt;
👉 &lt;a href="https://medium.com/@taruntelang/zero-logging-code-full-observability-production-ready-cdi-interceptors-91eda3388d7c" rel="noopener noreferrer"&gt;https://medium.com/@taruntelang/zero-logging-code-full-observability-production-ready-cdi-interceptors-91eda3388d7c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I regularly publish deep dives on Java architecture, microservices, observability, and system design. Feel free to follow along if those topics interest you.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>observability</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
