直接使用sl4j的注解接口使用不同的日志工具。

spring - log4j

maven依赖

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-log4j12</artifactId>
  4. <version>1.7.21</version>
  5. </dependency>

配置

log4j.properties

  1. log4j.rootLogger=DEBUG,console,dailyFile
  2. # 控制台(console)
  3. log4j.appender.console=org.apache.log4j.ConsoleAppender
  4. log4j.appender.console.Threshold=DEBUG
  5. log4j.appender.console.ImmediateFlush=true
  6. log4j.appender.console.Target=System.err
  7. log4j.appender.console.layout=org.apache.log4j.PatternLayout
  8. log4j.appender.console.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n
  9. # 日志文件按天保存,不删除
  10. log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppender
  11. log4j.appender.dailyFile.Threshold=DEBUG
  12. log4j.appender.dailyFile.ImmediateFlush=true
  13. log4j.appender.dailyFile.Append=true
  14. log4j.appender.dailyFile.File=file.log
  15. log4j.appender.dailyFile.DatePattern='.'yyyy-MM-dd
  16. log4j.appender.dailyFile.layout=org.apache.log4j.PatternLayout
  17. log4j.appender.dailyFile.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n
  18. # 日志文件保存到一个文件,不常用
  19. log4j.appender.logFile=org.apache.log4j.FileAppender
  20. log4j.appender.logFile.Threshold=DEBUG
  21. log4j.appender.logFile.ImmediateFlush=true
  22. log4j.appender.logFile.Append=true
  23. log4j.appender.logFile.File=D:/logs/log.log4j
  24. log4j.appender.logFile.layout=org.apache.log4j.PatternLayout
  25. log4j.appender.logFile.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n

使用

  1. Logger logger = LoggerFactory.getLogger(LogTest.class);
  2. logger.debug("123");
  3. # 效果:
  4. 20190622 16:11:42 [DEBUG] LogTest.main 123

自定义append

