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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
