2.引用 lombok 使用 log

2019年10月09日 16:54 · 阅读(911) ·

开发环境

名称 版本
操作系统 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

迁移说明

本文的内容出现了很多多余的内容,不利于快速阅读

快速了解,请参考

使用 Lombok log 管理项目日志

代码说明

本文代码基于 4.Spring Cloud-Hystrix Dashboard监控数据聚合(Turbine) 项目代码进行修改

文件结构

新增/修改下面选中的文件

test-invoice-web

test-invoice-common

pom.xml(test-invoice-clound)

新加入下面内容

  1. <properties>
  2. <lombok.version>1.18.6</lombok.version>
  3. <gson.version>2.8.5</gson.version>
  4. <hutool.version>4.5.1</hutool.version>
  5. </properties>
  6. <dependencies>
  7. <!--lombok start-->
  8. <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
  9. <dependency>
  10. <groupId>org.projectlombok</groupId>
  11. <artifactId>lombok</artifactId>
  12. <version>${lombok.version}</version>
  13. </dependency>
  14. <!--lombok end-->
  15. <!-- 用于日志切面中,以 json 格式打印出入参(本来使用阿里的 FASTJSON, 但是对于文件上传的接口,打印参数会报错,换为 Gson) -->
  16. <dependency>
  17. <groupId>com.google.code.gson</groupId>
  18. <artifactId>gson</artifactId>
  19. <version>${gson.version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>cn.hutool</groupId>
  23. <artifactId>hutool-all</artifactId>
  24. <version>${hutool.version}</version>
  25. </dependency>
  26. </dependencies>

test-invoice-common

新增公共类-HttpUtils

  1. package com.test.invoice.util;
  2. import cn.hutool.http.Header;
  3. import cn.hutool.http.HttpRequest;
  4. import cn.hutool.http.HttpUtil;
  5. import com.alibaba.fastjson.JSON;
  6. import lombok.extern.slf4j.Slf4j;
  7. import javax.servlet.http.HttpServletRequest;
  8. import java.net.InetSocketAddress;
  9. import java.net.Proxy;
  10. import java.util.Enumeration;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. /**
  14. * HTTP 代理工具类
  15. * 无业务需要不要使用此代理类,直接使用hutool工具包中的httputil
  16. *
  17. * @author:
  18. * @version:
  19. * @date: 2019-10-09 16:29
  20. */
  21. @Slf4j
  22. public class HttpUtils {
  23. /**
  24. * send post
  25. *
  26. * @param agentHost 代理地址
  27. * @param agentPort 代理端口
  28. * @param params 包体
  29. * @return java.lang.String
  30. * @author
  31. * @description //TODO
  32. * @date 15:42 2019/4/18
  33. * @params * @param url
  34. **/
  35. public static String post(String agentUrl, String agentHost, Integer agentPort, Object params) {
  36. return HttpRequest.post(agentUrl)
  37. .header(Header.USER_AGENT,
  38. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36")
  39. .setProxy(getProxy(agentHost, agentPort)).body(JSON.toJSONString(params)).execute().body();
  40. }
  41. /**
  42. * send post
  43. *
  44. * @param agentHost 代理地址
  45. * @param agentPort 代理端口
  46. * @return java.lang.String
  47. * @author
  48. * @description //TODO
  49. * @date 15:43 2019/4/18
  50. * @params * @param url
  51. **/
  52. public static String post(String agentUrl, String agentHost, Integer agentPort) {
  53. return post(agentUrl, agentHost, agentPort, null);
  54. }
  55. /**
  56. * send get
  57. *
  58. * @param agentHost 代理地址
  59. * @param agentPort 代理端口
  60. * @return java.lang.String
  61. * @author
  62. * @description //TODO
  63. * @date 15:43 2019/4/18
  64. * @params * @param url
  65. **/
  66. public static String get(String agentUrl, String agentHost, Integer agentPort, Object params) {
  67. return HttpRequest.get(agentUrl)
  68. .header(Header.USER_AGENT,
  69. "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36")
  70. .setProxy(getProxy(agentHost, agentPort)).body(JSON.toJSONString(params)).execute().body();
  71. }
  72. /**
  73. * send get
  74. *
  75. * @param agentHost 代理地址
  76. * @param agentPort 代理端口
  77. * @return java.lang.String
  78. * @author
  79. * @description //TODO
  80. * @date 15:43 2019/4/18
  81. * @params * @param url
  82. **/
  83. public static String get(String agentUrl, String agentHost, Integer agentPort) {
  84. return get(agentUrl, agentHost, agentPort, null);
  85. }
  86. private static Proxy getProxy(String agentHost, Integer agentPort) {
  87. InetSocketAddress addr = new InetSocketAddress(agentHost, agentPort);
  88. return new Proxy(Proxy.Type.HTTP, addr);
  89. }
  90. /**
  91. * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
  92. * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值
  93. *
  94. * @return ip
  95. */
  96. public static String getIpAddr(HttpServletRequest request) {
  97. String ip = request.getHeader("x-forwarded-for");
  98. log.info("x-forwarded-for ip: " + ip);
  99. if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
  100. // 多次反向代理后会有多个ip值,第一个ip才是真实ip
  101. if (ip.indexOf(",") != -1) {
  102. ip = ip.split(",")[0];
  103. }
  104. }
  105. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  106. ip = request.getHeader("Proxy-Client-IP");
  107. log.info("Proxy-Client-IP ip: " + ip);
  108. }
  109. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  110. ip = request.getHeader("WL-Proxy-Client-IP");
  111. log.info("WL-Proxy-Client-IP ip: " + ip);
  112. }
  113. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  114. ip = request.getHeader("HTTP_CLIENT_IP");
  115. log.info("HTTP_CLIENT_IP ip: " + ip);
  116. }
  117. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  118. ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  119. log.info("HTTP_X_FORWARDED_FOR ip: " + ip);
  120. }
  121. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  122. ip = request.getHeader("X-Real-IP");
  123. log.info("X-Real-IP ip: " + ip);
  124. }
  125. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  126. ip = request.getRemoteAddr();
  127. log.info("getRemoteAddr ip: " + ip);
  128. }
  129. log.info("获取客户端ip: " + ip);
  130. return ip;
  131. }
  132. public static Map<Object, Object> getHeaders(HttpServletRequest request) {
  133. Map<Object, Object> headers = new HashMap();
  134. Enumeration<String> headerNames = request.getHeaderNames();
  135. if (headerNames != null) {
  136. while (headerNames.hasMoreElements()) {
  137. String name = headerNames.nextElement();
  138. Enumeration<String> values = request.getHeaders(name);
  139. while (values.hasMoreElements()) {
  140. String value = values.nextElement();
  141. headers.put(name, value);
  142. }
  143. }
  144. }
  145. return headers;
  146. }
  147. public static void main(String[] args) {
  148. String url = "https://www.baidu.com";
  149. String rtn = HttpUtil.get(url);//get(url, "web-proxy.tencent.com", 8080);
  150. System.out.println("########" + rtn);
  151. }
  152. }

