利用拦截器来实现日志系统

 

记录日志的上下文类: ContextHolder



public final class ContextHolder {
    private static ThreadLocalContext context = new ThreadLocalContext();

    private long interval;

    private long startTime;

    private Throwable exception;

    private String requestBody;
    private String responseBody;
    private String Method;
    private String ServletPath;

    // 请求URL参数
    private Map<String, String> requestData;

    private Map<String, String> cookie;
    private Map<String, String> headers;
    // 业务具体日志
    private Map<String, Object> businessData;

    private ContextHolder() {
        this.requestData = new ConcurrentHashMap();
        this.cookie = new ConcurrentHashMap();
        this.headers = new ConcurrentHashMap();
        this.businessData = new ConcurrentHashMap();
    }


    public static ContextHolder ctx() {
        return (ContextHolder) context.get();
    }

    /**
     * Remove.
     */
    public static void remove() {
        context.remove();
    }

    public static void setContext(ContextHolder contextHolder) {
        context.set(contextHolder);
    }


    public static ThreadLocalContext getContext() {
        return context;
    }


    public void setException(Throwable exception) {
        this.exception = exception;
    }

    public static void setContext(ThreadLocalContext context) {
        ContextHolder.context = context;
    }

    public Throwable getException() {
        return exception;
    }

    public String getRequestBody() {
        return requestBody;
    }

    public void setRequestBody(String requestBody) {
        this.requestBody = requestBody;
    }

    public String getResponseBody() {
        return responseBody;
    }

    public void setResponseBody(String responseBody) {
        this.responseBody = responseBody;
    }

    public String getMethod() {
        return Method;
    }

    public void setMethod(String method) {
        Method = method;
    }

    public String getServletPath() {
        return ServletPath;
    }

    public void setServletPath(String servletPath) {
        ServletPath = servletPath;
    }

    public Map<String, String> getRequestData() {
        return requestData;
    }


    public Map<String, String> getCookie() {
        return cookie;
    }


    public Map<String, String> getHeaders() {
        return headers;
    }

    public Map<String, Object> getBusinessData() {
        return businessData;
    }

    public long getInterval() {
        return interval;
    }

    public void setInterval(long interval) {
        this.interval = interval;
    }


    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }


    private static class ThreadLocalContext extends ThreadLocal<ContextHolder> {
        private ThreadLocalContext() {
        }

        @Override
        protected ContextHolder initialValue() {
            return new ContextHolder();
        }
    }

}

request 请求包装类:

RequestWrapper
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.*;

public class RequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        ServletInputStream servletInputStream = new ServletInputStream() {
            public boolean isFinished() {
                return false;
            }
            public boolean isReady() {
                return false;
            }
            public void setReadListener(ReadListener readListener) {}
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public byte[] getBody() {
        return this.body;
    }

    public String getStringBody() throws UnsupportedEncodingException {
        return new String(this.body, "utf-8");
    }
}

 

ResponseWrapper


import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

/**
 * @author :yekeping
 * @date :Created in 2020/10/28 9:15 上午
 * @description:
 * @version: 1.0
 */

