本文目录导读:

我来介绍Java异常日志记录的几种典型案例和最佳实践。
基础日志记录案例
使用SLF4J + Logback
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExceptionLoggerDemo {
private static final Logger logger = LoggerFactory.getLogger(ExceptionLoggerDemo.class);
public void processData(String data) {
try {
// 业务处理逻辑
validateData(data);
saveToDatabase(data);
} catch (IllegalArgumentException e) {
// 错误案例:只记录异常信息
logger.error("处理数据失败:" + e.getMessage());
// 正确案例:记录完整异常堆栈
logger.error("处理数据失败,data={}", data, e);
} catch (SQLException e) {
// 记录关键上下文和异常
logger.error("数据库操作失败,data={},错误代码={}", data, e.getErrorCode(), e);
} catch (Exception e) {
// 兜底异常处理
logger.error("处理数据时发生未知异常,data={}", data, e);
}
}
}
不同级别异常记录
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public Order createOrder(OrderRequest request) {
try {
// 1. 参数校验异常 - WARN级别
if (request.getAmount() <= 0) {
logger.warn("订单金额异常,request={}", request);
throw new BusinessException("订单金额必须大于0");
}
// 2. 业务逻辑异常 - ERROR级别
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> {
logger.error("用户不存在,userId={}", request.getUserId());
return new BusinessException("用户不存在");
});
// 3. 外部服务调用异常 - ERROR级别
try {
paymentService.process(request.getAmount());
} catch (PaymentException e) {
logger.error("支付服务调用失败,orderId={},amount={}",
request.getOrderId(), request.getAmount(), e);
throw new BusinessException("支付失败", e);
}
// 4. 性能监控 - INFO级别
logger.info("订单创建成功,orderId={},userId={},amount={}",
order.getId(), request.getUserId(), request.getAmount());
return order;
} catch (BusinessException e) {
// 业务异常直接抛出
throw e;
} catch (Exception e) {
// 未知异常
logger.error("创建订单失败,request={}", request, e);
throw new RuntimeException("系统异常,请稍后重试", e);
}
}
}
异步任务异常记录
@Component
public class AsyncTaskLogger {
private static final Logger logger = LoggerFactory.getLogger(AsyncTaskLogger.class);
@Async
public void processTask(Task task) {
// 记录任务开始
logger.info("开始处理异步任务,taskId={},taskType={}",
task.getId(), task.getType());
try {
// 业务处理
task.execute();
// 记录任务完成
logger.info("异步任务处理完成,taskId={},耗时={}ms",
task.getId(), task.getDuration());
} catch (Exception e) {
// 异步任务异常必须记录完整信息
logger.error("异步任务执行失败,taskId={},taskType={},parameters={}",
task.getId(), task.getType(), task.getParameters(), e);
// 可以发送告警通知
alertService.sendAlert("异步任务失败", task.getId(), e);
}
}
}
批量处理异常记录
public class BatchProcessor {
private static final Logger logger = LoggerFactory.getLogger(BatchProcessor.class);
public void batchProcess(List<Record> records) {
int successCount = 0;
int failCount = 0;
List<String> errorMessages = new ArrayList<>();
for (int i = 0; i < records.size(); i++) {
Record record = records.get(i);
try {
processRecord(record);
successCount++;
// 定期打印进度
if ((i + 1) % 100 == 0) {
logger.info("批量处理进度:{}/{},成功:{},失败:{}",
i + 1, records.size(), successCount, failCount);
}
} catch (Exception e) {
failCount++;
String errorMsg = String.format("第%d条记录处理失败,recordId=%s,原因:%s",
i + 1, record.getId(), e.getMessage());
logger.error(errorMsg, e);
errorMessages.add(errorMsg);
}
}
// 汇总日志
logger.info("批量处理完成,总数:{},成功:{},失败:{}",
records.size(), successCount, failCount);
if (!errorMessages.isEmpty()) {
logger.warn("失败详情:{}", String.join("; ", errorMessages));
}
}
}
自定义异常日志格式
public class LoggingUtil {
private static final Logger logger = LoggerFactory.getLogger(LoggingUtil.class);
// 统一异常日志格式
public static void logException(Exception e, String businessType,
Map<String, Object> context) {
LogEvent logEvent = LogEvent.builder()
.businessType(businessType)
.exceptionClass(e.getClass().getName())
.exceptionMessage(e.getMessage())
.context(context)
.timestamp(LocalDateTime.now())
.build();
// 记录结构化日志
logger.error("业务异常日志: {}", logEvent.toJson(), e);
}
// 使用示例
public void businessMethod() {
try {
// 业务逻辑
} catch (Exception e) {
Map<String, Object> context = new HashMap<>();
context.put("userId", 12345);
context.put("orderId", "ORDER20231234");
context.put("requestAmount", 100.0);
LoggingUtil.logException(e, "ORDER_CREATE", context);
}
}
}
Spring AOP 统一异常日志
@Aspect
@Component
public class ExceptionLogAspect {
private static final Logger logger = LoggerFactory.getLogger(ExceptionLogAspect.class);
@Around("@annotation(LogException)")
public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
try {
Object result = joinPoint.proceed();
// 记录成功日志
logger.info("方法调用成功:{},参数:{},耗时:{}ms",
methodName, args, System.currentTimeMillis() - startTime);
return result;
} catch (BusinessException e) {
// 业务异常,记录WARN级别
logger.warn("业务异常:{},方法:{},参数:{}",
e.getMessage(), methodName, args);
throw e;
} catch (Exception e) {
// 系统异常,记录ERROR级别,包含完整堆栈
logger.error("系统异常:{},方法:{},参数:{},耗时:{}ms",
methodName, args, System.currentTimeMillis() - startTime, e);
throw new RuntimeException("系统繁忙,请稍后重试", e);
}
}
}
最佳实践总结
日志记录原则:
public class LoggingBestPractices {
// ✅ 正确:记录上下文信息和异常
public void correctExample() {
try {
// 业务逻辑
} catch (Exception e) {
logger.error("处理订单失败,orderId={},userId={}", orderId, userId, e);
}
}
// ❌ 错误:只记录异常信息
public void wrongExample() {
try {
// 业务逻辑
} catch (Exception e) {
logger.error(e.getMessage()); // 丢失堆栈信息
logger.error("处理失败"); // 没有上下文
}
}
// ✅ 正确:区分日志级别
public void levelExample() {
// 业务异常用WARN
if (amount <= 0) {
logger.warn("金额异常,amount={}", amount);
}
// 系统异常用ERROR
try {
databaseService.save(data);
} catch (SQLException e) {
logger.error("数据库保存失败,data={}", data, e);
}
// 正常流程用INFO
logger.info("数据保存成功,id={}", savedId);
// 调试信息用DEBUG
if (logger.isDebugEnabled()) {
logger.debug("详细参数:{}", JSON.toJSONString(complexObject));
}
}
// ✅ 正确:避免日志级别判断
public void performanceExample() {
// ❌ 错误:字符串拼接无论是否记录日志都会执行
logger.debug("User data: " + JSON.toJSONString(user));
// ✅ 正确:使用占位符,只在需要记录时才会处理
logger.debug("User data: {}", JSON.toJSONString(user));
}
}
配置文件示例(logback-spring.xml):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 异常日志单独文件 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 所有日志文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/all.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</configuration>
这些案例展示了如何在实际开发中规范地记录异常日志,记住关键点:记录上下文、记录完整堆栈、合理使用日志级别、避免性能问题。