新增公共类-MapperUtils

  1. package com.test.invoice.util;
  2. import org.dozer.DozerBeanMapperBuilder;
  3. import org.dozer.Mapper;
  4. import org.springframework.util.Assert;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /**
  8. * 对象转换类
  9. *
  10. * @author: v_weboyang
  11. * @version:
  12. * @date: 2019/3/13 18:13
  13. */
  14. public class MapperUtils {
  15. private static final Mapper MAPPER = DozerBeanMapperBuilder.buildDefault();
  16. public static <T, S> T convert(S source, T target) {
  17. if (source == null) {
  18. return null;
  19. }
  20. Assert.notNull(target, "target instance required");
  21. MAPPER.map(source, target);
  22. return target;
  23. }
  24. public static <T, S> T convert(S source, Class<T> targetClass) {
  25. if (source == null) {
  26. return null;
  27. }
  28. Assert.notNull(targetClass, "targetClass required");
  29. return MAPPER.map(source, targetClass);
  30. }
  31. public static <T, S> List<T> convert(List<S> source, Class<T> targetClass) {
  32. if (source == null) {
  33. return null;
  34. }
  35. List<T> targetList = new ArrayList<>();
  36. for (S s : source) {
  37. T target = MAPPER.map(s, targetClass);
  38. targetList.add(target);
  39. }
  40. return targetList;
  41. }
  42. }

test-invoice-web

application.yml

添加以下配置

  1. logging:
  2. config: classpath:logback-dev.xml

