log4j2.x教程和实战

log4j 2.x 官网:https://logging.apache.org/log4j/2.x/index.html

Apache Log4j 2是Log4j的升级版,对Log4j 1.x进行了重大改进,并提供了Logback中可用的许多改进,同时解决了Logback体系结构中的一些固有问题。

:log4j1.x的包时org.apach.log4j,log4j2.x的包名是org.apach.logging.log4j

Log4j 2包含基于LMAX Disruptor库的下一代异步记录器。在多线程方案中,与Log4j 1.x和Logback相比,异步Logger的吞吐量高18倍,延迟降低了几个数量级。Log4j 2明显优于Log4j 1.x,Logback和java.util.logging,尤其是在多线程应用程序中。

Log4j 2 API将提供最佳性能,且提供对Log4j 1.2,SLF4J,Commons Logging和java.util.logging(JUL)API的支持。依赖的maven:(slf4j+log4j2)

<!-- slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<!-- slf4j-log4j2-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.1</version>
</dependency> 

Log4j 2 使用

官网:https://logging.apache.org/log4j/2.x/manual/configuration.html

Log4j2不在支持log4j.properties这样的配置文件了,2.x版本配置文件后缀名只能为".xml",".json"或者".jsn"。系统选择配置文件的优先级(从先到后)如下:

  • .classpath下的名为log4j2-test.json 或者log4j2-test.jsn的文件.
  • .classpath下的名为log4j2-test.xml的文件.
  • .classpath下名为log4j2.json 或者log4j2.jsn的文件.
  • .classpath下名为log4j2.xml的文件.

log4j2和log4j1一样,都是有三大组件构成:Logger、Appender、Layout,同时也支持Filter。log4j1.x中常见的概念可以参考:https://blog.csdn.net/liuxiao723846/article/details/110929347

1、Appender:

官网:https://logging.apache.org/log4j/2.x/manual/appenders.html

1.1)Console Appender:

将日志信息输出写入System.out或System.err,其中System.out为默认目标。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="STDOUT"/>
    </Root>
  </Loggers>
</Configuration>

1.2)File Appender:

将日志信息输出到文件。主要的属性有:

  • bufferedIO:默认true,使用缓存写入磁盘;
  • bufferSize:When bufferedIO is true, this is the buffer size, the default is 8192 bytes.
  • immediateFlush:默认true,每次系操作后都会立即刷盘,会影响性能;(对于异步appender,会在一批实践结束后自动刷盘,不收该参数控制)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="MyFile"/>
    </Root>
  </Loggers>
</Configuration>

1.3)RandomAccessFile Appender:

RandomAccessFileAppender与标准FileAppender相似,它总是被缓冲(不能关闭)并且在内部它使用ByteBuffer + RandomAccessFile而不是BufferedOutputStream。在测量中,与FileAppender(bufferedIO = true)相比性能提高了20-200%。

<Appenders>
    <RandomAccessFile name="MyFile" fileName="logs/app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </RandomAccessFile>
</Appenders>

说明:对于不带滚动的文件appender,建议使用该appender。

1.4)RollingFile Appender:

将日志写入文件,并根据TriggeringPolicy和RolloverPolicy进行文件滚动。重要的属性:

  • bufferedIO、bufferSize、immediateFlush:和File Appender含义一样;
  • filePattern:设置归档文件的格式,和RolloverPolicy配合使用,支持如下规则:
    • date/time模式:用%d{}指定,如%d{yyyyMMdd}会被替换成20181001;
    • %i整数: 归档文件编号,每次归档依次递增1;
    • 压缩模式后缀:支持.gz, .zip, .bz2等;
    • 匹配Lookups:比如Date,支持SimpleDateFormat。
  • policy:TriggeringPolicy类型,设置文件滚动的触发时机;(一般基于时间、大小)
  • strategy:RolloverStrategy类型,用于确定存档文件的名称和位置的策略;

1)Triggering Policies:

A)CronTriggeringPolicy:基于cron expression的滚动策略;此策略由计时器控制,与处理日志事件异步,因此上一个或下一个时间段的日志事件可能会出现在日志文件的开头或结尾。Appender的filePattern属性应包含时间戳,否则目标文件将在每次翻转时被覆盖。属性:

  • schedule:cron表达式,例如:schedule="0 0 * * * ?"

