Implementing Operation and Exception Logging with Spring Boot AOP

This article demonstrates how to use Spring Boot AOP to automatically record operation and exception logs by defining log tables, adding the AOP starter dependency, creating a custom @OperLog annotation, implementing an aspect that captures request details and errors, and applying the annotation to controller methods, providing a clean, reusable logging solution for Java backend applications.

Top Architect
Top Architect
Top Architect
Implementing Operation and Exception Logging with Spring Boot AOP

When developing projects, recording operation and exception logs is essential for tracking and debugging, but manually adding logging code leads to repetitive and redundant code.

Spring's three core features—IoC, DI, and AOP—allow us to separate cross‑cutting concerns such as logging, performance monitoring, security, transactions, and exception handling from business logic.

1. Create log tables

Define database tables for operation logs and exception logs (illustrated with screenshots in the original article).

2. Add Maven dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3. Create custom annotation OperLog

package com.hyd.zcar.cms.common.utils.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Custom operation log annotation
 * @author wu
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
    String operModul() default ""; // operation module
    String operType() default "";  // operation type
    String operDesc() default "";  // operation description
}

4. Implement the AOP aspect

package com.hyd.zcar.cms.common.utils.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.gexin.fastjson.JSON;
import com.hyd.zcar.cms.common.utils.IPUtil;
import com.hyd.zcar.cms.common.utils.annotation.OperLog;
import com.hyd.zcar.cms.common.utils.base.UuidUtil;
import com.hyd.zcar.cms.common.utils.security.UserShiroUtil;
import com.hyd.zcar.cms.entity.system.log.ExceptionLog;
import com.hyd.zcar.cms.entity.system.log.OperationLog;
import com.hyd.zcar.cms.service.system.log.ExceptionLogService;
import com.hyd.zcar.cms.service.system.log.OperationLogService;

/**
 * Aspect class for handling operation and exception logs
 * @author wu
 * @date 2019/03/21
 */
@Aspect
@Component
public class OperLogAspect {

    /** Operation version, passed via command line, e.g., java -jar xxx.war --version=201902 */
    @Value("${version}")
    private String operVer;

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private ExceptionLogService exceptionLogService;

    /** Pointcut for methods annotated with @OperLog */
    @Pointcut("@annotation(com.hyd.zcar.cms.common.utils.annotation.OperLog)")
    public void operLogPoinCut() {}

    /** Pointcut for all controller methods to capture exceptions */
    @Pointcut("execution(* com.hyd.zcar.cms.controller..*.*(..))")
    public void operExceptionLogPoinCut() {}

    /** After successful execution, record operation log */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        OperationLog operlog = new OperationLog();
        try {
            operlog.setOperId(UuidUtil.get32UUID());
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperLog opLog = method.getAnnotation(OperLog.class);
            if (opLog != null) {
                operlog.setOperModul(opLog.operModul());
                operlog.setOperType(opLog.operType());
                operlog.setOperDesc(opLog.operDesc());
            }
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = method.getName();
            methodName = className + "." + methodName;
            operlog.setOperMethod(methodName);
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            String params = JSON.toJSONString(rtnMap);
            operlog.setOperRequParam(params);
            operlog.setOperRespParam(JSON.toJSONString(keys));
            operlog.setOperUserId(UserShiroUtil.getCurrentUserLoginName());
            operlog.setOperUserName(UserShiroUtil.getCurrentUserName());
            operlog.setOperIp(IPUtil.getRemortIP(request));
            operlog.setOperUri(request.getRequestURI());
            operlog.setOperCreateTime(new Date());
            operlog.setOperVer(operVer);
            operationLogService.insert(operlog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** After throwing an exception, record exception log */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        ExceptionLog excepLog = new ExceptionLog();
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            excepLog.setExcId(UuidUtil.get32UUID());
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = method.getName();
            methodName = className + "." + methodName;
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            String params = JSON.toJSONString(rtnMap);
            excepLog.setExcRequParam(params);
            excepLog.setOperMethod(methodName);
            excepLog.setExcName(e.getClass().getName());
            excepLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
            excepLog.setOperUserId(UserShiroUtil.getCurrentUserLoginName());
            excepLog.setOperUserName(UserShiroUtil.getCurrentUserName());
            excepLog.setOperUri(request.getRequestURI());
            excepLog.setOperIp(IPUtil.getRemortIP(request));
            excepLog.setOperVer(operVer);
            excepLog.setOperCreateTime(new Date());
            exceptionLogService.insert(excepLog);
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }

    /** Convert request parameter map to a simple key‑value map */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /** Convert exception stack trace to a single string */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "
");
        }
        String message = exceptionName + ":" + exceptionMessage + "
\t" + strbuff.toString();
        return message;
    }
}

5. Annotate controller methods with @OperLog

Apply the @OperLog annotation to any controller method that requires logging; the original article shows a screenshot of the annotated method.

6. Provide query interfaces for operation and exception logs

The article includes several screenshots demonstrating the UI for querying and viewing logged records.

Readers are invited to comment, join the architecture community, and can receive promotional gifts by replying with specific keywords.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopException HandlingloggingSpring Boot
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.