日誌接口(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) + "毫秒"); //第二種用法 } }
四、運行結果
持續完善中。。。