B)TimeBasedTriggeringPolicy:当日期/时间和filePattern不匹配时触发滚动,属性包含:

  • interval:时间间隔频率,默认1;
  • modulate:是否调整时间间隔,使下一次滚动发生在时间间隔边界上;

举例:假设filePattern是%d{yyyy-MM-dd}-%i.log.zip,表示按天归档日志,那么对于TimeBasedTrigger来说就是1天执行一次滚动,如果设置intervla=6,则表示6天执行一次。

C)SizeBasedTriggeringPolicy :基于文件大小触发滚动,单位可以是KB,MB或GB,属性有:

  • size:设置文件大小,例如:size=“200 MB”

注:与基于时间的触发策略结合使用时,filePattern必须包含%i,否则目标文件将在每次翻转时均被覆盖,因为基于大小的触发策略不会导致文件名中的时间戳值更改。当不使用基于时间的触发策略时,基于大小的触发策略将导致时间戳值发生更改。

2)RolloverStrategy:

A)DefaultRolloverStrategy:(推荐)由filePattern属性和以下属性共同决定,根据filePattern中的日期/时间、编号(%i)生成归档文件;如文件以gz、zip、bz2等结尾则压缩归档文件。属性有:

  • fileIndex:指定归档文件起始编号,可选“max”(默认,文件编号从小到大,不超max)、“min”(文件编号从大到小,不超min)、“nomax”(忽略最小最大值)
  • min:默认1,归档文件的最小编号;
  • max:默认7,归档文件的最大编号,达到该值后,较旧的归档文件被删除,并开始循环;
  • compressionLevel:设置压缩级别(0-9),仅使用zip压缩,0不压缩,1压缩速度最快,9压缩率最大;

B)DirectWriteRolloverStrategy : 属性有:

  • maxFiles:指定归档文件的最大数量。超过最大值,最早归档文件被删除,并开始循环。如果配置的值<0或者省略,会被设置成Integer.MAX_VALUE,即不限制数量;如果配置的值是0和1,会被强制设置成7。
  • compressionLevel:设置压缩级别(0-9),仅使用zip压缩,0不压缩,1压缩速度最快,9压缩率最大;

二者的区别:

  1. DefaultRolloverStrategy会根据appender中指定的fileName,将日志写入其中,触发归档时,根据filePattern计算出归档文件名,然后把fileName文件重命名为归档名,进行压缩,然后创建一个新的fileName文件,之后的日志写入到新创建的fileName文件中,当编号达到最大值后,开启循环使用编号。
  2. DirectWriteRolloverStrategy没有fileName,日志文件名由filePattern匹配所得,日志直接写入到filePattern匹配的归档文件中,触发归档时,进行压缩,然后由filePattern重新匹配一个文件名,之后的日志写入到重新匹配的文件中(不执行重命名),此外归档编号没有限制,始终会+1.

C)其他策略:如DefaultRolloverStrategy等

示例1:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

使用DefaultRolloverStrategy归档策略,且配置了TimeBased、SizeBased触发策略,日志保存到logs/app.log文件中,每天触发一次归档,同时当日志达到250MB也会触发一次归档,归档文件依次为:logs/2018-07/app-2018-07-17-1.log.gz,logs/2018-07/app-2018-07-18-2.log.gz…最多只有20个归档,当一天内达到20个归档后(编号达到20),编号1被删除,2重命名为1,3重命名为2...一次类推,开启了循环使用编号,直到第二天。

示例2:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" 
                 filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      DirectWriteRolloverStrategy maxFiles="20"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

使用DirectWriteRolloverStrategy归档策略,且配置了TimeBased、SizeBased触发策略,日志保存到logs/app-2018-07-17-1.log文件中,每天触发一次归档,同时当日志达到250MB也会触发一次归档,无论触发那种归档条件,会压缩logs/app-20180717-1.log.gz,同时创建新日志文件logs/app-20180718-2.log写入日志,当达到归档文件数量限制时,编号仍会递增,最旧的归档日志被删除 。

1.5)RollingRandomAccessFile Appender:

RollingRandomAccessFileAppender与标准RollingFileAppender相似,它总是被缓冲(不能关闭)并且在内部它使用ByteBuffer + RandomAccessFile而不是BufferedOutputStream。与RollingFileAppender相比,性能提高了20-200%。示例:

<RollingRandomAccessFile name="RollingRandomAccessFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="20"/>
    </RollingRandomAccessFile>

说明:对于带滚动的文件appender,建议使用该appender。

