1类图中+表示public,-表示private
2Log4j中日志level7个
All<Debug<info<WARN<ERROR<FATAL<off,debug到error是常用的
Debug是调试信息,开发用,info是输出一些关键信息,warn表示不符合要求,但不会导致程序保存,error出错,fatal致命错误,导致程序退出,off是关闭所有日志记录
https://zhuanlan.zhihu.com/p/50564627
3入门实例
https://blog.csdn.net/guanhang89/article/details/52262794
4.log4j三大组件构成
参考:https://blog.csdn.net/yangb0803/article/details/111319935
Logger:负责生成日志,并能够对日志信息进行分类筛选,通俗地讲就是决定什么日志信息应该输出,什么日志信息应该被忽略
Appender:定义了日志信息输出的目的地,指定日志信息应该被输出到什么地方,
这些地方可以是控制台、文件、网络设备等
Layout: 指定日志信息的输出格式
5.目前一个logger可以对应多个appender目的地,可以同时输出到多个位置
6.layout有htmllayout,PatternLayout(可以指定布局模式),SimpleLayout(包含日志信息级别和信息字符串),TTCCLayout(包含日志产生的时间、线程和类别等信息)
7.配置文件需要配置以上说的三个组件
Logger组件配置 log4j.rootLogger =WARN,file,console(分别是level,输出路径1,输出路径2 )
配置appender组件
log4j.appender.appenderName =fully.qualified.name.of.appender.class(这个分别可以选择以下5个)
1.org.apache.log4j.ConsoleAppender(控制台)
2.org.apache.log4j.FileAppender(文件) 【只能把日志输出一个文件,不推荐】
3.org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
4.org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
5.org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置日志格式,具体见链接
4.Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式
5.Log接口中还提供了一些isFatalEnabled() ,isErrorEnabled()这样的方法,用来判断相应日志级别是否能够输出,可以在输出前判断下,可以的话再做一些操作和输出,提高性能
6.LogFactory接口这个提供获得log日志器的两种静态方式,Getlog(class), getlog(string)第一种常用
7.log4j.rootLogger=ERROR, stdout这个配置的是正常级别,个性化配置则改为包名log4j.logger.com.xyc.demo=DEBUG,stdout
8.具体配置 log4j2 https://blog.csdn.net/vipinchan/article/details/81256917#2-log4j2xml 配置文件,
详解2 https://blog.csdn.net/yAYang_mo/article/details/77623625
9.指定多个输出路径,名字可以任意
log4j.rootLogger=INFO,Console,File 配置了2个输出地方,这个名字可以任意(如上面的Console和File),但必须与我们在后面进行的设置名字对应。例如:log4j.appender.Console中的Console 和 log4j.appender.File中的File就是对应之前写的名称。
10.日志使用常用的jar包配合 参考: https://www.jianshu.com/p/d7b0e981868d
将slf4j日志,采用log4j实现进行输出,需要如下jar包
slf4j-api-x.x.x.jar
slf4j-log4j12.jar
log4j-1.2.17.jar
将slf4j日志,采用log4j2实现进行输出,需要如下jar包
slf4j-api-x.x.x.jar
log4j-slf4j-impl.jar
log4j-api.jar
log4j-core.jar
11.当配置APPender为.org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),指定归档方式有多种:
在DailyRollingFileAppender中可以指定monthly(每月)、 weekly(每周)、daily(每天)、half-daily(每半天)、hourly(每小时)和minutely(每分钟)六个频度,这是通过为 DatePattern选项赋予不同的值来完成的。DatePattern选项的有效值为:
'.'yyyy-MM,对应monthly(每月)
'.'yyyy-ww,对应weekly(每周)
'.'yyyy-MM-dd,对应daily(每天)
'.'yyyy-MM-dd-a,对应half-daily(每半天)
'.'yyyy-MM-dd-HH,对应hourly(每小时)
'.'yyyy-MM-dd-HH-mm,对应minutely(每分钟)
配置举例:
log4j.appender.fdisk = org.apache.log4j.DailyRollingFileAppender
log4j.appender.fdisk.DatePattern = '.'yyyy-MM-dd'.log'
log4j.appender.fdisk.File= sender
ps:DatePattern会将归档格式添加到文件名sender的后缀, 格式化的字符串不能作为文件名前缀,只能后缀
对于不是标准格式的自定义字符要放在单引号内;
12.日志后面一定要注意不能有空格,否则服务器上打不开
13 动态改变日志级别:
https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/
14 druid团队的log4j2的使用和配置
https://github.com/alibaba/druid/wiki/Druid%E4%B8%AD%E4%BD%BF%E7%94%A8log4j2%E8%BF%9B%E8%A1%8C%E6%97%A5%E5%BF%97%E8%BE%93%E5%87%BA
<!--Spring-boot中去掉logback的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--日志-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.6</version>
</dependency>
<!--其他依赖-->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE" packages="com.bestpay.drip.event.log.plugin">
<Properties>
<property name="file.path">/data/appLogs</property>
<property name="shadow.path">shadow</property>
<property name="application.name">${sys:spring.application.name}</property>
</Properties>
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout
pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}][%5p][%c{100} %line][%thread][%X{SOFA-TraceId},%X{SOFA-SpanId}][%eventContext]%m%n"/>
</Console>
<RollingRandomAccessFile name="accessFile" fileName="${file.path}/${application.name}.log"
filePattern="${file.path}/${application.name}.%d{yyyy-MM-dd}.%i.log"
immediateFlush="false">
<Filters>
<!-- 只打印正常流量日志-->
<ShadowFilter onMatch="DENY" onMismatch="NEUTRAL"/>
<!-- 只打印INFO及以上级别日志 -->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout charset="UTF-8"
pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}][%5p][%c{100} %line][%thread][%X{SOFA-TraceId},%X{SOFA-SpanId}][%eventContext]%m%n"/>
<Policies>
<!--每小时滚动一次-->
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<!--日志达到512MB 滚动一次-->
<SizeBasedTriggeringPolicy size="512MB"/>
</Policies>
<!-- 日志文件超过最大数值,则删除最早生成的日志文件。注意%d{yyyy-MM-dd}要用年月日格式,不能加上时分秒,并且最后要有%i,这样log4j2
才能判断出哪天一共产生几个 -->
<DefaultRolloverStrategy max="100">
<Delete basePath="${file.path}" maxDepth="2">
<IfFileName glob="*/${application.name}*.log">
<IfLastModified age="6d">
<IfAny>
<IfAccumulatedFileSize exceeds="50 GB"/>
<IfAccumulatedFileCount exceeds="80"/>
</IfAny>
</IfLastModified>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="shadowAccessFile" fileName="${file.path}/${shadow.path}/${application.name}.log"
filePattern="${file.path}/${shadow.path}/${application.name}.%d{yyyy-MM-dd}.%i.log"
immediateFlush="false">
<Filters>
<!-- 只打印影子流量日志 -->
<ShadowFilter onMatch="NEUTRAL" onMismatch="DENY"/>
<!-- 只打印INFO及以上级别日志 -->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout charset="UTF-8"
pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}][%5p][%c{100} %line][%thread][%X{SOFA-TraceId},%X{SOFA-SpanId}][%eventContext]%m%n"/>
<Policies>
<!--每小时滚动一次-->
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<!--日志达到512MB 滚动一次-->
<SizeBasedTriggeringPolicy size="512MB"/>
</Policies>
<!-- 日志文件超过最大数值,则删除最早生成的日志文件。注意%d{yyyy-MM-dd}要用年月日格式,不能加上时分秒,并且最后要有%i,这样log4j2
才能判断出哪天一共产生几个 -->
<DefaultRolloverStrategy max="100">
<Delete basePath="${file.path}" maxDepth="2">
<IfFileName glob="*/${application.name}*.log">
<IfLastModified age="6d">
<IfAny>
<IfAccumulatedFileSize exceeds="50 GB"/>
<IfAccumulatedFileCount exceeds="80"/>
</IfAny>
</IfLastModified>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingRandomAccessFile>
</Appenders>
<loggers>
<Logger name="org.apache.commons" level="INFO"/>
<Logger name="org.apache.zookeeper" level="WARN"/>
<Logger name="org.apache.kafka" level="WARN"/>
<Logger name="org.apache.dubbo" level="WARN"/>
<Logger name="org.springframework" level="INFO"/>
<Logger name="org.springframework.web.servlet.DispatcherServlet" level="INFO"/>
<Logger name="org.springframework.web.context.support.XmlWebApplicationContext" level="INFO"/>
<Logger name="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" level="INFO"/>
<Logger name="java.sql.PreparedStatement" level="INFO"/>
<Logger name="java.sql" level="INFO"/>
<Logger name="java.sql.Statement" level="INFO"/>
<Logger name="java.sql.ResultSet" level="INFO"/>
<Logger name="java.sql.Connection" level="INFO"/>
<Logger name="com.bestpay.kafka" level="WARN"/>
<Logger name="kafka.producer" level="WARN"/>
<Logger name="kafka.consumer" level="WARN"/>
<Logger name="kafka.utils" level="WARN"/>
<Logger name="bestpay.tools.union" level="WARN"/>
<Logger name="com.bestpay.cif.core.commonmanager" level="INFO"/>
<Logger name="com.bestpay.cif.core" level="WARN"/>
<Logger name="com.bestpay.cif.product" level="INFO"/>
<Logger name="com.bestpay.devops" level="WARN"/>
<root level="INFO" includeLocation="true">
<AppenderRef ref="accessFile"/>
<AppenderRef ref="shadowAccessFile"/>
<AppenderRef ref="CONSOLE"/>
</root>
</loggers>
</Configuration>
15.Log4j自定义参数打印
Long sequence = SequenceUtils.getSequence(QualityConstant.SEQUENCE_TAG_DISPATCH_TASK);
ThreadContext.putIfNull(“”sequence“”,String.valueOf(sequence));
log.info("task start working");
log.info("task ended");
ThreadContext.remove(sequence);
在打印日志格式中加入Sequence:[%X{sequence}]
16 动态改日志级别
@Service
public class LogLevelConfig implements ConfigChangeListener {
private static final String EMPTY = "";
private static final String LOG_PACKAGE_PREFIX = "logging.level.";
private static final LoggingSystem loggingSystem = LoggingSystem.get(Thread.currentThread().getContextClassLoader());
@ApolloConfigChangeListener(interestedKeyPrefixes = LOG_PACKAGE_PREFIX)
public void onChange(ConfigChangeEvent configChangeEvent) {
refeshLogLevel(configChangeEvent);
}
private void refeshLogLevel(ConfigChangeEvent configChangeEvent) {
Set<String> keyNames = configChangeEvent.changedKeys();
Map<String, ConfigChange> map = configChangeEvent.getChangeValue();
for (String key : keyNames) {
String logLevel = map.get(key).getNewValue();
String packageName = key.replace(LOG_PACKAGE_PREFIX, "");
changeLevel(packageName, logLevel);
}
}
private void changeLevel(String packageName, String strLevel) {
final LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
List<String> names = getLoggerConfigurations();
if(names.isEmpty()) return;
if (EMPTY.equals(packageName)) {
loggingSystem.setLogLevel(EMPTY, level);
} else {
List<String> enableName = names.stream().filter(entry -> entry.startsWith(packageName)).collect(Collectors.toList());
if (enableName.isEmpty() || null == enableName) return;
enableName.stream().forEach(item -> {
loggingSystem.setLogLevel(item, level);
});
}
}
private List<String> getLoggerConfigurations() {
return loggingSystem.getLoggerConfigurations().
stream().map(entry -> entry.getName()).collect(Collectors.toList());
}
}