springboot 统一异常日志记录

通过注解@ControllerAdvice和@ExceptionHandler({Exception.class})捕获异常,利用注解@ModelAttribute获取请求参数

代码如下:
 

import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
 
/**
 * 用于重复读取requestBody
 */
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
 
    private byte[] body;
 
    private BufferedReader reader;
 
    private ServletInputStream inputStream;
 
    public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        loadBody(request);
    }
 
    private void loadBody(HttpServletRequest request) throws IOException {
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new RequestCachingInputStream(body);
    }
 
    public byte[] getBody() {
        return body;
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {
            return inputStream;
        }
        return super.getInputStream();
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }
 
    private static class RequestCachingInputStream extends ServletInputStream {
 
        private final ByteArrayInputStream inputStream;
 
        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }
 
        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
 
        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }
 
        @Override
        public boolean isReady() {
            return true;
        }
 
        @Override
        public void setReadListener(ReadListener readlistener) {
        }
 
    }
 
}

过滤器filter:

import org.springframework.stereotype.Component;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
@Component
public class LedgerReportFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        chain.doFilter(requestWrapper, response);
    }
 
    @Override
    public void destroy() {
 
    }
 
 
}

异常统一处理:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.domain.document.ExceptionLog;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.domain.repository.mongo.ExceptionLogRepository;
import com.lenovo.lsump.ledger.ledgerpostingreportservice.filter.ContentCachingRequestWrapper;
import feign.FeignException;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
 
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
 
    private static String REQUESTBODY = "requestBodyMessage";
 
    @Autowired
    ExceptionLogRepository exceptionLogRepository;
 
    @ExceptionHandler({Exception.class})
    public Map<String, Object> handleException(HttpServletRequest req, Exception e) throws Exception {
        try {
            String uri = req.getRequestURI();
            ExceptionLog exceptionLog = new ExceptionLog();
            exceptionLog.setCreateDate(new Date());
            Object body = req.getAttribute(REQUESTBODY);
            if (body != null) exceptionLog.setRequestBody(body.toString());
            exceptionLog.setUri(uri);
            Map<String, String[]> parameterMap = req.getParameterMap();
            if (!parameterMap.isEmpty()) {
                ObjectMapper objectMapper = new ObjectMapper();
                exceptionLog.setRequestParams(objectMapper.writeValueAsString(parameterMap));
            }
            Enumeration<String> headerNames = req.getHeaderNames();
            Map<String, String> headers = new HashMap<>();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                headers.put(headerName, req.getHeader(headerName));
            }
            if (!headers.isEmpty()) {
                ObjectMapper objectMapper = new ObjectMapper();
                exceptionLog.setRequestHeaders(objectMapper.writeValueAsString(headers));
            }
            exceptionLog.setMessage(e.toString());
 
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw, true));
            exceptionLog.setStackTrace(sw.toString());
 
            exceptionLogRepository.save(exceptionLog);
        } catch (Exception ex) {
        }
        throw e;
    }
 
 
    /**
     * 由于body在接口读取后无法获取,这里把body提前取出放到参数中,在上面处理异常时使用
     */
    @ModelAttribute
    public void getBobyInfo(HttpServletRequest request) {
        //获取requestBody
        try {
            ContentCachingRequestWrapper requestWapper = null;
            if (request instanceof HttpServletRequest) {
                requestWapper = (ContentCachingRequestWrapper) request;
            }
            String body = IOUtils.toString(requestWapper.getBody(), request.getCharacterEncoding());
            request.setAttribute(REQUESTBODY, body);
        } catch (Exception e) {
 
        }
 
    }
 
    @ExceptionHandler(FeignException.class)
    public Map<String, Object> handleException(FeignException feignException, HttpServletResponse response) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String message = feignException.getMessage();
            if (!message.contains("{")) {
                message = feignException.contentUTF8();
            }
            String originalBody = message;
            Map<String, Object> map = objectMapper.readValue(originalBody, Map.class);
            response.setStatus((Integer) map.get("status"));
            return map;
        } catch (Exception e) {
            throw feignException;
        }
    }
}

                
Spring Boot 提供了一种方便的方式来统一处理应用程序中的异常,这主要通过全局异常处理机制实现。在 Spring Boot 3 中,你可以配置 `@ControllerAdvice` 或 `@ExceptionHandler` 注解的方法来捕获并处理各种类型的异常。以下是几个关键点: 1. **全局异常处理器 (`@ControllerAdvice`)**:创建一个单独的类,其中包含 `@ExceptionHandler` 注解的方法来处理来自 @RestController 的 HTTP 请求引发的特定类型异常。这些方法通常接收 `HttpServletResponse` 和 `Throwable` 参数。 ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) public ResponseEntity<?> handleAllErrors(Exception ex) { // 异常处理逻辑,如记录日志、返回错误响应等 return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); } } ``` 2. **异常映射 (`@MappingException`)**:如果你想要对特定异常类型进行定制化的处理,可以使用 `@MappingException` 注解将它映射到一个特定的处理器方法。 3. **自定义异常类 (`@ControllerAdvice` 或者 @ResponseBody)`**:定义自定义异常类,并在需要的地方抛出,然后在异常处理器中捕获并返回给客户端。 4. **嵌套异常处理器**:如果需要处理更深层次的异常,例如业务层异常,可以在异常处理器中继续捕获并适当地处理。 **相关问题--:** 1. Spring Boot 如何设置全局异常消息模板? 2. 如何处理Spring MVC的预定义HTTP状态码? 3. 怎么在Spring Boot中启用详尽的异常堆栈跟踪?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程治铭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>