1.6)其他appender:

CassandraAppender、FlumeAppender、JDBCAppender、JMS Appender、KafkaAppender、HttpAppender、NoSQLAppender for MongoDB、NoSQLAppender for Apache CouchDB等。。。

 2、Filter:

官网:https://logging.apache.org/log4j/2.x/manual/filters.html

允许对LogEvent进行评估,以确定是否或如何发布它们。一个过滤器将使用其一种过滤方法被调用,并返回一个结果,该结果是一个枚举之一:ACCEPT,DENY或NEUTRAL。

2.1)BurstFilter:

在达到最大限制后通过静默丢弃事件来控制LogEvent的处理速率。属性有:

  • level:要过滤的消息级别,默认值WARN。如果超过了maxBurst,则等于或低于此级别的所有内容都会被滤除。
  • rate:float类型,每秒允许LogEvent的平均值;
  • maxBurst:integer类型,在过滤事件以超过平均速率之前可以发生的最大事件数。默认值为速率的10倍。
  • onMatch:默认是NEUTRAL,匹配时执行哪种动作ACCEPT, DENY or NEUTRAL
  • onMismatch:默认是DENY,不匹配时执行哪种动作ACCEPT, DENY or NEUTRAL

示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
      <BurstFilter level="INFO" rate="16" maxBurst="100"/>
      <PatternLayout>
        <pattern>%d %p %c{1.} [%t] %m%n</pattern>
      </PatternLayout>
      <TimeBasedTriggeringPolicy />
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

2.2)ThresholdFilter:

如果LogEvent中的级别与指定的级别相同或更特定,则此过滤器返回onMatch结果,否则返回onMismatch值。属性有:

  • level:指定日志级别;
  • onMatch:默认是NEUTRAL,匹配时执行哪种动作ACCEPT, DENY or NEUTRAL
  • onMismatch:默认是DENY,不匹配时执行哪种动作ACCEPT, DENY or NEUTRAL

示例:在appender上设置日志级别过滤

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
      <ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY"/>
      <PatternLayout>
        <pattern>%d %p %c{1.} [%t] %m%n</pattern>
      </PatternLayout>
      <TimeBasedTriggeringPolicy />
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

2.3)TimeFilter:

按照时间来过滤。属性有:

  • start:开始时间,格式 HH:mm:ss
  • end:结束时间,格式 HH:mm:ss,假如结束时间小于开始时间,则不写入日志
  • timezone:时区
  • onMatch:默认是NEUTRAL,匹配时执行哪种动作ACCEPT, DENY or NEUTRAL
  • onMismatch:默认是DENY,不匹配时执行哪种动作ACCEPT, DENY or NEUTRAL

示例:默认时区在上午5:00至5:30写入日志:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
      <TimeFilter start="05:00:00" end="05:30:00" onMatch="ACCEPT" onMismatch="DENY"/>
      <PatternLayout>
        <pattern>%d %p %c{1.} [%t] %m%n</pattern>
      </PatternLayout>
      <TimeBasedTriggeringPolicy />
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

2.4)MarkerFilter:

MarkerFilter将配置的Marker值与LogEvent中包含的Marker进行比较。当标记名称与日志事件的标记或其父项之一匹配时,将执行onMatch,属性有:

  • marker:比较的marker名
  • onMatch:默认是NEUTRAL,匹配时执行哪种动作ACCEPT, DENY or NEUTRAL
  • onMismatch:默认是DENY,不匹配时执行哪种动作ACCEPT, DENY or NEUTRAL

说明:同理,还有NoMarkerFilter。

示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
      <Filters>
        <MarkerFilter marker="test" onMatch="ACCEPT" onMismatch="DENY"/>
        <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
      </Filters>
      <PatternLayout>
        <pattern>%d %p %c{1.} [%t] %m%n</pattern>
      </PatternLayout>
      <TimeBasedTriggeringPolicy />
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

在代码中写日志时指定Marker:

public class Test {
	private static Logger logger = LoggerFactory.getLogger(Test.class);
	private static final Marker MARKER = MarkerFactory.getMarker("test");
	
	public static void main(String...strings) {
		logger.info(MARKER,  "Test message~");
		logger.info("没有指定marker,该条日志会被过滤掉");
	}
}

2.5)其他Filter:

RegexFilter、ScriptFilter 等。。。

3、Logger:

同log4j1在log4j2中日志记录器(Logger)也具有继承特性,可以指定日志级别,有Root和自定义Logger两种(其他Logger默认会继承RootLogger)。具有的属性:

  • name:一般使用类的包名来命名,然后再代码中getLogger(My.class)这样方便继承,当然可以简单使用字符串,然后代码中通过getLogger("logger_name");来使用该Logger;
  • level:日志输出级别,默认是ERROR,从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
  • AppenderRef:指定该日志输出到哪个Appender,可以有多个appender;
  • additivity:是否继承父Logger;

注:每个配置都必须有一个根记录器Root,如果未配置,则将使用默认根LoggerConfig,其级别为ERROR且附加了Console appender。Root没有name属性,且不支持additivity属性(没有父级)

接下来重点介绍一下AsyncLogger ,官网:https://logging.apache.org/log4j/2.x/manual/async.html

4、configuration:

有如下属性:

  • shutdownHook:当JVM shutdown时log4j是否自动关闭,默认开启,可设置“disable”关闭;
  • shutdownTimeout:指定JVM shutdown后的延时,默认是0
  • status:Log4j2将有关初始化,滚动等内部操作的详细信息记录到控制台中,该属性可以设置这些内部信息的级别,例如:“ trace”,“ debug”,“ info”,“ warn”,“ error”和“ fatal”。如果需要对log4j进行故障排除,设置status =“ trace”是您可以使用的首批工具之一。
  • monitorInterval:自动更新log4j2配置文件的检查时间,单位秒。

实战

1、各级别日志不重复的打印到各日志文件:

通过Appender的Threshold以及Filter可以实现。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
	<Properties>
        <Property name="filePath">/data/logs/logtest</Property>
        <Property name="fileName">test</Property>
        <Property name="errorFileName">test_error</Property>
    </Properties>
      
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n" />
        </Console>
        
        <RollingFile name="RollingFile" fileName="${filePath}/${fileName}.log" 
                     filePattern="${filePath}/${fileName}-%d{yyyy-MM-dd-HH}-%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n"/>
            <Filters>
            	<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        
        <RollingFile name="ErrorRollingFile" fileName="${filePath}/${errorFileName}.log" filePattern="${filePath}/${errorFileName}-%d{yyyy-MM-dd}-%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n"/>
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <AppenderRef ref="RollingFile" />
            <AppenderRef ref="ErrorRollingFile" />
        </Root>
    </Loggers>
</Configuration>

:在Filters中,首先要过滤不符合的日志级别,把不需要的首先DENY掉,然后再ACCEPT需要的日志级别,这个次序不能搞颠倒。

2、某类日志单独输出到指定文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
	<Properties>
        <Property name="filePath">/data/logs/logtest</Property>
        <Property name="fileName">test</Property>
        <Property name="errorFileName">test_error</Property>
    </Properties>
      
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n" />
        </Console>
        
        <RollingFile name="RollingFile" fileName="${filePath}/${fileName}.log" 
                     filePattern="${filePath}/${fileName}-%d{yyyy-MM-dd-HH}-%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n"/>
            <Filters>
            	<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        
        <RollingFile name="ErrorRollingFile" fileName="${filePath}/${errorFileName}.log" 
                     filePattern="${filePath}/${errorFileName}-%d{yyyy-MM-dd}-%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n"/>
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
        </RollingFile>
        
        <RollingFile name="RollingMyFile" fileName="${filePath}/my.log" 
                     filePattern="${filePath}/my-%d{yyyy-MM-dd-HH}-%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %p %c{1}[%L]-%m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <AppenderRef ref="RollingFile" />
            <AppenderRef ref="ErrorRollingFile" />
        </Root>
        
	    <Logger name="myfile" level="INFO" additivity="false">
	        <Appender-ref ref="RollingMyFile"/>
	    </Logger>
    </Loggers>
</Configuration>

在代码中如下使用可以单独打印到myfile中

public class Test2 {
	static Logger logger = LoggerFactory.getLogger("myfile");

	public static void main(String...strings) {
		logger.info("test");
		logger.error("erro test");
	}
}

 

参考:https://www.jianshu.com/p/ce06b4aab704

https://www.cnblogs.com/yeyang/p/10485681.html

https://my.oschina.net/u/3783256/blog/3120113

https://blog.csdn.net/ThinkWon/article/details/101628736

上一篇:平衡二叉树的根


下一篇:平衡二叉树