新增文件-logback-dev.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration scan="true" scanPeriod="60 seconds" debug="false">
  3. <contextName>logback</contextName>
  4. <property name="log.path" value="/v_hwhao/test-invoice-cloud/logs/test-invoice-web/logs"/>
  5. <!--输出到控制台 -->
  6. <appender name="console"
  7. class="ch.qos.logback.core.ConsoleAppender">
  8. <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level>
  9. </filter> -->
  10. <encoder>
  11. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>
  12. </encoder>
  13. </appender>
  14. <!-- info日志 -->
  15. <appender name="fileInfoLog"
  16. class="ch.qos.logback.core.rolling.RollingFileAppender">
  17. <!-- 过滤日志 -->
  18. <file>${log.path}/blockchain-invoice-api.log</file>
  19. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  20. <level>ERROR</level>
  21. <onMatch>DENY</onMatch> <!-- 如果命中就禁止这条日志 -->
  22. <onMismatch>ACCEPT</onMismatch> <!-- 如果没有命中就使用这条规则 -->
  23. </filter>
  24. <encoder>
  25. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>
  26. </encoder>
  27. <!-- 滚动策略 -->
  28. <rollingPolicy
  29. class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  30. <!-- 设置info日志路径 -->
  31. <fileNamePattern>${log.path}/blockchain-invoice-api.logback.%d{yyyy-MM-dd}.log
  32. </fileNamePattern>
  33. </rollingPolicy>
  34. </appender>
  35. <!-- error日志 -->
  36. <appender name="fileErrorLog"
  37. class="ch.qos.logback.core.rolling.RollingFileAppender">
  38. <file>${log.path}/blockchain-invoice-api.error.log</file>
  39. <!-- 过滤日志 -->
  40. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  41. <level>ERROR</level>
  42. </filter>
  43. <encoder>
  44. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>
  45. </encoder>
  46. <!-- 滚动策略 -->
  47. <rollingPolicy
  48. class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  49. <!-- 设置error日志路径 -->
  50. <fileNamePattern>${log.path}/blockchain-invoice-api.error.logback.%d{yyyy-MM-dd}.log
  51. </fileNamePattern>
  52. </rollingPolicy>
  53. </appender>
  54. <!-- all日志 -->
  55. <appender name="fileAllLog"
  56. class="ch.qos.logback.core.rolling.RollingFileAppender">
  57. <!-- 过滤日志 -->
  58. <file>${log.path}/blockchain-invoice-api.all.log</file>
  59. <encoder>
  60. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%X{requestId}] %-5level %logger{36} - %msg%n</pattern>
  61. </encoder>
  62. <!-- 滚动策略 -->
  63. <rollingPolicy
  64. class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  65. <!-- 设置info日志路径 -->
  66. <fileNamePattern>${log.path}/blockchain-invoice-api.all.logback.%d{yyyy-MM-dd}.log
  67. </fileNamePattern>
  68. </rollingPolicy>
  69. </appender>
  70. <root level="info">
  71. <appender-ref ref="console"/>
  72. <appender-ref ref="fileInfoLog"/>
  73. <appender-ref ref="fileErrorLog"/>
  74. <appender-ref ref="fileAllLog"/>
  75. </root>
  76. </configuration>

TRbtTestController-Get()方法修改

  1. @Slf4j
  2. public class TRbtTestController{
  3. @PostMapping("/Test/Get")
  4. @ApiOperation(value = "系统框架测试-获取单个数据", httpMethod = "POST", response = ResponseVO.class, notes = "系统框架测试-获取单个数据")
  5. public ResponseVO Get(@RequestBody TRbtTestData data){
  6. GsonBuilder builder = new GsonBuilder();
  7. Gson gson = builder.create();
  8. log.info("请求方法 Get(),请求参数:"+ gson.toJson(data));
  9. ResponseVO result = testConsumer.Get(data);
  10. log.info("请求方法 Get(),返回数据:"+ gson.toJson(result));
  11. return result;
  12. }
  13. }

