springboot打印接口調(diào)用日志的實例
概述
請求日志幾乎是所有大型企業(yè)級項目的必要的模塊,請求日志對于我們來說后期在項目運行上線一段時間用于排除異常、請求分流處理、限制流量等。
請求日志一般都會記錄請求參數(shù)、請求地址、請求狀態(tài)(Status Code)、SessionId、請求方法方式(Method)、請求時間、客戶端IP地址、請求返回內(nèi)容、耗時等等。如果你得系統(tǒng)還有其他個性化的配置,也可以完成記錄。
記錄請求參數(shù)時,由于servlet.getInputStream的數(shù)據(jù)只能讀取一次,因此需要先把數(shù)據(jù)緩存下來,構(gòu)造返回流,保證之后的Controller可以正常讀取到請求體的數(shù)據(jù)。
方案思路
- 封裝HttpServletRequest請求類,改類在構(gòu)造方法中將請求的輸入流中的數(shù)據(jù)緩存了起來,保證之后的處理可以重復(fù)讀取輸入流中的數(shù)據(jù)。
- 實現(xiàn)過濾器,把上步封裝的請求類傳下去,保證Controller可以正常讀取輸入流中的數(shù)據(jù)。
- 添加攔截器,讀取輸入流中的數(shù)據(jù)。
- 讀取返回參數(shù)。
封裝HttpServletRequest請求
package com.example.demo.intercept; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /** ?* @author? ?* @date 封裝HttpServletRequest請求 ?*/ public class RequestWrapper extends HttpServletRequestWrapper { ? ? private final String body; ? ? public RequestWrapper(HttpServletRequest request) { ? ? ? ? super(request); ? ? ? ? StringBuilder stringBuilder = new StringBuilder(); ? ? ? ? BufferedReader bufferedReader = null; ? ? ? ? InputStream inputStream = null; ? ? ? ? try { ? ? ? ? ? ? inputStream = request.getInputStream(); ? ? ? ? ? ? if (inputStream != null) { ? ? ? ? ? ? ? ? bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); ? ? ? ? ? ? ? ? char[] charBuffer = new char[128]; ? ? ? ? ? ? ? ? int bytesBody = -1; ? ? ? ? ? ? ? ? while ((bytesBody = bufferedReader.read(charBuffer)) > 0) { ? ? ? ? ? ? ? ? ? ? stringBuilder.append(charBuffer, 0, bytesBody); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? stringBuilder.append(""); ? ? ? ? ? ? } ? ? ? ? } catch (IOException e) { ? ? ? ? } finally { ? ? ? ? ? ? if (inputStream != null) { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? inputStream.close(); ? ? ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if (bufferedReader != null) { ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? bufferedReader.close(); ? ? ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? body = stringBuilder.toString(); ? ? } ? ? @Override ? ? public ServletInputStream getInputStream() throws IOException { ? ? ? ? final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); ? ? ? ? ServletInputStream servletInputStream = new ServletInputStream() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public boolean isFinished() { ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public boolean isReady() { ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public void setReadListener(ReadListener readListener) { ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public int read() throws IOException { ? ? ? ? ? ? ? ? return byteArrayInputStream.read(); ? ? ? ? ? ? } ? ? ? ? }; ? ? ? ? return servletInputStream; ? ? } ? ? @Override ? ? public BufferedReader getReader() throws IOException { ? ? ? ? return new BufferedReader(new InputStreamReader(this.getInputStream())); ? ? } ? ? public String getBody() { ? ? ? ? return this.body; ? ? } }
把可重復(fù)讀請求體通過過濾器往下傳
防止請求流讀取一次后就沒有了,之后的不管是過濾器、攔截器、處理器都是讀的已經(jīng)緩存好的數(shù)據(jù),實現(xiàn)可重復(fù)讀。
package com.example.demo.intercept; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** ?* @author? ?* @date 防止請求流讀取一次后就沒有了 ?*/ @Component @WebFilter(urlPatterns = "/**") public class RecordChannelFilter implements Filter { ? ? @Override ? ? public void init(FilterConfig filterConfig) throws ServletException { ? ? } ? ? @Override ? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ? ? ? ? ServletRequest request = null; ? ? ? ? if (servletRequest instanceof HttpServletRequest) { ? ? ? ? ? ? request = new RequestWrapper((HttpServletRequest) servletRequest); ? ? ? ? } ? ? ? ? if (request ==null){ ? ? ? ? ? ? //防止流讀取一次就沒有了,將流傳遞下去 ? ? ? ? ? ? filterChain.doFilter(servletRequest,servletResponse); ? ? ? ? }else { ? ? ? ? ? ? filterChain.doFilter(request,servletResponse); ? ? ? ? } ? ? } ? ? @Override ? ? public void destroy() { ? ? } }
記錄入?yún)⑷罩?/h2>
實現(xiàn)入?yún)⒂涗洈r截器
通過攔截器的方式實現(xiàn)用戶入?yún)⒂涗洝?/p>
package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import io.jsonwebtoken.Claims; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** ?* @author? ?* @date 記錄用戶操作記錄入?yún)? ?*/ @Component public class OperationLogInterceptor implements HandlerInterceptor { ? ? /** ? ? ?* Jwt secert串,需要與加密token的秘鑰一致 ? ? ?*/ ? ? public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa"; ? ? /** ? ? ?* 需要記錄的接口URL ? ? ?*/ ? ? private static List<String> pathList = new ArrayList<>(); ? ? static { ? ? ? ? pathList.add("/mdms/model"); ? ? } ? ? @Resource ? ? private FunctionDOMapper functionDOMapper;//菜單動能sql ? ? @Resource ? ? private UserOperationHistoryDOMapper historyDOMapper;//操作日志記錄表 ? ? @Override ? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { ? ? ? ? String servletPath = "" + request.getServletPath(); ? ? ? ? String method = request.getMethod(); ? ? ? ? pathList.forEach(path -> { ? ? ? ? ? ? if (servletPath.contains(path)){ ? ? ? ? ? ? ? ? Cookie[] cookies = request.getCookies(); ? ? ? ? ? ? ? ? if (cookies != null) { ? ? ? ? ? ? ? ? ? ? for (Cookie cookie : cookies) { ? ? ? ? ? ? ? ? ? ? ? ? //獲取token在請求中 ? ? ? ? ? ? ? ? ? ? ? ? if (cookie.getName().equals("_qjt_ac_")) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? String token = cookie.getValue(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? /**解密token**/ ? ? ? ? ? ? ? ? ? ? ? ? ? ? byte[] encodeKey = Base64.decode(JWT_SECERT); ? ? ? ? ? ? ? ? ? ? ? ? ? ? Claims claims = null; ? ? ? ? ? ? ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES"); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? //用戶賬號 ? ? ? ? ? ? ? ? ? ? ? ? ? ? String account = claims.getSubject(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? //查詢URL在功能表中的功能 ? ? ? ? ? ? ? ? ? ? ? ? ? ? functionDOMapper.selectOne(servletPath, method); ? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲取入?yún)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RequestWrapper requestWrapper = null; ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (request instanceof HttpServletRequest) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? requestWrapper = new RequestWrapper(request); ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? Map<String,Object> map = new HashMap<>(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? map.put("parameter", JSONObject.parse(requestWrapper.getBody())); ? ? ? ? ? ? ? ? ? ? ? ? ? ? historyDOMapper.insert(map);//將操作信息入庫 ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? } ? ? ? ? }); ? ? ? ? return true; ? ? } }
注冊攔截器
package com.example.demo.config; import com.example.demo.intercept.OperationLogInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** ?* @author? ?* @date 注冊攔截器 ?*/ public class WebConfig implements WebMvcConfigurer { ? ? @Bean ? ? public HandlerInterceptor getOperationLogInterceptor() { ? ? ? ? return new OperationLogInterceptor(); ? ? } ? ? @Override ? ? public void addInterceptors(InterceptorRegistry registry){ ? ? ? ? registry.addInterceptor(getOperationLogInterceptor()).addPathPatterns("/**").excludePathPatterns(); ? ? } }
記錄返參日志
package com.example.demo.intercept; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.util.encoders.Base64; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.annotation.Resource; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.xml.ws.Response; import java.util.*; /** ?* @author? ?* @date 記錄用戶操作日志返參 ?*/ @ControllerAdvice(basePackages = "項目包") public class GetResponseBody implements ResponseBodyAdvice<Object> { ? ? /** ? ? ?* Jwt secert串,需要與加密token的秘鑰一致 ? ? ?*/ ? ? public static final String JWT_SECERT = "23142d7a9s7d66970ad07d8sa"; ? ? /** ? ? ?* 需要記錄的接口URL ? ? ?*/ ? ? private static List<String> pathList = new ArrayList<>(); ? ? static { ? ? ? ? pathList.add("/mdms/model"); ? ? } ? ? @Resource ? ? private FunctionDOMapper functionDOMapper;//菜單動能sql ? ? @Resource ? ? private UserOperationHistoryDOMapper historyDOMapper;//操作日志記錄表 ? ? @Override ? ? public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { ? ? ? ? return false; ? ? } ? ? @Override ? ? public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { ? ? ? ? String path = serverHttpRequest.getURI().getPath(); ? ? ? ? String methodValue = serverHttpRequest.getMethodValue(); ? ? ? ? pathList.forEach(serverPath -> { ? ? ? ? ? ? if (path.contains(serverPath)) { ? ? ? ? ? ? ? ? HashMap<String, String> cookieMap = new HashMap<>(); ? ? ? ? ? ? ? ? HttpHeaders headers = serverHttpRequest.getHeaders(); ? ? ? ? ? ? ? ? List<String> cookieList = headers.get("cookie"); ? ? ? ? ? ? ? ? if (CollectionUtils.isEmpty(cookieList)) { ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? String replaceAll = cookieList.get(0).replaceAll(";", "").replaceAll(";", ""); ? ? ? ? ? ? ? ? String[] split = replaceAll.split(";"); ? ? ? ? ? ? ? ? for (String cookie : split) { ? ? ? ? ? ? ? ? ? ? String[] param = cookie.split("="); ? ? ? ? ? ? ? ? ? ? cookieMap.put(param[0], param[1]); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? String token = cookieMap.get("_qjt_ac_"); ? ? ? ? ? ? ? ? byte[] encodeKey = Base64.decode(JWT_SECERT); ? ? ? ? ? ? ? ? Claims claims = null; ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? SecretKeySpec keySpec = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES"); ? ? ? ? ? ? ? ? ? ? claims = Jwts.parser().setSigningKey(keySpec).parseClaimsJws(token).getBody(); ? ? ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? //用戶賬號 ? ? ? ? ? ? ? ? String account = claims.getSubject(); ? ? ? ? ? ? ? ? //查詢URL在功能表中的功能 ? ? ? ? ? ? ? ? functionDOMapper.selectOne(servletPath, method); ? ? ? ? ? ? ? ? //獲取返參 ? ? ? ? ? ? ? ? List<Object> list = historyDOMapper.select("功能表參數(shù)", account); ? ? ? ? ? ? ? ? list.sort((Object1,Object2)->Object2.getTime().compareTo(Object1.getTime()));//將查詢到的操作記錄按時間降序排列 ? ? ? ? ? ? ? ? Object history = list.get(0); ? ? ? ? ? ? ? ? if (body instanceof Response) { ? ? ? ? ? ? ? ? ? ? Response response = (Response) body; ? ? ? ? ? ? ? ? ? ? JSONObject jsonObject = JSONObject.parseObject(history.getParam()); ? ? ? ? ? ? ? ? ? ? jsonObject.put("message",response.getMessage()); ? ? ? ? ? ? ? ? ? ? jsonObject.put("body",response.getData()); ? ? ? ? ? ? ? ? ? ? history.setParam(jsonObject.toString()); ? ? ? ? ? ? ? ? ? ? history.setDes(response.getMessage()); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? historyDOMapper.updateByPrimaryKeySelective(history);//將操作信息更新 ? ? ? ? ? ? } ? ? ? ? }); ? ? ? ? return body; ? ? } }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringMVC結(jié)合天氣api實現(xiàn)天氣查詢
這篇文章主要為大家詳細(xì)介紹了SpringMVC結(jié)合天氣api實現(xiàn)天氣查詢,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Java實現(xiàn)經(jīng)典游戲打磚塊游戲的示例代碼
這篇文章主要介紹了如何利用Java實現(xiàn)經(jīng)典的游戲—打磚塊。玩家操作一根螢?zāi)簧纤降摹鞍糇印?,讓一顆不斷彈來彈去的“球”在撞擊作為過關(guān)目標(biāo)消去的“磚塊”的途中不會落到螢?zāi)坏紫?。感興趣的小伙伴可以了解一下2022-02-02Java數(shù)據(jù)結(jié)構(gòu)之線段樹詳解
線段樹是一種二叉搜索樹,與區(qū)間樹相似,它將一個區(qū)間劃分成一些單元區(qū)間,每個單元區(qū)間對應(yīng)線段樹中的一個葉結(jié)點。本文將介紹線段樹的Java實現(xiàn)代碼,需要的可以參考一下2022-01-01