因为log4j版本存在bug,公司要求将log4j升级到log4j2,在处理过程中,有一些心得体会,这里分享一下;
升级场景:我们的代码不是web程序,也没有用到spring或者springboot,这里所有的东西都是自己写的,使用的main函数,从main函数直接进入,然后自己填写log4j2.xml,自己读取log4j2.xml,之后将日志打印到指定的目录下;
log4j和log4j2的比较:
(1)log4j中的日志配置信息是保存在.properties的配置文件中,而log4j2的配置文件中保存在.xml配置文件中;
(2)log4j是由logger和appender这两个部分组成,将logger和appender进行绑定,当在logger中输入logger.info则会将对应的日志输入appender设定的文件当中;而对于log4j2来说,一个logger可以绑定多个的appender,即当你在代码中输出日志信息时,可以将这些日志信息输出到多个日志文件中(多个appender),当然在log4j中可能也会存在这种情况,但是在log4j2中这种配置更加的明显;
log4j2的基本知识:
在将log4j2的组成部分时,我们可以从log4j2.xml文件来看,以下是一个log4j2的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info" monitorInterval="1800">
<Appenders>
<RollingFile name="A1" fileName="D:/java/log/TestLog4j2.log" append="true" filePattern="D:/java/log/TestLog4j2-%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="D:/java/log/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--ThresholdFilter :日志输出过滤-->
<!--level="info" :日志级别,onMatch="ACCEPT" :级别在info之上则接受,onMismatch="DENY" :级别在info之下则拒绝-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- Policies :日志滚动策略-->
<Policies>
<!-- TimeBasedTriggeringPolicy :时间滚动策略,默认0点小时产生新的文件,interval="6" : 自定义文件滚动时间间隔,每隔6小时产生新文件, modulate="true" : 产生文件是否以0点偏移时间,即6点,12点,18点,0点-->
<TimeBasedTriggeringPolicy interval="6" modulate="true"/>
<!-- SizeBasedTriggeringPolicy :文件大小滚动策略-->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<loggers>
<root level="INFO">
<appender-ref ref="RollingFile"/>
</root>
<logger name="Test.TestLog4j2" includeLocation="false" additivity="false">
<AppenderRef ref="A1"/>
</logger>
</loggers>
可以看到log4j2的具体的配置信息是在configuration下面,其主要有2部分组成,分别是appenders和loggers:
(1)appenders由多个appender组成,每个appender代表一个写入的文件;目前常用的appender有以下几种,分别是:
ConsoleAppnder:将日志输出到命令行
FileAppender:普通地输入到本地文件
RollingFileAppender:对日志文件进行封存
这里参考了这个博客,https://blog.csdn.net/henry115/article/details/78483457, 具体可以去查看
(2)loggers,logger也是由多个logger组成,每个logger都对应的原始类的日志输出方向;
策略:我们的logger和appender是一一对应的,即每个main函数所对应的类都会写入一个新的日志空间当中,但是有一点,就是写入的日志文件是根据传递给main函数的参数而变化的,基于这一点,我们配置了每个类对应的logger,然后将其对接的appender设置为空,与此同时,我们会在配置文件中设置一个模板appender;之后在代码中,我们加载配置信息,然后获取对应类的logger和appender模板,根据appender模板,我们创建一个我们需要的appender(主要是设置appender的名称和appender存储的文件路径),最后将创建的appender和logger进行关联,提供对应的logger给用户;
注:在log4j中可以直接获取logger和appender,并且可以直接修改appender存储的文件路径,而这一特性在log4j2中没有找到,因此我们采用新创建appender,然后将appender和logger进行绑定的方式;
代码:
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<loggers>
<root level="INFO">
</root>
<logger name="merchant.analysis.entry.BatchPreOptBySys" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.entry.ExtractTableCtl" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.utils.FileUtils" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.entry.InitExtractTableCtl" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.entry.PtfExtractCtl" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.entry.HiveExport" includeLocation="false" additivity="false">
</logger>
<logger name="merchant.analysis.entry.MonSrcTblColumn" includeLocation="false" additivity="false">
</logger>
</loggers>
代码:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.OutputStreamManager;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Property;
@SuppressWarnings("all")
public class LogUtils {
/**
* 逻辑:
* (1)先获取模板的appender
* (2)然后按照模板创建新的appender
* (3)最后在logger里添加appender的关联关系
* @param config
* @param loggerName
* @param moduleAppenderName
* @param appenderName
* @param logFilePath
* @return
*/
public static Logger getLogger(Configuration config, String loggerName, String moduleAppenderName, String appenderName, String logFilePath){
//通过config获取相应的Appender
RollingFileAppender appender = config.getAppender(moduleAppenderName);
//提取Appender的对应信息
TriggeringPolicy policy = appender.getTriggeringPolicy();
String filePattern = appender.getFilePattern();
Filter filter = appender.getFilter();
ErrorHandler handler = appender.getHandler();
String immediateFlush = String.valueOf(appender.getImmediateFlush());
Layout layout = appender.getLayout();
OutputStreamManager manager = appender.getManager();
Property[] properties = appender.getPropertyArray();
LifeCycle.State state = appender.getState();
String name = appenderName;
String fileName = logFilePath;
//通过Appender的模板创建新的Appender,主要是修改Appender的路径信息
Appender appender2 = RollingFileAppender.createAppender(fileName,filePattern,"true",name,"true",
"8192",immediateFlush,policy,null,layout,filter,"true","true","",config);
config.addAppender(appender2);
//将Appender添加到Logger中,并返回该Logger
LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
loggerConfig.removeAppender(moduleAppenderName);
loggerConfig.addAppender(appender2, Level.INFO, null);
LoggerContext loggerContext = config.getLoggerContext();
Logger logger = loggerContext.getLogger(loggerName);
return logger;
}
}
采用这种方式,我们可以将返回一个logger