{{announcement.body}}
{{announcement.title}}

Automating Tracing Concern Implementation in Enterprise Java Applications

DZone 's Guide to

Automating Tracing Concern Implementation in Enterprise Java Applications

Learn more about implementing a Java tracing design with AspectJ and the enterprise-aspects AOP Java library.

· Java Zone ·
Free Resource

Learn more about implementing a Java tracing design with AspectJ and the enterprise-aspects AOP Java library.

This article explains the importance of tracing and demonstrates a consistent enterprise Java tracing design, with the help of AspectJ and the enterprise-aspects AOP Java library.

You may also like: Implementing AOP With Spring Boot and AspectJ

What Is Tracing? And Why Is It Important?

There is a distinction between logging and tracing. Where logging provides a discrete view of important application events, tracing provides continuous insight into application internal state. While logging is usually used for the auditing of, for example, calls to services or log-in attempts, tracing is usually used for debugging. Below is a trace example as printed in the trace.log file:

Java




xxxxxxxxxx
1


 
1
[10/10/19 0:13:33:810 CEST] 000003ac com.my.package > myMethod ENTRY ["Input","parameters"]
2
[10/10/19 0:13:33:811 CEST] 000003ac com.my.package 1 myMethod In-method trace entry.
3
[10/10/19 0:13:33:811 CEST] 000003ac com.my.package < myMethod RETURN ["Result","object"]



Trace commonly provides the following information: trace entry timestamp, method input parameters and return value, and in-method trace information and thread id, which processed a method invocation (i.e. thread 000003ac). From the provided information, it is possible to understand the program flow and execution duration.

But why is tracing important? Consider the following software design questions first:

  1. How will I troubleshoot production problems for my application?
  2. How long are my clients prepared to wait for the problem’s root cause to be discovered and the problem to be fixed?
  3. How can my application be supported by the limited involvement of the development team?

Often, the exception stack trace will not provide enough information for deducing the reason why an exception occurred, or an application may behave in unexpected ways without throwing an exception:

  1. Some activities are slower than expected
  2. Results are not as expected
  3. Sporadic errors occur

Consider how will the above production situations, which will definitively occur, be solved for your application; I have found that most often a solution to a problem is found by:

  1. Discussing a problem with the users
  2. Searching logs for exceptions
  3. Enabling trace and examining input parameters and return values of methods involved in the problem.

If an application is not designed for tracing, the third option above will not be available, so your problem will be harder to solve. Therefore, tracing is important because it is an important troubleshooting tool for resolving production problems.

Let us examine a traditional approach to Java tracing concern implementation.

Tools Required for Example Configuration:

A Traditional Approach to Java Tracing Concern Implementation

The traditional tracing concern implementation approach is highlighted in the following example:

Java




xxxxxxxxxx
1
49


1
package samples.web.rest;
2
 
          
3
import java.util.ArrayList;
4
import java.util.List;
5
import java.util.logging.Logger;
6
import java.util.logging.Level;
7
 
          
8
import javax.ws.rs.GET;
9
import javax.ws.rs.Path;
10
import javax.ws.rs.Produces;
11
import javax.ws.rs.QueryParam;
12
import javax.ws.rs.core.MediaType;
13
 
          
14
import com.github.akovac35.enterprise.aspects.common.*;
15
 
          
16
@Path("/TraditionalJavaTracingConcern")
17
@Produces({ MediaType.APPLICATION_JSON })
18
public class TraditionalJavaTracingConcern {
19
   private final static String CLASSNAME = TraditionalJavaTracingConcern.class.getName();
20
   private static final Logger _logger = Logger.getLogger(CLASSNAME);
21
 
          
22
   @GET
23
   @Path("/doSomething")
24
   public List<String> doSomething(@QueryParam("input1") String input1) {
25
      final String METHODNAME = "doSomething";
26
      if (_logger.isLoggable(Level.FINER)) {
27
         _logger.entering(CLASSNAME, METHODNAME, new String[] {input1});
28
      }
29
 
          
30
      // Do some work and add tracing in case it is needed
31
      if (_logger.isLoggable(Level.FINE)) {
32
         String tmp = DefaultStringifier.stringify(new int[] {3,2,1,0});
33
         _logger.logp(Level.FINE, CLASSNAME, METHODNAME, "Chasing pests.", tmp);
34
      }
35
 
          
36
      // Do some more work and produce a return
37
      ArrayList<String> ret = new ArrayList<String>();
38
      ret.add("Not a");
39
      ret.add("bug");
40
 
          
41
      if (_logger.isLoggable(Level.FINER)) {
42
         String retStr = DefaultStringifier.stringify(ret);
43
         _logger.exiting(CLASSNAME, METHODNAME, retStr);
44
      }
45
 
          
46
      return ret;
47
   }
48
}
49
 
          



Testing result as printed in the trace.log file:

Java




xxxxxxxxxx
1


 
1
[10/10/19 0:13:33:810 CEST] 000003ac samples.web.rest.TraditionalJavaTracingConcern > doSomething ENTRY Testing
2
[10/10/19 0:13:33:811 CEST] 000003ac samples.web.rest.TraditionalJavaTracingConcern 1 doSomething Chasing pests. 
3
                                                                                     [3,2,1,0]
