今天讲讲Java中的日志---logging、logback

2,154 阅读5分钟

今天中午就不休息了,把剩余的草稿写完~

一、Java API 中自带的日志系统

Java自带的日志处理api位于java.util.logging包下,该包下只有少数的一些类,如下图:

其实结构非常地简单,主要由以下三个组件组成:

  • Logger组件:logger组件定义了一些日志的基本操作,如log输出日志,getName获取该日志对象的名称,setLevel()设置日志级别等等,就是一些基本的操作啦!
  • Handler组件:Handler就是定义一些日志处理嘛!比如你想将日志输出到文件,可使用FileHandler,想将日志输出到控制台,可使用ConsoleHandler
  • Formatter组件:很显然就是对日志输出的格式进行定义嘛!
  • Level组件:日志的级别,最基本的级别INFOWARNING
  • filter组件:过滤器,允许你对输出日志的一个过滤

对于Java api的日志体系,下面我写了一个小栗子进行总结:

package com.wokao66.logger;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class JavaLogger {

  static Logger logger = Logger.getLogger(JavaLogger.class.getName());

  public static void HandlerFilter() throws SecurityException, IOException {
    //首先我声明一个文件处理器,因为我打算将日志输出到一个文件当中,即保存在E盘的a.txt文件
    FileHandler handler = new FileHandler("E://a.txt");
    //我自定义一个Formatter,用于对每条输出日志进行格式化,你也可以不指定,则默认使用SimpleFormatter
    handler.setFormatter(new Formatter() {

    @Override
    public String format(LogRecord record) {
      //这就是我自定义的格式
      return "[" + new SimpleDateFormat().format(record.getMillis()) + "]-[" + record.getLevel()
    + "]-" + record.getSourceClassName() + "-" + "[" + record.getMessage() + "]\n";
    
    }
 });
    //当然,我也可以设置一个过滤器
    handler.setFilter(new Filter() {

    @Override
    public boolean isLoggable(LogRecord record) {
      //当日志内容与"this is a unloged message!"相同时,则不输出该日志,当然这里只是测试,实际项目这个规则是不存在的
      return !record.getMessage().equals("this is a unloged message!");

    }
 });
    //给Logger设置Handler
    logger.addHandler(handler);
    //默认是ConsoleHandler,禁用之后才不会输出到控制台,而是输出到文件中
    logger.setUseParentHandlers(false);
}

public static void main(String[] args) throws SecurityException, IOException {
  HandlerFilter();
  logger.log(Level.INFO, "this is a info message!");
  logger.log(Level.OFF, "this is a OFF message!");
  logger.log(Level.SEVERE, "this is a SEVERE message!");
  logger.log(Level.WARNING, "this is a WARNING message!");
  logger.log(Level.INFO, "this is a unloged message!");//这一句将不会被记录到文件中
	}
}

二、说一说Logback咯!

logback这个家伙在企业中的使用很频繁。我先讲下logback的一些常用的组件和配置,然后再来个栗子作为演示。

  • logback由三大基本模块组成,分别是logback-core,logback-access,logback-classic.其中core是核心模块,是其他两模块的基础。

  • logback由三大组件组成,分别是Logger,Appender,Layout.其中logger属于classic模块,appender,layout属于core模块,core模块没有logger的概念!Appender其实就是表示一个日志输出的目的地,可以是控制台,也可以是文件。

  • logback拥有自己的命名规范,一般logger是按照层次命名的,具有继承关系,以.来作为分割,拥有祖先和父节点的概念,比如一个logger被命名为com.wokao666.www,那么com就是wokao666的父节点,comwww的祖先节点。

  • root属于所有logger的根节点,以org.slf4j.Logger.ROOT_LOGGER_NAME作为root的名字。

  • logback会默认在类路径下查找名为logback-test.xmllogback.groovy或者是logback.xml的配置文件。

  • 日志的打印是向上传递的,什么意思呢?比如你打印了一个info级别的日志(输出到控制台),同时也打印了一个error的日志,那么控制台会同时输出info和error两条日志,因为error的级别比info高。