对于某些不重要的日志,我们可以设置保留最近一周,我们需要自定义一个方法,改进如下:

  1. package util;
  2. import org.apache.log4j.FileAppender;
  3. import org.apache.log4j.Layout;
  4. import org.apache.log4j.helpers.LogLog;
  5. import org.apache.log4j.spi.LoggingEvent;
  6. import java.io.File;
  7. import java.io.IOException;
  8. import java.text.SimpleDateFormat;
  9. import java.util.ArrayList;
  10. import java.util.Calendar;
  11. import java.util.Date;
  12. import java.util.List;
  13. /**
  14. * @Description
  15. * @Author 田云
  16. * @Date 2019/6/22 16:03
  17. * @Version 1.0
  18. */
  19. public class CustomLogAppender extends FileAppender {
  20. /** 不允许改写的datepattern */
  21. private static final String datePattern = "'.'yyyy-MM-dd";
  22. /** 最多文件增长个数 */
  23. private int maxBackupIndex = 2;
  24. /** 文件名+上次最后更新时间 */
  25. private String scheduledFilename;
  26. /**
  27. * The next time we estimate a rollover should occur.
  28. */
  29. private long nextCheck = System.currentTimeMillis() - 1;
  30. Date now = new Date();
  31. SimpleDateFormat sdf;
  32. /**
  33. * The default constructor does nothing.
  34. */
  35. public CustomLogAppender() {
  36. }
  37. /**
  38. * 改造过的构造器
  39. */
  40. public CustomLogAppender(Layout layout, String filename, int maxBackupIndex) throws IOException {
  41. super(layout, filename, true);
  42. this.maxBackupIndex = maxBackupIndex;
  43. activateOptions();
  44. }
  45. /** * 初始化本Appender对象的时候调用一次 */
  46. @Override
  47. public void activateOptions() {
  48. super.activateOptions();
  49. if (fileName != null) {
  50. // perf.log now.setTime(System.currentTimeMillis());
  51. sdf = new SimpleDateFormat(datePattern);
  52. File file = new File(fileName);
  53. // 获取最后更新时间拼成的文件名
  54. scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));
  55. } else {
  56. LogLog.error("File is not set for appender [" + name + "].");
  57. } if (maxBackupIndex <= 0) {
  58. LogLog.error("maxBackupIndex reset to default value[2],orignal value is:" + maxBackupIndex);
  59. maxBackupIndex = 2;
  60. }
  61. }
  62. /**
  63. * 滚动文件的函数:<br>
  64. * 1. 对文件名带的时间戳进行比较, 确定是否更新<br>
  65. * 2. if需要更新, 当前文件rename到文件名+日期, 重新开始写文件<br>
  66. * 3. 针对配置的maxBackupIndex,删除过期的文件
  67. */
  68. void rollOver() throws IOException {
  69. String datedFilename = fileName + sdf.format(now);
  70. // 如果上次写的日期跟当前日期相同,不需要换文件
  71. if (scheduledFilename.equals(datedFilename)) {
  72. return;
  73. }
  74. // close current file, and rename it to datedFilename
  75. this.closeFile();
  76. File target = new File(scheduledFilename);
  77. if (target.exists()) {
  78. try {
  79. target.delete();
  80. } catch (SecurityException e) {
  81. e.printStackTrace();
  82. }
  83. }
  84. File file = new File(fileName);
  85. boolean result = file.renameTo(target);
  86. if (result) {
  87. LogLog.debug(fileName + " -> " + scheduledFilename);
  88. } else {
  89. LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].");
  90. }
  91. // 删除过期文件
  92. if (maxBackupIndex > 0) {
  93. File folder = new File(file.getParent());
  94. List<String> maxBackupIndexDates = getMaxBackupIndexDates();
  95. for (File ff : folder.listFiles()) {
  96. // 遍历目录,将日期不在备份范围内的日志删掉
  97. if (ff.getName().startsWith(file.getName()) && !ff.getName().equals(file.getName())) {
  98. // 获取文件名带的日期时间戳
  99. String markedDate = ff.getName().substring( file.getName().length());
  100. if (!maxBackupIndexDates.contains(markedDate)) {
  101. result = ff.delete();
  102. }
  103. if (result) {
  104. LogLog.debug(ff.getName() + " -> deleted ");
  105. } else {
  106. LogLog.error("Failed to deleted old DayRollingFileAppender file :" + ff.getName());
  107. }
  108. }
  109. }
  110. }
  111. try {
  112. // This will also close the file. This is OK since multiple
  113. // close operations are safe.
  114. this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
  115. } catch (IOException e) {
  116. errorHandler.error("setFile(" + fileName + ", false) call failed.");
  117. }
  118. scheduledFilename = datedFilename;
  119. // 更新最后更新日期戳
  120. }
  121. /**
  122. * Actual writing occurs here.
  123. * 这个方法是写操作真正的执行过程.
  124. */
  125. @Override
  126. protected void subAppend(LoggingEvent event) {
  127. long n = System.currentTimeMillis();
  128. if (n >= nextCheck) {
  129. // 在每次写操作前判断一下是否需要滚动文件
  130. now.setTime(n);
  131. nextCheck = getNextDayCheckPoint(now);
  132. try {
  133. rollOver();
  134. } catch (IOException ioe) {
  135. LogLog.error("rollOver() failed.", ioe);
  136. }
  137. } super.subAppend(event);
  138. }
  139. /**
  140. * 获取下一天的时间变更点
  141. *
  142. * @param now
  143. * @return
  144. */
  145. long getNextDayCheckPoint(Date now) {
  146. Calendar calendar = Calendar.getInstance();
  147. calendar.setTime(now);
  148. calendar.set(Calendar.HOUR_OF_DAY, 0);
  149. calendar.set(Calendar.MINUTE, 0);
  150. calendar.set(Calendar.SECOND, 0);
  151. calendar.set(Calendar.MILLISECOND, 0);// 注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的 calendar.add(Calendar.DATE, 1);
  152. return calendar.getTimeInMillis();
  153. }
  154. /**
  155. * 根据maxBackupIndex配置的备份文件个数,获取要保留log文件的日期范围集合
  156. * @return list<'fileName+yyyy-MM-dd'>
  157. */
  158. List<String> getMaxBackupIndexDates() {
  159. List<String> result = new ArrayList<String>();
  160. if (maxBackupIndex > 0) {
  161. for (int i = 1; i <= maxBackupIndex; i++) {
  162. Calendar calendar = Calendar.getInstance();
  163. calendar.setTime(now);
  164. calendar.set(Calendar.HOUR_OF_DAY, 0);
  165. calendar.set(Calendar.MINUTE, 0);
  166. calendar.set(Calendar.SECOND, 0);
  167. calendar.set(Calendar.MILLISECOND, 0);
  168. // 注意MILLISECOND,毫秒也要置0...否则错了也找不出来的
  169. calendar.add(Calendar.DATE, -i);
  170. result.add(sdf.format(calendar.getTime()));
  171. }
  172. }
  173. return result;
  174. }
  175. public int getMaxBackupIndex() {
  176. return maxBackupIndex;
  177. }
  178. public void setMaxBackupIndex(int maxBackupIndex) {
  179. this.maxBackupIndex = maxBackupIndex;
  180. }
  181. public String getDatePattern() {
  182. return datePattern;
  183. }
  184. }

