日志接口(slf4j)
slf4j是对全部日志框架制定的一种规范、标准、接口,并非一个框架的具体的实现,由于接口并不能独立使用,须要和具体的日志框架实现配合使用(如log4j、logback)html
日志实现(log4j、logback、log4j2)java
为何须要日志接口,直接使用具体的实现不就好了吗?web
接口用于定制规范,能够有多个实现,使用时是面向接口的(导入的包都是slf4j的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,因此能够任意的更换实现而不用更改代码中的日志相关代码。sql
好比:slf4j定义了一套日志接口,项目中使用的日志框架是logback,开发中调用的全部接口都是slf4j的,不直接使用logback,调用是 本身的工程调用slf4j的接口,slf4j的接口去调用logback的实现,能够看到整个过程应用程序并无直接使用logback,当项目须要更换更加优秀的日志框架时(如log4j2)只须要引入Log4j2的jar和Log4j2对应的配置文件便可,彻底不用更改Java代码中的日志相关的代码logger.info(“xxx”),也不用修改日志相关的类的导入的包(import org.slf4j.Logger;
import org.slf4j.LoggerFactory;)数据库
使用日志接口便于更换为其余日志框架。apache
log4j、logback、log4j2都是一种日志具体实现框架,因此既能够单独使用也能够结合slf4j一块儿搭配使用)json
建立maven web 项目, 结构以下
api
配置pom.xml,引入log4j2必要的依赖(log4j-api、log4j-core)app
<properties> <junit.version>3.8.1</junit.version> <log4j.version>2.5</log4j.version> </properties> <!-- 使用aliyun镜像 --> <repositories> <repository> <id>aliyun</id> <name>aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> </dependency> </dependencies>
三、 使用Main方法简单测试
框架
测试说明:
工程中只引入的jar并无引入任何配置文件,在测试的时候能够看到有ERROR输出:“ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.”
输出logger时能够看到只有error和fatal级别的被输出来,是由于没有配置文件就使用默认的,默认级别是error,因此只有error和fatal输出来
引入的包是log4j自己的包(import org.apache.logging.log4j.LogManager)
配置文件的格式:log2j配置文件能够是xml格式的,也能够是json格式的,
配置文件的位置:log4j2默认会在classpath目录下寻找log4j2.xml、log4j.json、log4j.jsn等名称的文件,若是都没有找到,则会按默认配置输出,也就是输出到控制台,也能够对配置文件自定义位置(须要在web.xml中配置),通常放置在src/main/resources根目录下便可
纯Java方式:
public static void main(String[] args) throws IOException { File file = new File("D:/log4j2.xml"); BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); final ConfigurationSource source = new ConfigurationSource(in); Configurator.initialize(null, source); Logger logger = LogManager.getLogger("myLogger"); }
Web工程方式:
<context-param> <param-name>log4jConfiguration</param-name> <param-value>/WEB-INF/conf/log4j2.xml</param-value> </context-param> <listener> <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class> </listener>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
示例结果:
结果解释:
日志管理器获取的是根日志器LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
对应的log4j2.xml中的Loggers节点下的Root,由于该根日志器的level=“info”,因此输出的info级别以上的日志
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <File name="FileAppender" fileName="D:/logs/app.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </File> <!-- 发现Async 好像PatternLayout的输出格式配置的和输出的格式不同,不用异步就彻底同样 --> <Async name="AsyncAppender"> <AppenderRef ref="FileAppender"/> </Async> </Appenders> <Loggers> <Logger name="AsyncFileLogger" level="trace" additivity="true"> <AppenderRef ref="AsyncAppender" /> </Logger> <Root level="info"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Log4j2Test { public static void main(String[] args) { Logger logger = LogManager.getLogger("AsyncFileLogger"); // Logger的名称 logger.trace("trace level"); logger.debug("debug level"); logger.info("info level"); logger.warn("warn level"); logger.error("error level"); logger.fatal("fatal level"); } }
AsyncFileLogger的additivity的值若是为false的话,就不会在控制台上输出或者为该Logger再增长一个输出源Consloe
<Logger name="AsyncFileLogger" level="trace" additivity="false"> <AppenderRef ref="AsyncAppender" /> <AppenderRef ref="Console" /> </Logger>
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <properties> <property name="LOG_HOME">D:/logs</property> <property name="FILE_NAME">mylog</property> </properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingRandomAccessFile> <Async name="AsyncAppender"> <AppenderRef ref="RollingRandomAccessFile"/> </Async> </Appenders> <Loggers> <Logger name="RollingRandomAccessFileLogger" level="info" additivity="false"> <AppenderRef ref="AsyncAppender" /> <AppenderRef ref="Console" /> </Logger> </Loggers> </Configuration>
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Log4j2Test { public static void main(String[] args) { Logger logger = LogManager.getLogger("RollingRandomAccessFileLogger"); for(int i = 0; i < 50000; i++) { logger.trace("trace level"); logger.debug("debug level"); logger.info("info level"); logger.warn("warn level"); logger.error("error level"); logger.fatal("fatal level"); } try { Thread.sleep(1000 * 61); } catch (InterruptedException e) {} logger.trace("trace level"); logger.debug("debug level"); logger.info("info level"); logger.warn("warn level"); logger.error("error level"); logger.fatal("fatal level"); } }
RollingRandomAccessFile 会根据命名规则当文件知足必定大小时就会另起一个新的文件
log4j2.xml文件的配置大体以下:
Configuration:为根节点,有status和monitorInterval等多个属性
Appenders:输出源,用于定义日志输出的地方
log4j2支持的输出源有不少,有控制台Console、文件File、RollingRandomAccessFile、MongoDB、Flume 等
Console:控制台输出源是将日志打印到控制台上,开发的时候通常都会配置,以便调试
File:文件输出源,用于将日志写入到指定的文件,须要配置输入到哪一个位置(例如:D:/logs/mylog.log)
RollingRandomAccessFile: 该输出源也是写入到文件,不一样的是比File更增强大,能够指定当文件达到必定大小(如20MB)时,另起一个文件继续写入日志,另起一个文件就涉及到新文件的名字命名规则,所以须要配置文件命名规则
这种方式更加实用,由于你不可能一直往一个文件中写,若是一直写,文件过大,打开就会卡死,也不便于查找日志。
NoSql:MongoDb, 输出到MongDb数据库中
Flume:输出到Apache Flume(Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各种数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各类数据接受方(可定制)的能力。)
Async:异步,须要经过AppenderRef来指定要对哪一种输出源进行异步(通常用于配置RollingRandomAccessFile)
PatternLayout:控制台或文件输出源(Console、File、RollingRandomAccessFile)都必须包含一个PatternLayout节点,用于指定输出文件的格式(如 日志输出的时间 文件 方法 行数 等格式),例如 pattern=”%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n”
%d{HH:mm:ss.SSS} 表示输出到毫秒的时间 %t 输出当前线程名称 %-5level 输出日志级别,-5表示左对齐而且固定输出5个字符,若是不足在右边补0 %logger 输出logger名称,由于Root Logger没有名称,因此没有输出 %msg 日志文本 %n 换行 其余经常使用的占位符有: %F 输出所在的类文件名,如Log4j2Test.java %L 输出行号 %M 输出所在方法名 %l 输出语句所在的行数, 包括类名、方法名、文件名、行数
Loggers:日志器
日志器分根日志器Root和自定义日志器,当根据日志名字获取不到指定的日志器时就使用Root做为默认的日志器,自定义时须要指定每一个Logger的名称name(对于命名能够以包名做为日志的名字,不一样的包配置不一样的级别等),日志级别level,相加性additivity(是否继承下面配置的日志器), 对于通常的日志器(如Console、File、RollingRandomAccessFile)通常须要配置一个或多个输出源AppenderRef;
每一个logger能够指定一个level(TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF),不指定时level默认为ERROR
additivity指定是否同时输出log到父类的appender,缺省为true。
<Logger name="rollingRandomAccessFileLogger" level="trace" additivity="true"> <AppenderRef ref="RollingRandomAccessFile" /> </Logger>
<properties> <junit.version>3.8.1</junit.version> <log4j.version>2.5</log4j.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- slf4j + log4j2 begin --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.10</version> </dependency> <dependency> <!-- 桥接:告诉Slf4j使用Log4j2 --> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.2</version> </dependency> <dependency> <!-- 桥接:告诉commons logging使用Log4j2 --> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> </dependency> <!-- log4j end--> </dependencies> <!-- 使用aliyun镜像 --> <repositories> <repository> <id>aliyun</id> <name>aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository> </repositories>
二、配置log2.xml
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <properties> <property name="LOG_HOME">D:/logs</property> <property name="FILE_NAME">mylog</property> <property name="log.sql.level">info</property> </properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n" /> </Console> <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingRandomAccessFile> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console" /> <AppenderRef ref="RollingRandomAccessFile" /> </Root> <Logger name="com.mengdee.dao" level="${log.sql.level}" additivity="false"> <AppenderRef ref="Console" /> </Logger> </Loggers> </Configuration>
三、 Java
package com.mengdee.manage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Log4j2Test { // Logger和LoggerFactory导入的是org.slf4j包 private final static Logger logger = LoggerFactory.getLogger(Log4j2Test.class); public static void main(String[] args) { long beginTime = System.currentTimeMillis(); for(int i = 0; i < 100000; i++) { logger.trace("trace level"); logger.debug("debug level"); logger.info("info level"); logger.warn("warn level"); logger.error("error level"); } try { Thread.sleep(1000 * 61); } catch (InterruptedException e) {} logger.info("请求处理结束,耗时:{}毫秒", (System.currentTimeMillis() - beginTime)); //第一种用法 logger.info("请求处理结束,耗时:" + (System.currentTimeMillis() - beginTime) + "毫秒"); //第二种用法 } }
四、运行结果
持续完善中。。。