很简单吧!所以说你要使用logback,主要有以下三大步骤:

  • 1、在类路径下创建你相应的配置文件,如logback.xml,并做好相关配置。
  • 2、通过调用LoggerFactory.getLogger()方法并传递一个名称或类进去获得一个Logger对象
  • 3、通过第二步获得的Logger对象,调用相应的日志级别方法,如info(),error()

好了,我不多说了,基本的就这些,下面我通过问题和栗子来演示一波:

先看看我的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 表示启动配置文件扫描,每隔30秒扫描一次,什么意思?就是说它会去检查你的配置文件在运行过程中配置是否有变化 -->
<configuration scan="true" scanPeriod="30 seconds">
    <!-- 配置一个存放日志文件的地方,注意这里是一个变量来着,以后可以通过${变量名}来获取变量的值 -->
	<property name="log.base" value="/logs" />
	<!-- 这里又定义一个变量 -->
	<property name="log.controller" value="${log.base}/controller" />
	<!-- 输出到控制台 -->
	<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${log.pattern}</pattern>
		</encoder>
	</appender>
	<!-- 声明了一个存放info消息的appender -->
	<!-- 启动日志回滚appender,就是说有一些规则,比如说规定一个文件大小达到10M之后输出到一个新的文件中去 -->
	<appender name="controller-info"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${log.controller}/current-info.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${log.controller}/controller_info_%d{yyyyMMdd_HH}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder charset="UTF-8">
			<pattern>%msg%n</pattern>
		</encoder>
	</appender>
	<!-- 声明了一个存放error消息的appender -->
	<appender name="controller-error"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${log.controller}/current-error.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${log.controller}/controller_error_%d{yyyyMMdd_HH}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder charset="UTF-8">
			<pattern>%msg%n</pattern>
		</encoder>
	</appender>
    <!-- 这里我定义一个logger,该logger会跟踪club.wokao666包下的日志打印语句,但是它不会对日志进行输出,因为你没有关联任何的appender,虽然没有关联,但是默认会继承自父logger的相应配置,即club包下的配置,由于我们也没有对club进行相应的配置,所以它默认会继承自下面的 root logger -->
	<logger name="club.wokoa666" level="info"></logger>
	
	<logger name="club.wokao666.controller" additivity="false" level="WARN">
		<appender-ref ref="controller-error" /><!-- 关联我们的Appender,表示日志输出的地址 -->
	</logger>
    <!-- 这就是我们的根logger -->
	<root level="info">
		<appender-ref ref="controller-info" />
	</root>
</configuration>

日志分类实例,添加相应的过滤器

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
	<property name="log.base" value="/logs" />
	<property name="log.controller" value="${log.base}/controller" />
	<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${log.pattern}</pattern>
		</encoder>
	</appender>
	<appender name="controller-info"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${log.controller}/current-info.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${log.controller}/controller_info_%d{yyyyMMdd_HH}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder charset="UTF-8">
			<pattern>%msg%n</pattern>
		</encoder>
		<!-- 过滤器配置 -->
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<level>INFO</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>
	<appender name="controller-debug"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${log.controller}/current-debug.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${log.controller}/controller_debug_%d{yyyyMMdd_HH}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder charset="UTF-8">
			<pattern>%msg%n</pattern>
		</encoder>
		<!-- 过滤器配置 -->
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<level>DEBUG</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>
	<appender name="controller-error"
		class="ch.qos.logback.core.rolling.RollingFileAppender">
		<File>${log.controller}/current-error.log</File>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${log.controller}/controller_error_%d{yyyyMMdd_HH}.%i.log
			</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
		</rollingPolicy>
		<encoder charset="UTF-8">
			<pattern>%msg%n</pattern>
		</encoder>
	   	<!-- 过滤器配置 -->
		<filter class="ch.qos.logback.classic.filter.LevelFilter">
			<level>ERROR</level>
			<onMatch>ACCEPT</onMatch>
			<onMismatch>DENY</onMismatch>
		</filter>
	</appender>
	<root level="trace">
		<appender-ref ref="controller-error" />
		<appender-ref ref="controller-info" />
		<appender-ref ref="controller-debug" />
	</root>
</configuration>

最后,更多高级内容可以查阅官网文档进行学习,我有一点感想,就是说如果我大学四年的空闲时间都花在学习英文技术文档的话,可能,或许现在的我会不那么一样,一首《我们不一样》送给大家,谢谢阅读!