新增类-WebLogAspect

  1. package com.test.invoice.aspect;
  2. import com.google.common.collect.Maps;
  3. import com.google.gson.Gson;
  4. import com.google.gson.GsonBuilder;
  5. import com.test.invoice.util.HttpUtils;
  6. import com.test.invoice.util.Serializer;
  7. import lombok.extern.slf4j.Slf4j;
  8. import net.bytebuddy.asm.Advice;
  9. import org.aspectj.lang.JoinPoint;
  10. import org.aspectj.lang.ProceedingJoinPoint;
  11. import org.aspectj.lang.annotation.*;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.context.request.RequestContextHolder;
  14. import org.springframework.web.context.request.ServletRequestAttributes;
  15. import javax.servlet.http.HttpServletRequest;
  16. import java.util.Map;
  17. /**
  18. * web 请求,响应日志记录类
  19. *
  20. * @author: v_hwhao
  21. * @version:
  22. * @date: 2019-10-09 16:25
  23. */
  24. @Aspect
  25. @Component
  26. @Slf4j
  27. public class WebLogAspect {
  28. ThreadLocal<Long> startTime = new ThreadLocal<>();
  29. Map params = Maps.newLinkedHashMap();
  30. /**
  31. * 以 controller 包下定义的所有请求为切入点
  32. */
  33. @Pointcut("execution(public * com.test.invoice.controller..*.*(..))")
  34. public void webLog() {
  35. }
  36. /**
  37. * 在切点之前织入
  38. *
  39. * @param joinPoint
  40. * @throws Throwable
  41. */
  42. @Before("webLog()")
  43. public void doBefore(JoinPoint joinPoint) throws Throwable {
  44. // 开始打印请求日志
  45. startTime.set(System.currentTimeMillis());
  46. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  47. HttpServletRequest request = attributes.getRequest();
  48. params.put("【url】", request.getRequestURL()); // 获取请求的url
  49. params.put("【method】", request.getMethod()); // 获取请求的方式
  50. params.put("【ip】", HttpUtils.getIpAddr(request)); // 获取请求的ip地址
  51. params.put("【className】", joinPoint.getSignature().getDeclaringTypeName()); // 获取类名
  52. params.put("【classMethod】", joinPoint.getSignature().getName()); // 获取类方法
  53. params.put("【request args】", Serializer.serialize(joinPoint.getArgs())); // 请求参数
  54. }
  55. /**
  56. * 在切点之后织入
  57. *
  58. * @throws Throwable
  59. */
  60. @After("webLog()")
  61. public void doAfter() throws Throwable {
  62. // 输出格式化后的json字符串
  63. Gson gson = new GsonBuilder().setPrettyPrinting().create();
  64. log.info(gson.toJson(params));
  65. //清空每次内容
  66. params.clear();
  67. // 每个请求之间空一行
  68. log.info("");
  69. }
  70. /**
  71. * 环绕
  72. *
  73. * @param proceedingJoinPoint
  74. * @return
  75. * @throws Throwable
  76. */
  77. @Around("webLog()")
  78. public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  79. Object result = proceedingJoinPoint.proceed();
  80. params.put("【response args】", result); // 响应回包参数
  81. params.put("【spend time】", (System.currentTimeMillis() - startTime.get()) + "ms"); // 响应回包参数
  82. return result;
  83. }
  84. @AfterReturning(value = "webLog()", returning = "returnValue")
  85. public void doRetrun(JoinPoint point, Object returnValue){
  86. }
  87. }

测试

使用 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 生成了下面文件

  1. blockchain-invoice-api.all.log
  2. blockchain-invoice-api.error.log
  3. blockchain-invoice-api.log

打开 blockchain-invoice-api.log,部分内容如下

  1. 2019-10-09 17:09:37.396 logback [] INFO c.t.i.controller.TRbtTestController - 请求方法 Get(),请求参数:{"id":"","name":"测试数据-菲克-1","version":""}
  2. 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}
  3. 2019-10-09 16:49:26.429 logback [] INFO com.test.invoice.aspect.WebLogAspect - {
  4. "【url】": "http://localhost:8080/Inv/Api/Test/Get",
  5. "【method】": "POST",
  6. "【ip】": "127.0.0.1",
  7. "【className】": "com.test.invoice.controller.TRbtTestController",
  8. "【classMethod】": "Get",
  9. "【request args】": "[{\"id\":\"\",\"name\":\"测试数据-菲克-1\",\"version\":\"\"}]",
  10. "【response args】": {
  11. "code": 2000,
  12. "msg": "Success",
  13. "data": {
  14. "id": "",
  15. "name": "测试数据-菲克-1",
  16. "version": "1.0.1"
  17. },
  18. "timestamp": 1570610966375
  19. },
  20. "【spend time】": "131ms"
  21. }

可以看到自定义和自动生成的日志都在里面进行记录