自定义的配置

  1. # 按天存储,保存7天(日志文件不需要全部保留的情况)
  2. log4j.appender.customer=util.CustomLogAppender
  3. log4j.appender.customer.File=file.log
  4. log4j.appender.customer.maxBackupIndex=7
  5. log4j.appender.customer.layout=org.apache.log4j.PatternLayout
  6. log4j.appender.customer.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n

springboot - logback

1、引用,web的引用里面已经引入了。

2、配置文件

logback-spring.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="logback.logdir" value="./log/"/>
  5. <property name="logback.appname" value="java"/>
  6. <!--输出到控制台 ConsoleAppender-->
  7. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  8. <!--展示格式 layout-->
  9. <layout class="ch.qos.logback.classic.PatternLayout">
  10. <pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern>
  11. </layout>
  12. </appender>
  13. <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
  14. <!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高,
  15. 所以我们使用下面的策略,可以避免输出 Error 的日志-->
  16. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  17. <!--过滤 Error-->
  18. <level>ERROR</level>
  19. <!--匹配到就禁止-->
  20. <onMatch>DENY</onMatch>
  21. <!--没有匹配到就允许-->
  22. <onMismatch>ACCEPT</onMismatch>
  23. </filter>
  24. <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
  25. 如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
  26. 的日志改名为今天的日期。即,<File> 的日志都是当天的。
  27. -->
  28. <File>${logback.logdir}/info.${logback.appname}.log</File>
  29. <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
  30. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  31. <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
  32. <FileNamePattern>${logback.logdir}/info.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern>
  33. <!--只保留最近90天的日志-->
  34. <maxHistory>90</maxHistory>
  35. <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
  36. <!--<totalSizeCap>1GB</totalSizeCap>-->
  37. </rollingPolicy>
  38. <!--日志输出编码格式化-->
  39. <encoder>
  40. <charset>UTF-8</charset>
  41. <pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern>
  42. </encoder>
  43. </appender>
  44. <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
  45. <!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
  46. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  47. <level>Error</level>
  48. </filter>
  49. <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
  50. 如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
  51. 的日志改名为今天的日期。即,<File> 的日志都是当天的。
  52. -->
  53. <File>${logback.logdir}/error.${logback.appname}.log</File>
  54. <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
  55. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  56. <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
  57. <FileNamePattern>${logback.logdir}/error.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern>
  58. <!--只保留最近90天的日志-->
  59. <maxHistory>90</maxHistory>
  60. <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
  61. <!--<totalSizeCap>1GB</totalSizeCap>-->
  62. </rollingPolicy>
  63. <!--日志输出编码格式化-->
  64. <encoder>
  65. <charset>UTF-8</charset>
  66. <pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern>
  67. </encoder>
  68. </appender>
  69. <!--指定最基础的日志输出级别-->
  70. <root level="INFO">
  71. <!--appender将会添加到这个loger-->
  72. <appender-ref ref="console"/>
  73. <appender-ref ref="fileInfoLog"/>
  74. <appender-ref ref="fileErrorLog"/>
  75. </root>
  76. </configuration>

3、使用

  1. @Slf4j
  2. public class LogServiceImpl implements LogService {
  3. public PageResult<LogModel> query(LogModelVO logModelVO) {
  4. log.info("123")
  5. }
  6. }

springboot - log4j2

推荐
引用

  1. <!--log4j2 依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-log4j2</artifactId>
  5. </dependency>
  6. <!--异步日志依赖-->
  7. <dependency>
  8. <groupId>com.lmax</groupId>
  9. <artifactId>disruptor</artifactId>
  10. <version>3.3.7</version>
  11. </dependency>