4
[10/10/19 0:13:33:811 CEST] 000003ac samples.web.rest.TraditionalJavaTracingConcern < doSomething RETURN ["Not a","bug"]



To be useful, the above approach must be implemented consistently and with the same coding style for basically every method of an application; manual implementation thus quickly becomes costly and time-consuming.

Tracing code by itself will result in undiscovered bugs that will crash the application when it is enabled, or the implementation may not be consistent and will require frequent development team involvement for problem resolution.

A Modern Approach to Java Tracing Concern Implementation

Let’s adopt the traditional tracing example for a modern approach using AspectJ and enterprise-aspects AOP Java library. First, we extend the TracingAspect by specifying the packages that will be included for tracing — this is done once per code library or application:

Java




xxxxxxxxxx
1
22


 
1
package samples.web.aspects;
2
 
          
3
import com.github.akovac35.enterprise.aspects.tracing.TracingAspect;
4
 
          
5
public aspect TracingAspectImpl extends TracingAspect pertypewithin(samples..*) {
6
 
          
7
   public pointcut traceableCode(): !within(samples.web.rest.TraditionalJavaTracingConcern+);
8
 
          
9
   /* Optionally define custom logger
10
   @Override
11
   protected ILogger getLogger(String name) {
12
      return new DefaultLogger(name);
13
   }
14
   */
15
 
          
16
   /* Optionally define custom stringifier
17
   @Override
18
   protected String stringify(Object o) {
19
      return DefaultStringifier.stringify(o);
20
   }
21
   */
22
}



And continue by simplifying the traditional tracing approach example:

Java




xxxxxxxxxx
1
30


1
package samples.web.rest;
2
 
          
3
import java.util.ArrayList;
4
import java.util.List;
5
 
          
6
import javax.ws.rs.GET;
7
import javax.ws.rs.Path;
8
import javax.ws.rs.Produces;
9
import javax.ws.rs.QueryParam;
10
import javax.ws.rs.core.MediaType;
11
 
          
12
import com.github.akovac35.enterprise.aspects.tracing.*;
13
 
          
14
@Path("/ModernJavaTracingConcern")
15
@Produces({ MediaType.APPLICATION_JSON })
16
public class ModernJavaTracingConcern {
17
   @GET
18
   @Path("/doSomething")
19
   public List<String> doSomething(@QueryParam("input1") String input1) {
20
      // Do some work and add tracing in case it is needed   
21
      TracingHelper.fine("Chasing pests.", new int[] {3,2,1,0});
22
 
          
23
      // Do some more work and produce a return
24
      ArrayList<String> ret = new ArrayList<String>();
25
      ret.add("Not a");
26
      ret.add("bug");
27
 
          
28
      return ret;
29
   }
30
}



Testing result as printed in the trace.log file:

Java




xxxxxxxxxx
1


1
[10/9/19 22:52:29:188 CEST] 000001a8 samples.web.rest.ModernJavaTracingConcern > doSomething ENTRY ["Testing"]
2
[10/9/19 22:52:29:189 CEST] 000001a8 samples.web.rest.ModernJavaTracingConcern 1 Chasing pests.
3
                                                                                [3,2,1,0]
4
[10/9/19 22:52:29:191 CEST] 000001a8 samples.web.rest.ModernJavaTracingConcern < doSomething RETURN ["Not a","bug"]



We can quickly see that most of the tracing code has now been eliminated with the remainder being simpler than the traditional approach. Simplicity usually results in fewer bugs, better consistency and performance, and increased code coverage.

Java Tracing Concern Design Considerations

Tracing concern design should consider the following design aspects:

  1. Enabling and disabling tracing in production runtime
  2. Optimization of logger instantiation
  3. Pretty-printing objects without relying on any specific interface methods
  4. Performance impact of tracing code when tracing is not enabled
  5. Development effort to support tracing in custom code
  6. Ensuring tracing implementation consistency across the entire portfolio.

The enterprise-aspects AOP Java library implements the listed tracing concern design aspects as follows:

  1. The default logger uses the standard  java.util.logging.Logger functionality, but can be customized to use other logging libraries. This ensures that production runtime tracing functionality used is the one provided by the hosting runtime components.
  2. One instantiated named logger instance per class, used directly and without a list or array search.
  3. Adefault stringifier for transforming any Java object to JSON can be replaced with a custom one
  4. Performance impact when tracing is not enabled is limited to testing whether tracing is enabled
  5. Development effort to support tracing in custom code is reduced to extending the tracing aspect and defining the package or type pattern to which it applies. Debugging advised code remains supported.
  6. Tracing consistency is guaranteed by the tracing the aspect’s advice implementation.

Further Reading

Spring AOP Tutorial With Examples

Implementing AOP With Spring Boot and AspectJ

Overview of Spring Aspect-Oriented Programming

Topics:
java ,tracing ,aop ,java ee architecture

Published at DZone with permission of Aleksander Kovač . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}