> 技术文档 > gateway进行接口日志打印_使用globalfilter实现打印springcloud gateway请求日志和响应日志打印

gateway进行接口日志打印_使用globalfilter实现打印springcloud gateway请求日志和响应日志打印


打印需求:

        对所有的接口打印:请求方式,请求路径,请求参数,用户id,访问IP,访问时间

        对增删改操作的接口打印:接口响应

打印方案:

        给GET设置一个白名单(因为get请求大多数是查询,仅有部分增删改操作),在这个白名单里的接口,需要打印响应,不在的话就只打印一下基础信息

        给POST请求设置一个黑名单(因为post请求大多数会对数据进行操作,仅有小部分的查询),在这个黑名单里的查询接口,则不需要打印接口响应

 实现方式:

        在过滤器中进行操作

package com.xxxxxx.gateway.filter;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.parser.Feature;import com.alibaba.fastjson.serializer.SerializerFeature;import com.alibaba.fastjson.serializer.ValueFilter;import com.koushare.common.constant.AuthConstants;import com.koushare.common.constant.TokenConstants;import com.koushare.common.utils.IPUtils;import com.koushare.common.utils.StringUtils;import com.koushare.gateway.model.CheckRequest;import com.koushare.gateway.service.SysPrintLogService;import com.koushare.jwt.config.PassJavaJwtProperties;import lombok.extern.slf4j.Slf4j;import org.reactivestreams.Publisher;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;import org.springframework.core.Ordered;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.core.io.buffer.DataBufferFactory;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.core.io.buffer.DefaultDataBufferFactory;import org.springframework.http.HttpMethod;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.http.server.reactive.ServerHttpResponseDecorator;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import javax.annotation.Resource;import java.nio.CharBuffer;import java.nio.charset.StandardCharsets;import java.util.Date;import java.util.Optional;import java.util.concurrent.atomic.AtomicReference;import static com.koushare.common.convertor.ConvertHelper.bean2map;import static com.koushare.common.utils.IPUtils.getIpAddrByServerHttpRequest;/** * 自定义全局过滤器 */@Component@Slf4jpublic class GlobalRequestFilter implements GlobalFilter, Ordered { @Resource private PassJavaJwtProperties jwtProperties; @Autowired private SysPrintLogService sysPrintLogService; private static final String OPENAPI_SERVICE = \"/openapi/\"; private static final String OPENAPI_CODE_PATH = \"/code/\"; @Override public int getOrder() { return -20; } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest serverHttpRequest = exchange.getRequest(); // 获取原始响应对象和数据缓冲工厂 ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); // 原始响应对象,用于拦截和修改响应内容 ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { /** * 重写writeWith方法拦截响应体 */ @Override public Mono writeWith(Publisher body) { if (body instanceof Flux) {  Flux fluxBody = Flux.from(body);  return super.writeWith(fluxBody.buffer().map(dataBuffers -> { // 获取一些需要打印的参数 long timestamp = System.currentTimeMillis(); HttpMethod method = serverHttpRequest.getMethod(); String requestUrl = serverHttpRequest.getPath().toString(); String userId = Optional.ofNullable(serverHttpRequest.getHeaders().getFirst(AuthConstants.USER_ID)) .filter(StringUtils::isNotBlank).orElse(\"未登录\"); String ip = IPUtils.getIpAddrByServerHttpRequest(serverHttpRequest); String params = getRequestparams(serverHttpRequest, exchange); log.info(\"{} ========================接口详细日志========================\", timestamp); log.info(\"{} 请求方式:{} 请求路径: {}\", timestamp, method, requestUrl); log.info(\"{} 请求参数: {}\", timestamp, params); log.info(\"{} 用户ID: {} 访问IP: {} 访问时间:{}\", timestamp, userId, ip, new Date()); // 判断是否需要打印响应 if (isUpdateDate(method, requestUrl)) { // 创建数据缓冲工厂和缓冲区,用于读取响应内容 DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer buff = dataBufferFactory.join(dataBuffers); byte[] content = new byte[buff.readableByteCount()]; buff.read(content); // 释放缓冲区资源 DataBufferUtils.release(buff); // 获取响应内容类型 MediaType contentType = originalResponse.getHeaders().getContentType(); if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { // 如果不是JSON类型,直接返回原始内容,不进行处理 log.info(\"{} ===============================================================\", timestamp); return bufferFactory.wrap(content); } // 将字节数组转换为字符串 对响应体进行统一格式化处理 String result = modifyBody(new String(content)); log.info(\"{} 响应结果: {}\", timestamp, result); log.info(\"{} ===============================================================\", timestamp); getDelegate().getHeaders().setContentLength(result.getBytes().length); return bufferFactory.wrap(result.getBytes()); } else { // 不需要打印响应结果时,直接合并并返回原始数据 log.info(\"{} ===============================================================\", timestamp); DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer joinedBuffer = dataBufferFactory.join(dataBuffers); byte[] content = new byte[joinedBuffer.readableByteCount()]; joinedBuffer.read(content); DataBufferUtils.release(joinedBuffer); return bufferFactory.wrap(content); }  })); } else {  return super.writeWith(body); } } }; return chain.filter(exchange.mutate().response(decoratedResponse).build()); } private static String getRouteName(String requestUrl) { String serviceUrl = requestUrl.substring(requestUrl.indexOf(\"/\") + 1); log.info(\"getRouteName: \" + serviceUrl.substring(0, serviceUrl.indexOf(\"/\"))); return serviceUrl.substring(0, serviceUrl.indexOf(\"/\")); } /** * 获取请求token */ private String getToken(ServerHttpRequest request) { String token = request.getHeaders().getFirst(jwtProperties.getHeader()); // 如果前端设置了令牌前缀,则裁剪掉前缀 if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); } return token; } /** * 获取去除路由后的path * * @param requestUrl * @return */ private static String getPath(String requestUrl) { String path = requestUrl.substring(1); log.info(\"getPath: \" + path.substring(path.indexOf(\"/\"))); return path.substring(path.indexOf(\"/\")); } /** * 判断是否为增删改接口 只有增删改接口才会打印响应结果 */ private boolean isUpdateDate(HttpMethod method, String requestUrl){ switch (method) { case PUT: case DELETE: return true; case GET: return sysPrintLogService.checkNeedPrint_GET(requestUrl); case POST: return sysPrintLogService.checkNeedPrint_POST(requestUrl); default: return false; } } /** * 获取请求参数 */ private String getRequestparams(ServerHttpRequest serverHttpRequest, ServerWebExchange exchange) { HttpMethod method = serverHttpRequest.getMethod(); // 检查是否为文件上传请求,如果是则不打印参数 MediaType contentType = serverHttpRequest.getHeaders().getContentType(); if (contentType != null && (contentType.includes(MediaType.MULTIPART_FORM_DATA) || contentType.includes(MediaType.APPLICATION_OCTET_STREAM))) { return \"\"; } if (HttpMethod.GET.equals(method) || HttpMethod.DELETE.equals(method)) { StringBuilder params = new StringBuilder(); serverHttpRequest.getQueryParams().forEach((key, value) -> { value.forEach(v -> params.append(key).append(\"=\").append(v).append(\"&\")); }); // 移除末尾的 \"&\" if (params.length() > 0) { params.deleteCharAt(params.length() - 1); } return params.toString(); } else if (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method)) { return getBodyContent(exchange); } return \"\"; } // 从其他filter中copy过来的 目的是获取post请求的body private String getBodyContent(ServerWebExchange exchange){ Flux body = exchange.getRequest().getBody(); AtomicReference bodyRef = new AtomicReference(); // 缓存读取的request body信息 body.subscribe(dataBuffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); DataBufferUtils.release(dataBuffer); bodyRef.set(charBuffer.toString()); }); //获取request body return bodyRef.get(); } /** * 修改响应体内容,统一JSON数据格式 */ private String modifyBody(String str){ JSONObject json = JSON.parseObject(str, Feature.AllowISO8601DateFormat); JSONObject.DEFFAULT_DATE_FORMAT = \"yyyy-MM-dd HH:mm:ss\"; return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? \"\" : value, SerializerFeature.WriteDateUseDateFormat); }}

其中的service实现类:

package com.xxxxxx.gateway.service.impl;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.koushare.gateway.entity.SysPrintLog;import com.koushare.gateway.mapper.SysPrintLogMapper;import com.koushare.gateway.service.SysPrintLogService;import com.koushare.redis.RedisUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Slf4j@Servicepublic class SysPrintLogServiceImpl implements SysPrintLogService { private final static String PrintLogUrlWhitelist_GET = \"PrintLog:UrlWhiteList\"; private final static String PrintLogUrlBlacklist_POST = \"PrintLog:UrlBlackList\"; @Autowired private SysPrintLogMapper sysPrintLogMapper; @Autowired private RedisUtils redisUtils; /** * 检查Get请求是否需要打印 * @return true:需要打印; false:不需要打印 */ @Override public boolean checkNeedPrint_GET(String requestUrl) { return checkWhiteList(requestUrl); } /** * 检查Post请求是否需要打印 * @return true:需要打印; false:不需要打印 */ @Override public boolean checkNeedPrint_POST(String requestUrl) { return checkBlackList(requestUrl); } /** * 重新加载redis的Get请求日志打印接口白名单 */ @Override public List loadPringLogWhiteList() { List list = sysPrintLogMapper.queryPrintLogWhiteList(); JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(list)); redisUtils.set(PrintLogUrlWhitelist_GET, jsonArray); return list; } /** * 重新加载redis的Post请求日志打印接口黑名单 */ @Override public List loadPrintLogBlackList() { List list = sysPrintLogMapper.queryPrintLogBlackList(); JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(list)); redisUtils.set(PrintLogUrlBlacklist_POST, jsonArray); return list; } /** * 读redis中的白名单,不存在则读取数据库 */ private List WhiteList(){ JSONArray jsonArray = redisUtils.getJsonArray(PrintLogUrlWhitelist_GET); if (jsonArray == null || jsonArray.size() == 0) { return loadPringLogWhiteList(); } return jsonArray.toJavaList(SysPrintLog.class); } /** * 读redis中的黑名单,不存在则读取数据库 */ private List BlackList(){ JSONArray jsonArray = redisUtils.getJsonArray(PrintLogUrlBlacklist_POST); if (jsonArray == null || jsonArray.size() == 0) { return loadPrintLogBlackList(); } return jsonArray.toJavaList(SysPrintLog.class); } /** * 白名单列表中查找是否存在匹配的URL */ private boolean checkWhiteList(String requestUrl){ return WhiteList().stream() .anyMatch(sysPrintLog -> sysPrintLog.getUrl().equals(requestUrl)); } /** * 黑名单列表中查找是否存在匹配的URL */ private boolean checkBlackList(String requestUrl){ return BlackList().stream() .noneMatch(sysPrintLog -> sysPrintLog.getUrl().equals(requestUrl)); }}

用到的两个查询:

@Mapperpublic interface SysPrintLogMapper { @Select(\"select id,url,description from sys_print_log_whitelist\") List queryPrintLogWhiteList(); @Select(\"select id,url,description from sys_print_log_blacklist\") List queryPrintLogBlackList();}

以及数据库实体

@Data@Builder(setterPrefix = \"set\")@NoArgsConstructor@AllArgsConstructorpublic class SysPrintLog { private Integer id; private String url; private String description;}

获取ip地址的工具类:

import lombok.extern.slf4j.Slf4j;import org.springframework.http.server.reactive.ServerHttpRequest;import javax.servlet.http.HttpServletRequest;import java.net.InetAddress;import java.net.UnknownHostException;/** * IP地址 * * @author zhanghai on 2018/9/17 */@Slf4jpublic class IPUtils { // 多次反向代理后会有多个ip值 的分割符 private final static String IP_UTILS_FLAG = \",\"; // 未知IP private final static String UNKNOWN = \"unknown\"; // 本地 IP private final static String LOCALHOST_IP = \"0:0:0:0:0:0:0:1\"; private final static String LOCALHOST_IP1 = \"127.0.0.1\"; /** * 获取IP地址 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddr(HttpServletRequest request) { String ip = null; try { ip = request.getHeader(\"x-forwarded-for\"); if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeader(\"Proxy-Client-IP\"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeader(\"WL-Proxy-Client-IP\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeader(\"HTTP_CLIENT_IP\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeader(\"HTTP_X_FORWARDED_FOR\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } catch (Exception e) { log.error(\"IPUtils ERROR \", e); } // 使用代理,则获取第一个IP地址 if (StringUtils.isEmpty(ip) && ip.length() > 15) { if (ip.indexOf(\",\") > 0) { ip = ip.substring(0, ip.indexOf(\",\")); } } return ip; } public static String getIpAddrByServerHttpRequest(ServerHttpRequest request) { // 根据 HttpHeaders 获取 请求 IP地址 String ip = request.getHeaders().getFirst(\"X-Forwarded-For\"); if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"x-forwarded-for\"); if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) { // 多次反向代理后会有多个ip值,第一个ip才是真实ip if (ip.contains(IP_UTILS_FLAG)) {  ip = ip.split(IP_UTILS_FLAG)[0]; } } } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"Proxy-Client-IP\"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"WL-Proxy-Client-IP\"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"HTTP_CLIENT_IP\"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"HTTP_X_FORWARDED_FOR\"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"X-Real-IP\"); } //兼容k8s集群获取ip if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getRemoteAddress().getAddress().getHostAddress(); if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) { //根据网卡取本机配置的IP InetAddress iNet = null; try {  iNet = InetAddress.getLocalHost(); } catch (UnknownHostException e) {  log.error(\"getClientIp error: \", e); } ip = iNet.getHostAddress(); } } return ip; } /** * 获取IP地址 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddrByHttp(ServerHttpRequest request) { String ip = null; try { ip = request.getHeaders().getFirst(\"x-forwarded-for\"); if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"Proxy-Client-IP\"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"WL-Proxy-Client-IP\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"HTTP_CLIENT_IP\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"HTTP_X_FORWARDED_FOR\"); } if (StringUtils.isEmpty(ip) || \"unknown\".equalsIgnoreCase(ip)) { ip = request.getHeaders().getFirst(\"X-Real-IP\"); } } catch (Exception e) { log.error(\"IPUtils ERROR \", e); } // 使用代理,则获取第一个IP地址 if (StringUtils.isEmpty(ip) && ip.length() > 15) { if (ip.indexOf(\",\") > 0) { ip = ip.substring(0, ip.indexOf(\",\")); } } return ip; }}