直接使用sl4j的注解接口使用不同的日志工具。
spring - log4j
maven依赖
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.21</version></dependency>
配置
log4j.properties
log4j.rootLogger=DEBUG,console,dailyFile# 控制台(console)log4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.ImmediateFlush=truelog4j.appender.console.Target=System.errlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n# 日志文件按天保存,不删除log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.dailyFile.Threshold=DEBUGlog4j.appender.dailyFile.ImmediateFlush=truelog4j.appender.dailyFile.Append=truelog4j.appender.dailyFile.File=file.loglog4j.appender.dailyFile.DatePattern='.'yyyy-MM-ddlog4j.appender.dailyFile.layout=org.apache.log4j.PatternLayoutlog4j.appender.dailyFile.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n# 日志文件保存到一个文件,不常用log4j.appender.logFile=org.apache.log4j.FileAppenderlog4j.appender.logFile.Threshold=DEBUGlog4j.appender.logFile.ImmediateFlush=truelog4j.appender.logFile.Append=truelog4j.appender.logFile.File=D:/logs/log.log4jlog4j.appender.logFile.layout=org.apache.log4j.PatternLayoutlog4j.appender.logFile.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n
使用
Logger logger = LoggerFactory.getLogger(LogTest.class);logger.debug("123");# 效果:20190622 16:11:42 [DEBUG] LogTest.main 123
自定义append
对于某些不重要的日志,我们可以设置保留最近一周,我们需要自定义一个方法,改进如下:
package util;import org.apache.log4j.FileAppender;import org.apache.log4j.Layout;import org.apache.log4j.helpers.LogLog;import org.apache.log4j.spi.LoggingEvent;import java.io.File;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Calendar;import java.util.Date;import java.util.List;/*** @Description* @Author 田云* @Date 2019/6/22 16:03* @Version 1.0*/public class CustomLogAppender extends FileAppender {/** 不允许改写的datepattern */private static final String datePattern = "'.'yyyy-MM-dd";/** 最多文件增长个数 */private int maxBackupIndex = 2;/** 文件名+上次最后更新时间 */private String scheduledFilename;/*** The next time we estimate a rollover should occur.*/private long nextCheck = System.currentTimeMillis() - 1;Date now = new Date();SimpleDateFormat sdf;/*** The default constructor does nothing.*/public CustomLogAppender() {}/*** 改造过的构造器*/public CustomLogAppender(Layout layout, String filename, int maxBackupIndex) throws IOException {super(layout, filename, true);this.maxBackupIndex = maxBackupIndex;activateOptions();}/** * 初始化本Appender对象的时候调用一次 */@Overridepublic void activateOptions() {super.activateOptions();if (fileName != null) {// perf.log now.setTime(System.currentTimeMillis());sdf = new SimpleDateFormat(datePattern);File file = new File(fileName);// 获取最后更新时间拼成的文件名scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));} else {LogLog.error("File is not set for appender [" + name + "].");} if (maxBackupIndex <= 0) {LogLog.error("maxBackupIndex reset to default value[2],orignal value is:" + maxBackupIndex);maxBackupIndex = 2;}}/*** 滚动文件的函数:<br>* 1. 对文件名带的时间戳进行比较, 确定是否更新<br>* 2. if需要更新, 当前文件rename到文件名+日期, 重新开始写文件<br>* 3. 针对配置的maxBackupIndex,删除过期的文件*/void rollOver() throws IOException {String datedFilename = fileName + sdf.format(now);// 如果上次写的日期跟当前日期相同,不需要换文件if (scheduledFilename.equals(datedFilename)) {return;}// close current file, and rename it to datedFilenamethis.closeFile();File target = new File(scheduledFilename);if (target.exists()) {try {target.delete();} catch (SecurityException e) {e.printStackTrace();}}File file = new File(fileName);boolean result = file.renameTo(target);if (result) {LogLog.debug(fileName + " -> " + scheduledFilename);} else {LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].");}// 删除过期文件if (maxBackupIndex > 0) {File folder = new File(file.getParent());List<String> maxBackupIndexDates = getMaxBackupIndexDates();for (File ff : folder.listFiles()) {// 遍历目录,将日期不在备份范围内的日志删掉if (ff.getName().startsWith(file.getName()) && !ff.getName().equals(file.getName())) {// 获取文件名带的日期时间戳String markedDate = ff.getName().substring( file.getName().length());if (!maxBackupIndexDates.contains(markedDate)) {result = ff.delete();}if (result) {LogLog.debug(ff.getName() + " -> deleted ");} else {LogLog.error("Failed to deleted old DayRollingFileAppender file :" + ff.getName());}}}}try {// This will also close the file. This is OK since multiple// close operations are safe.this.setFile(fileName, false, this.bufferedIO, this.bufferSize);} catch (IOException e) {errorHandler.error("setFile(" + fileName + ", false) call failed.");}scheduledFilename = datedFilename;// 更新最后更新日期戳}/*** Actual writing occurs here.* 这个方法是写操作真正的执行过程.*/@Overrideprotected void subAppend(LoggingEvent event) {long n = System.currentTimeMillis();if (n >= nextCheck) {// 在每次写操作前判断一下是否需要滚动文件now.setTime(n);nextCheck = getNextDayCheckPoint(now);try {rollOver();} catch (IOException ioe) {LogLog.error("rollOver() failed.", ioe);}} super.subAppend(event);}/*** 获取下一天的时间变更点** @param now* @return*/long getNextDayCheckPoint(Date now) {Calendar calendar = Calendar.getInstance();calendar.setTime(now);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);// 注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的 calendar.add(Calendar.DATE, 1);return calendar.getTimeInMillis();}/*** 根据maxBackupIndex配置的备份文件个数,获取要保留log文件的日期范围集合* @return list<'fileName+yyyy-MM-dd'>*/List<String> getMaxBackupIndexDates() {List<String> result = new ArrayList<String>();if (maxBackupIndex > 0) {for (int i = 1; i <= maxBackupIndex; i++) {Calendar calendar = Calendar.getInstance();calendar.setTime(now);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);// 注意MILLISECOND,毫秒也要置0...否则错了也找不出来的calendar.add(Calendar.DATE, -i);result.add(sdf.format(calendar.getTime()));}}return result;}public int getMaxBackupIndex() {return maxBackupIndex;}public void setMaxBackupIndex(int maxBackupIndex) {this.maxBackupIndex = maxBackupIndex;}public String getDatePattern() {return datePattern;}}
自定义的配置
# 按天存储,保存7天(日志文件不需要全部保留的情况)log4j.appender.customer=util.CustomLogAppenderlog4j.appender.customer.File=file.loglog4j.appender.customer.maxBackupIndex=7log4j.appender.customer.layout=org.apache.log4j.PatternLayoutlog4j.appender.customer.layout.ConversionPattern=%d{yyyyMMdd HH:mm:ss} [%p] %c.%M %m%n
springboot - logback
1、引用,web的引用里面已经引入了。
2、配置文件
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?><configuration scan="true" scanPeriod="60 seconds" debug="false"><contextName>logback</contextName><property name="logback.logdir" value="./log/"/><property name="logback.appname" value="java"/><!--输出到控制台 ConsoleAppender--><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><!--展示格式 layout--><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern></layout></appender><appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高,所以我们使用下面的策略,可以避免输出 Error 的日志--><filter class="ch.qos.logback.classic.filter.LevelFilter"><!--过滤 Error--><level>ERROR</level><!--匹配到就禁止--><onMatch>DENY</onMatch><!--没有匹配到就允许--><onMismatch>ACCEPT</onMismatch></filter><!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。--><File>${logback.logdir}/info.${logback.appname}.log</File><!--滚动策略,按照时间滚动 TimeBasedRollingPolicy--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间--><FileNamePattern>${logback.logdir}/info.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern><!--只保留最近90天的日志--><maxHistory>90</maxHistory><!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志--><!--<totalSizeCap>1GB</totalSizeCap>--></rollingPolicy><!--日志输出编码格式化--><encoder><charset>UTF-8</charset><pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern></encoder></appender><appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>Error</level></filter><!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。--><File>${logback.logdir}/error.${logback.appname}.log</File><!--滚动策略,按照时间滚动 TimeBasedRollingPolicy--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间--><FileNamePattern>${logback.logdir}/error.${logback.appname}.%d{yyyy-MM-dd}.log</FileNamePattern><!--只保留最近90天的日志--><maxHistory>90</maxHistory><!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志--><!--<totalSizeCap>1GB</totalSizeCap>--></rollingPolicy><!--日志输出编码格式化--><encoder><charset>UTF-8</charset><pattern>%d [%thread] %-5level %logger{36} %line : %msg%n</pattern></encoder></appender><!--指定最基础的日志输出级别--><root level="INFO"><!--appender将会添加到这个loger--><appender-ref ref="console"/><appender-ref ref="fileInfoLog"/><appender-ref ref="fileErrorLog"/></root></configuration>
3、使用
@Slf4jpublic class LogServiceImpl implements LogService {public PageResult<LogModel> query(LogModelVO logModelVO) {log.info("123")}}
springboot - log4j2
推荐
引用
<!--log4j2 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><!--异步日志依赖--><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.3.7</version></dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?><!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --><!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--><!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--><configuration status="WARN" monitorInterval="30"><properties><property name="logDir" value="log"/></properties><!--先定义所有的appender--><appenders><!--这个输出控制台的配置--><console name="Console" target="SYSTEM_OUT"><!--输出日志的格式--><PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/></console><!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--><File name="log" fileName="${logDir}/test.log" append="false"><PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/></File><!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingRandomAccessFile name="RollingFileInfo" fileName="${logDir}/info.log"filePattern="${logDir}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"immediateFlush="false"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="500 MB"/></Policies></RollingRandomAccessFile><RollingRandomAccessFile name="RollingFileWarn" fileName="${logDir}/warn.log"filePattern="${logDir}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"immediateFlush="false"><ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="500 MB"/></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --><DefaultRolloverStrategy max="20"/></RollingRandomAccessFile><RollingRandomAccessFile name="RollingFileError" fileName="${logDir}/error.log"filePattern="${logDir}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"immediateFlush="false"><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="500 MB"/></Policies></RollingRandomAccessFile></appenders><!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--><loggers><!--过滤掉spring和mybatis的一些无用的DEBUG信息--><logger name="org.springframework" level="INFO"/><logger name="org.mybatis" level="INFO"/><root level="all" includeLocation="true"><appender-ref ref="Console"/><appender-ref ref="RollingFileInfo"/><appender-ref ref="RollingFileWarn"/><appender-ref ref="RollingFileError"/></root></loggers></configuration>
启动函数
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”?>
测试```bashimport lombok.extern.slf4j.Slf4j;import org.apache.logging.log4j.ThreadContext;import org.slf4j.Marker;import org.slf4j.MarkerFactory;import java.util.concurrent.ThreadLocalRandom;/** * @author GiraffeTree * @date 2019/12/5 */@Slf4jpublic class Log4j2Test {private final static Marker BOY = MarkerFactory.getMarker("boy");private final static Marker GIRL = MarkerFactory.getMarker("girl");private final static Marker OTHER = MarkerFactory.getMarker("other");public static void main(String[] args) {testRouting(1000, 3);}public static void addRandomLog() {int num = ThreadLocalRandom.current().nextInt(0, 1000);if (num < 950) {ThreadContext.put("logFileName", "David");log.info("current: {}", num);} else {log.error("current: {}", num);}}public static void testRouting(int loopCount, int userSize) {long l1 = System.currentTimeMillis();int count = loopCount;while (count > 0) {writeMultipleUsersLog(userSize);count--;}long l2 = System.currentTimeMillis();System.out.println(String.format("size:%d loopCount:%d cost: %dms", userSize, loopCount, l2 - l1));}public static void writeMultipleUsersLog(int size) {for (int i = 0; i < size; i++) {String userName = "user" + i;ThreadContext.put("logFileName", userName);int num = ThreadLocalRandom.current().nextInt(0, 1200);if (num < 500) {log.info(BOY, "current: {}", num);} else if (num < 1000) {log.info(GIRL, "current: {}", num);} else {log.info(OTHER, "current: {}", num);}}}}
