开发环境
| 名称 | 版本 |
|---|---|
| 操作系统 | Windows 10 X64 |
| JDK | JDK1.8(jdk-8u151-windows-x64) |
| IntelliJ IDEA | IntelliJ IDEA 2018.3 |
| Maven | Maven 3.6.0 |
| lombok | 1.18.6 |
迁移说明
本文的内容出现了很多多余的内容,不利于快速阅读
快速了解,请参考
代码说明
本文代码基于 4.Spring Cloud-Hystrix Dashboard监控数据聚合(Turbine) 项目代码进行修改
文件结构
新增/修改下面选中的文件
test-invoice-web
test-invoice-common
pom.xml(test-invoice-clound)
新加入下面内容
<properties><lombok.version>1.18.6</lombok.version><gson.version>2.8.5</gson.version><hutool.version>4.5.1</hutool.version></properties><dependencies><!--lombok start--><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!--lombok end--><!-- 用于日志切面中,以 json 格式打印出入参(本来使用阿里的 FASTJSON, 但是对于文件上传的接口,打印参数会报错,换为 Gson) --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>${gson.version}</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency></dependencies>
test-invoice-common
新增公共类-HttpUtils
package com.test.invoice.util;import cn.hutool.http.Header;import cn.hutool.http.HttpRequest;import cn.hutool.http.HttpUtil;import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;import javax.servlet.http.HttpServletRequest;import java.net.InetSocketAddress;import java.net.Proxy;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;/*** HTTP 代理工具类* 无业务需要不要使用此代理类,直接使用hutool工具包中的httputil** @author:* @version:* @date: 2019-10-09 16:29*/@Slf4jpublic class HttpUtils {/*** send post** @param agentHost 代理地址* @param agentPort 代理端口* @param params 包体* @return java.lang.String* @author* @description //TODO* @date 15:42 2019/4/18* @params * @param url**/public static String post(String agentUrl, String agentHost, Integer agentPort, Object params) {return HttpRequest.post(agentUrl).header(Header.USER_AGENT,"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36").setProxy(getProxy(agentHost, agentPort)).body(JSON.toJSONString(params)).execute().body();}/*** send post** @param agentHost 代理地址* @param agentPort 代理端口* @return java.lang.String* @author* @description //TODO* @date 15:43 2019/4/18* @params * @param url**/public static String post(String agentUrl, String agentHost, Integer agentPort) {return post(agentUrl, agentHost, agentPort, null);}/*** send get** @param agentHost 代理地址* @param agentPort 代理端口* @return java.lang.String* @author* @description //TODO* @date 15:43 2019/4/18* @params * @param url**/public static String get(String agentUrl, String agentHost, Integer agentPort, Object params) {return HttpRequest.get(agentUrl).header(Header.USER_AGENT,"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36").setProxy(getProxy(agentHost, agentPort)).body(JSON.toJSONString(params)).execute().body();}/*** send get** @param agentHost 代理地址* @param agentPort 代理端口* @return java.lang.String* @author* @description //TODO* @date 15:43 2019/4/18* @params * @param url**/public static String get(String agentUrl, String agentHost, Integer agentPort) {return get(agentUrl, agentHost, agentPort, null);}private static Proxy getProxy(String agentHost, Integer agentPort) {InetSocketAddress addr = new InetSocketAddress(agentHost, agentPort);return new Proxy(Proxy.Type.HTTP, addr);}/*** 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值** @return ip*/public static String getIpAddr(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");log.info("x-forwarded-for ip: " + ip);if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif (ip.indexOf(",") != -1) {ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");log.info("Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");log.info("WL-Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");log.info("HTTP_CLIENT_IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");log.info("HTTP_X_FORWARDED_FOR ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");log.info("X-Real-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();log.info("getRemoteAddr ip: " + ip);}log.info("获取客户端ip: " + ip);return ip;}public static Map<Object, Object> getHeaders(HttpServletRequest request) {Map<Object, Object> headers = new HashMap();Enumeration<String> headerNames = request.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String name = headerNames.nextElement();Enumeration<String> values = request.getHeaders(name);while (values.hasMoreElements()) {String value = values.nextElement();headers.put(name, value);}}}return headers;}public static void main(String[] args) {String url = "https://www.baidu.com";String rtn = HttpUtil.get(url);//get(url, "web-proxy.tencent.com", 8080);System.out.println("########" + rtn);}}
新增公共类-MapperUtils
package com.test.invoice.util;import org.dozer.DozerBeanMapperBuilder;import org.dozer.Mapper;import org.springframework.util.Assert;import java.util.ArrayList;import java.util.List;/*** 对象转换类** @author: v_weboyang* @version:* @date: 2019/3/13 18:13*/public class MapperUtils {private static final Mapper MAPPER = DozerBeanMapperBuilder.buildDefault();public static <T, S> T convert(S source, T target) {if (source == null) {return null;}Assert.notNull(target, "target instance required");MAPPER.map(source, target);return target;}public static <T, S> T convert(S source, Class<T> targetClass) {if (source == null) {return null;}Assert.notNull(targetClass, "targetClass required");return MAPPER.map(source, targetClass);}public static <T, S> List<T> convert(List<S> source, Class<T> targetClass) {if (source == null) {return null;}List<T> targetList = new ArrayList<>();for (S s : source) {T target = MAPPER.map(s, targetClass);targetList.add(target);}return targetList;}}
test-invoice-web
application.yml
添加以下配置
logging:config: classpath:logback-dev.xml
新增文件-logback-dev.xml
<?xml version="1.0" encoding="UTF-8"?><configuration scan="true" scanPeriod="60 seconds" debug="false"><contextName>logback</contextName><property name="log.path" value="/v_hwhao/test-invoice-cloud/logs/test-invoice-web/logs"/><!--输出到控制台 --><appender name="console"class="ch.qos.logback.core.ConsoleAppender"><!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level></filter> --><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- info日志 --><appender name="fileInfoLog"class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 过滤日志 --><file>${log.path}/blockchain-invoice-api.log</file><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>DENY</onMatch> <!-- 如果命中就禁止这条日志 --><onMismatch>ACCEPT</onMismatch> <!-- 如果没有命中就使用这条规则 --></filter><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern></encoder><!-- 滚动策略 --><rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 设置info日志路径 --><fileNamePattern>${log.path}/blockchain-invoice-api.logback.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy></appender><!-- error日志 --><appender name="fileErrorLog"class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}/blockchain-invoice-api.error.log</file><!-- 过滤日志 --><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>ERROR</level></filter><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern></encoder><!-- 滚动策略 --><rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 设置error日志路径 --><fileNamePattern>${log.path}/blockchain-invoice-api.error.logback.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy></appender><!-- all日志 --><appender name="fileAllLog"class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 过滤日志 --><file>${log.path}/blockchain-invoice-api.all.log</file><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern></encoder><!-- 滚动策略 --><rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 设置info日志路径 --><fileNamePattern>${log.path}/blockchain-invoice-api.all.logback.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy></appender><root level="info"><appender-ref ref="console"/><appender-ref ref="fileInfoLog"/><appender-ref ref="fileErrorLog"/><appender-ref ref="fileAllLog"/></root></configuration>
TRbtTestController-Get()方法修改
@Slf4jpublic class TRbtTestController{@PostMapping("/Test/Get")@ApiOperation(value = "系统框架测试-获取单个数据", httpMethod = "POST", response = ResponseVO.class, notes = "系统框架测试-获取单个数据")public ResponseVO Get(@RequestBody TRbtTestData data){GsonBuilder builder = new GsonBuilder();Gson gson = builder.create();log.info("请求方法 Get(),请求参数:"+ gson.toJson(data));ResponseVO result = testConsumer.Get(data);log.info("请求方法 Get(),返回数据:"+ gson.toJson(result));return result;}}
新增类-WebLogAspect
package com.test.invoice.aspect;import com.google.common.collect.Maps;import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.test.invoice.util.HttpUtils;import com.test.invoice.util.Serializer;import lombok.extern.slf4j.Slf4j;import net.bytebuddy.asm.Advice;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Map;/*** web 请求,响应日志记录类** @author: v_hwhao* @version:* @date: 2019-10-09 16:25*/@Aspect@Component@Slf4jpublic class WebLogAspect {ThreadLocal<Long> startTime = new ThreadLocal<>();Map params = Maps.newLinkedHashMap();/*** 以 controller 包下定义的所有请求为切入点*/@Pointcut("execution(public * com.test.invoice.controller..*.*(..))")public void webLog() {}/*** 在切点之前织入** @param joinPoint* @throws Throwable*/@Before("webLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {// 开始打印请求日志startTime.set(System.currentTimeMillis());ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();params.put("【url】", request.getRequestURL()); // 获取请求的urlparams.put("【method】", request.getMethod()); // 获取请求的方式params.put("【ip】", HttpUtils.getIpAddr(request)); // 获取请求的ip地址params.put("【className】", joinPoint.getSignature().getDeclaringTypeName()); // 获取类名params.put("【classMethod】", joinPoint.getSignature().getName()); // 获取类方法params.put("【request args】", Serializer.serialize(joinPoint.getArgs())); // 请求参数}/*** 在切点之后织入** @throws Throwable*/@After("webLog()")public void doAfter() throws Throwable {// 输出格式化后的json字符串Gson gson = new GsonBuilder().setPrettyPrinting().create();log.info(gson.toJson(params));//清空每次内容params.clear();// 每个请求之间空一行log.info("");}/*** 环绕** @param proceedingJoinPoint* @return* @throws Throwable*/@Around("webLog()")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {Object result = proceedingJoinPoint.proceed();params.put("【response args】", result); // 响应回包参数params.put("【spend time】", (System.currentTimeMillis() - startTime.get()) + "ms"); // 响应回包参数return result;}@AfterReturning(value = "webLog()", returning = "returnValue")public void doRetrun(JoinPoint point, Object returnValue){}}
测试
使用 Postman 访问 Get 接口
地址:http://localhost:8080/Inv/Api/Test/Get
Body raw 参数:"id":"","name":"测试数据-菲克-1","version":""}
在目录 D:\v_hwhao\test-invoice-cloud\logs\test-invoice-web\logs 生成了下面文件
blockchain-invoice-api.all.logblockchain-invoice-api.error.logblockchain-invoice-api.log
打开 blockchain-invoice-api.log,部分内容如下
2019-10-09 17:09:37.396 logback [] INFO c.t.i.controller.TRbtTestController - 请求方法 Get(),请求参数:{"id":"","name":"测试数据-菲克-1","version":""}2019-10-09 17:09:37.436 logback [] INFO c.t.i.controller.TRbtTestController - 返回数据:{"code":2000,"msg":"Success","data":{"id":"","name":"测试数据-菲克-1","version":"1.0.1"},"timestamp":1570612177430}2019-10-09 16:49:26.429 logback [] INFO com.test.invoice.aspect.WebLogAspect - {"【url】": "http://localhost:8080/Inv/Api/Test/Get","【method】": "POST","【ip】": "127.0.0.1","【className】": "com.test.invoice.controller.TRbtTestController","【classMethod】": "Get","【request args】": "[{\"id\":\"\",\"name\":\"测试数据-菲克-1\",\"version\":\"\"}]","【response args】": {"code": 2000,"msg": "Success","data": {"id": "","name": "测试数据-菲克-1","version": "1.0.1"},"timestamp": 1570610966375},"【spend time】": "131ms"}
可以看到自定义和自动生成的日志都在里面进行记录