Logging, Tracking, and more with Spring Boot

One of the critical aspects of building and maintaining financial applications is logging and tracking. Using Spring Boot’s built-in libraries, I implemented a comprehensive logging and tracking system to monitor application behavior and diagnose issues.

Logback Configuration

I usedLogback as the logging framework, which is the default in Spring Boot. Here’s a sample configuration:

<configuration>    <appender name="FILE">        <file>logs/app.log</file>        <rollingPolicy>            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>            <maxHistory>30</maxHistory>        </rollingPolicy>        <encoder>            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>        </encoder>    </appender>    <root level="info">        <appender-ref ref="FILE" />    </root></configuration>

Custom Logback Customizations

To extend the functionality of Logback for our financial application prototypes, I implemented several customizations to meet specific needs, such as filtering sensitive information, integrating with external logging services, and enhancing log readability.

Filtering Sensitive Information

Given the sensitivity of financial data, it was crucial to ensure that no sensitive information (e.g., credit card numbers, personal identification information) was logged. I implemented a customTurboFilter to scrub logs of any sensitive data.

import ch.qos.logback.classic.spi.ILoggingEvent;import ch.qos.logback.core.filter.Filter;import ch.qos.logback.core.spi.FilterReply;import java.util.regex.Pattern;public class SensitiveDataFilter extends Filter<ILoggingEvent> {    private static final Pattern SENSITIVE_DATA_PATTERN = Pattern.compile("(\\d{4}-?\\d{4}-?\\d{4}-?\\d{4})");    @Override    public FilterReply decide(ILoggingEvent event) {        if (SENSITIVE_DATA_PATTERN.matcher(event.getFormattedMessage()).find()) {            return FilterReply.DENY;        }        return FilterReply.NEUTRAL;    }}

Integration with External Logging Services

To enhance monitoring and alerting, we integrated our logging system with an external logging service like ELK (Elasticsearch, Logstash, Kibana) stack. This required customizing the Logback configuration to use a Logstash appender.

<configuration>    <!-- Other appenders and configuration -->    <appender name="LOGSTASH">        <destination>logstash.example.com:5000</destination>        <encoder />    </appender>    <root level="info">        <appender-ref ref="FILE" />        <appender-ref ref="LOGSTASH" />    </root></configuration>

Enhancing Log Readability

To make logs more readable and easier to parse, I implemented a custom log format that includes additional context information such as request IDs, user IDs, and timestamps.

import ch.qos.logback.classic.PatternLayout;import ch.qos.logback.classic.encoder.PatternLayoutEncoder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class LoggingConfiguration {    @Bean    public PatternLayoutEncoder patternLayoutEncoder() {        PatternLayoutEncoder encoder = new PatternLayoutEncoder();        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");        return encoder;    }}

Tracking Requests

To track requests, I used Spring Boot’sHandlerInterceptor to log incoming requests and responses.

@Componentpublic class RequestInterceptor implements HandlerInterceptor {    private static final Logger logger = LoggerFactory.getLogger(RequestInterceptor.class);    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        logger.info("Incoming request: {} {}", request.getMethod(), request.getRequestURI());        return true;    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        logger.info("Outgoing response: {}", response.getStatus());    }}

Custom Exception Handling

Custom exception handling was implemented using@ControllerAdvice to provide meaningful error messages and log exceptions.

@ControllerAdvicepublic class GlobalExceptionHandler {    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);    @ExceptionHandler(Exception.class)    public ResponseEntity<String> handleException(Exception ex) {        logger.error("An error occurred: {}", ex.getMessage());        return new ResponseEntity<>("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);    }}

For details, related to these prototyping projects, check outDevelopment Patterns for Long-Term Financial Applications.