public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    private HttpServletResponse response;
    private PrintWriter pwrite;

    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new HllServletOutputStream(bytes); // 将数据写到 byte 中
    }

    /**
     * 重写父类的 getWriter() 方法,将响应数据缓存在 PrintWriter 中
     */
    @Override
    public PrintWriter getWriter() throws IOException {
        try {
            pwrite = new PrintWriter(new OutputStreamWriter(bytes, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return pwrite;
    }

    /**
     * 获取缓存在 PrintWriter 中的响应数据
     *
     * @return
     */
    public byte[] getBytes() {
        if (null != pwrite) {
            pwrite.close();
            return bytes.toByteArray();
        }

        try {
            bytes.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bytes.toByteArray();
    }

    class HllServletOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream ostream;

        public HllServletOutputStream(ByteArrayOutputStream ostream) {
            this.ostream = ostream;
        }

        @Override
        public void write(int b) throws IOException {
            ostream.write(b); // 将数据写到 stream 中
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {

        }
    }

}
ReplaceStreamFilter


import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author :yekeping
 * @date :Created in 2020/10/27 8:02 下午
 * @description:
 * @version: 1.0
 */
@Component
public class ReplaceStreamFilter implements Filter {

    private static Logger logger = LoggerFactory.getLogger(ReplaceStreamFilter.class);


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
        ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
        chain.doFilter(requestWrapper, responseWrapper);
        byte[] bytes = responseWrapper.getBytes();
        responseWrapper.setContentLength(-1);
        ServletOutputStream out = response.getOutputStream();
        out.write(bytes);
        out.flush();

        ContextHolder contextHolder = ContextHolder.ctx();
        contextHolder.setResponseBody(new String(bytes,"utf-8"));
        // 计算耗时
        contextHolder.setInterval(System.currentTimeMillis() - contextHolder.getStartTime());
        logger.info(JSON.toJSONString(contextHolder));
        // 移除线程上下文记录
        ContextHolder.remove();
    }

    @Override
    public void destroy() {
    }
}

 

LogHubInterceptor:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author :yekeping
 * @date :Created in 2020/10/23 2:40 下午
 * @description:
 * @version: 1.0
 */

@Component
public class LogHubInterceptor extends HandlerInterceptorAdapter {

    private static Logger logger = LoggerFactory.getLogger(LogHubInterceptor.class);


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ContextHolder contextHolder = ContextHolder.ctx();
        try {
            contextHolder.setStartTime(System.currentTimeMillis());
            RequestWrapper requestWrapper = new RequestWrapper(request);
            contextHolder.setMethod(request.getMethod());
            contextHolder.setServletPath(request.getServletPath());
            contextHolder.setRequestBody(requestWrapper.getStringBody());


            //如果是spring mvc 方法
            if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
                // 检测是否有 @LogHeader 注解
                LogHeader logHeader = ((HandlerMethod) handler).getMethodAnnotation(LogHeader.class);
                if (logHeader != null) {
                    if (logHeader.needCooke()) {
                        Cookie[] cookies = request.getCookies();
                        if (cookies != null) {
                            Map<String, String> cookiesMap = contextHolder.getCookie();
                            for (Cookie cookie : cookies) {
                                cookiesMap.put(cookie.getName(), cookie.getValue());
                            }
                        }
                    }
                    if (logHeader.needHeader()) {
                        Enumeration<String> headerNames = request.getHeaderNames();
                        Map<String, String> headerMap = contextHolder.getHeaders();
                        while (headerNames.hasMoreElements()) {
                            String key = headerNames.nextElement();
                            if ("cookie".equalsIgnoreCase(key)) {
                                continue;
                            }
                            headerMap.put(key, request.getHeader(key));
                        }
                    }
                }
            }

            String query = request.getQueryString();
            Map<String, String> querysMap = contextHolder.getRequestData();
            if (query != null && !query.isEmpty()) {
                String[] querys = query.split("\\&");
                for (String q : querys) {
                    String[] value = q.split("\\=");
                    if (value.length >= 2) {
                        querysMap.put(value[0], value[1]);
                    }
                }
            }
            return true;
        } catch (Exception e) {
            contextHolder.setException(e);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView
            modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception
            ex) throws Exception {

    }

 

FilterConfig

@Configuration
public class FilterConfig {

    @Autowired
    private ReplaceStreamFilter replaceStreamFilter;

    /**
     * 注册过滤器
     *
     * @return FilterRegistrationBean
     */
    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(replaceStreamFilter);
        registration.addUrlPatterns("/*");
        registration.setName("streamFilter");
        return registration;
    }

}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LogHubInterceptor logHubInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logHubInterceptor).addPathPatterns("/**");
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/***
 * 打印 http 请求头好Cookes 日志
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogHeader {
    boolean needCooke() default true;
    boolean needHeader() default true;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值