log4j2.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
  3. <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
  4. <!--monitorIntervalLog4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
  5. <configuration status="WARN" monitorInterval="30">
  6. <properties>
  7. <property name="logDir" value="log"/>
  8. </properties>
  9. <!--先定义所有的appender-->
  10. <appenders>
  11. <!--这个输出控制台的配置-->
  12. <console name="Console" target="SYSTEM_OUT">
  13. <!--输出日志的格式-->
  14. <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
  15. </console>
  16. <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
  17. <File name="log" fileName="${logDir}/test.log" append="false">
  18. <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
  19. </File>
  20. <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
  21. <RollingRandomAccessFile name="RollingFileInfo" fileName="${logDir}/info.log"
  22. filePattern="${logDir}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"
  23. immediateFlush="false">
  24. <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
  25. <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
  26. <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
  27. <Policies>
  28. <TimeBasedTriggeringPolicy/>
  29. <SizeBasedTriggeringPolicy size="500 MB"/>
  30. </Policies>
  31. </RollingRandomAccessFile>
  32. <RollingRandomAccessFile name="RollingFileWarn" fileName="${logDir}/warn.log"
  33. filePattern="${logDir}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"
  34. immediateFlush="false">
  35. <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
  36. <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
  37. <Policies>
  38. <TimeBasedTriggeringPolicy/>
  39. <SizeBasedTriggeringPolicy size="500 MB"/>
  40. </Policies>
  41. <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
  42. <DefaultRolloverStrategy max="20"/>
  43. </RollingRandomAccessFile>
  44. <RollingRandomAccessFile name="RollingFileError" fileName="${logDir}/error.log"
  45. filePattern="${logDir}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"
  46. immediateFlush="false">
  47. <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
  48. <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
  49. <Policies>
  50. <TimeBasedTriggeringPolicy/>
  51. <SizeBasedTriggeringPolicy size="500 MB"/>
  52. </Policies>
  53. </RollingRandomAccessFile>
  54. </appenders>
  55. <!--然后定义logger,只有定义了logger并引入的appenderappender才会生效-->
  56. <loggers>
  57. <!--过滤掉springmybatis的一些无用的DEBUG信息-->
  58. <logger name="org.springframework" level="INFO"/>
  59. <logger name="org.mybatis" level="INFO"/>
  60. <root level="all" includeLocation="true">
  61. <appender-ref ref="Console"/>
  62. <appender-ref ref="RollingFileInfo"/>
  63. <appender-ref ref="RollingFileWarn"/>
  64. <appender-ref ref="RollingFileError"/>
  65. </root>
  66. </loggers>
  67. </configuration>

启动函数

  1. system.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");

然后直接使用sl4j即可。

自定义输出目录

参考:https://giraffetree.me/2019/12/06/log4j2-slf4j-routing/

  • 配置 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

  1. 测试
  2. ```bash
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.apache.logging.log4j.ThreadContext;
  5. import org.slf4j.Marker;
  6. import org.slf4j.MarkerFactory;
  7. import java.util.concurrent.ThreadLocalRandom;
  8. /** * @author GiraffeTree * @date 2019/12/5 */
  9. @Slf4j
  10. public class Log4j2Test {
  11. private final static Marker BOY = MarkerFactory.getMarker("boy");
  12. private final static Marker GIRL = MarkerFactory.getMarker("girl");
  13. private final static Marker OTHER = MarkerFactory.getMarker("other");
  14. public static void main(String[] args) {
  15. testRouting(1000, 3);
  16. }
  17. public static void addRandomLog() {
  18. int num = ThreadLocalRandom.current().nextInt(0, 1000);
  19. if (num < 950) {
  20. ThreadContext.put("logFileName", "David");
  21. log.info("current: {}", num);
  22. } else {
  23. log.error("current: {}", num);
  24. }
  25. }
  26. public static void testRouting(int loopCount, int userSize) {
  27. long l1 = System.currentTimeMillis();
  28. int count = loopCount;
  29. while (count > 0) {
  30. writeMultipleUsersLog(userSize);
  31. count--;
  32. }
  33. long l2 = System.currentTimeMillis();
  34. System.out.println(String.format("size:%d loopCount:%d cost: %dms", userSize, loopCount, l2 - l1));
  35. }
  36. public static void writeMultipleUsersLog(int size) {
  37. for (int i = 0; i < size; i++) {
  38. String userName = "user" + i;
  39. ThreadContext.put("logFileName", userName);
  40. int num = ThreadLocalRandom.current().nextInt(0, 1200);
  41. if (num < 500) {
  42. log.info(BOY, "current: {}", num);
  43. } else if (num < 1000) {
  44. log.info(GIRL, "current: {}", num);
  45. } else {
  46. log.info(OTHER, "current: {}", num);
  47. }
  48. }
  49. }
  50. }