Web基礎之日志
日志在企業開發中有着不可或缺的作用,它可以用以記錄用戶操作、系統運行狀態和錯誤信息。日志記錄的好壞直接關系到系統出現問題時定位的速度。
最開始的日志一般使用log4j,后來sun公司心有不甘,在jdk 1.4中加入了一個叫java.util.logging的日志包,簡稱jul。兩種日志的api肯定是不同的,此時日志就出現了混亂。log4j的作者Ceki Gülcü就寫了一個叫Jakarta Commons Logging的接口,簡稱jcl,來共同管理log4j和jul,並且jcl中提供了一個日志實現simplelog。接着又改良了下log4j,寫出了logback。再后來Ceki Gülcü覺得jcl不好用(的確用問題),然后寫了一個新的接口slf4j,來共同管理這些日志系統。接着呢Ceki Gülcü又覺得logj4性能不夠了,又寫了一個log4j2,。真是專注日志100年啊。
所以他們之間的關系是這樣的:

其中slf4j並不提供日志實現,而是使用外觀模式創建了一個接口,來統一這些日志的api。
這也是為什么阿里規范上強制使用slf4j的原因:
【強制】應用中不可直接使用日志系統(Log4j、Logback)中的 API,而應依賴使用日志框架SLF4J 中的 API,使用門面模式的日志框架,有利於維護和各個類的日志處理方式統一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
slf4j + log4j / log4j2
認識了之后接下來就是使用了,首先在maven中導入依賴:
<!-- log4j依賴,好像不更新了,我看版本還是12年的 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- slf4j的依賴 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<!-- 告訴slf4j使用log4j,也就是綁定 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.28</version>
<scope>test</scope>
</dependency>
如果使用log4j2的話這么配:
<!-- log4j2的核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2的api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<!-- slf4j綁定log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<!-- slf4j的依賴 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
這里我將使用log4j2的xml和properties兩種方式來簡單配置。
在resources目錄下創建log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<!-- 輸出源的配置:日志輸出的目的地 -->
<Appenders>
<!--
Console標簽;指定這是一個控制台輸出源。
name:名稱,需要在被引用才會生效
target: 控制台字體的顏色
SYSTEM_ERR:紅色字體
SYSTEM_OUT:黑色字體
PatternLayout:布局,輸出到控制台日志的格式。
-->
<Console name="myConsole" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %5level %logger{36} - %msg%n"/>
</Console>
<!-- 文件輸出源
fileName:指定保存日志的文件的名字和位置。
一般寫絕對路徑。
-->
<File name="myFile" fileName="D://demo.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
<!--
多文件輸出源:
fileName:實時記錄日志的文件名和位置
filePattern : 文件封存的位置和格式
-->
<RollingFile name="rollingFile" fileName="D://demo2.log" filePattern="D://logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<!-- 多文件拆分的規則 -->
<Policies>
<!-- 基於時間的 此處interval屬性值的單位是由filePattern屬性中文件名時間精確的單位 決定的。 -->
<TimeBasedTriggeringPolicy interval="1" />
<!-- 基於文件大小的 -->
<SizeBasedTriggeringPolicy size="512 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!-- 配置日志輸出的級別的
將當前級別以及比當前級別高的消息進行輸出。
-->
<Root level="trace">
<AppenderRef ref="myConsole"/>
<!-- 可以同時多種方式輸出日志 -->
<AppenderRef ref="MyFile"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
log4j使用properties方式:
# 配置輸出源的 log4j.appender.輸出源名=輸出源的實現類
# 屬性的配置 log4j.appender.輸出源名.屬性名=屬性值
log4j.appender.a=org.apache.log4j.ConsoleAppender
log4j.appender.a.Target=System.out
log4j.appender.a.layout=org.apache.log4j.PatternLayout
log4j.appender.a.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
### log4j.rootLogger=輸出級別,輸出源1,輸出源2....
log4j.rootLogger=debug,a,file
log4j2的properties(參考):
status = warn
name = MyApp
appender.console.type = Console
appender.console.name = consoleLogDemo
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n
appender.console.target = System_out
appender.rolling.type = File
appender.rolling.name = fileLogDemo
appender.rolling.filter.threshold.type = ThresholdFilter
appender.rolling.filter.threshold.level = error
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d-%m%n
appender.rolling.append = true
appender.rolling.fileName = e:\\propertiesTest.log
rootLogger.level = debug
rootLogger.appenderRef.consolelogdemo.ref = consoleLogDemo
rootLogger.appenderRef.filelogdemo.ref = fileLogDemo
輸出格式參數為:
%p:輸出日志信息的優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%d:輸出日志時間點的日期或時間,默認格式為ISO8601,可以指定格式如:%d{yyyy/MM/dd HH:mm:ss,SSS}
%r:輸出自應用程序啟動到輸出該log信息耗費的毫秒數
%t:輸出產生該日志事件的線程名
%l:輸出日志事件的發生位置,相當於%c.%M(%F:%L)的組合,包括類全名、方法、文件名以及在代碼中的行數
%c:輸出日志信息所屬的類目,通常就是類全名
%M:輸出產生日志信息的方法名
%F:輸出日志消息產生時所在的文件名
%L:輸出代碼中的行號
%m:輸出代碼中指定的具體日志信息
%n:輸出一個回車換行符,Windows平台為"rn",Unix平台為"n"
%x:輸出和當前線程相關聯的NDC(嵌套診斷環境)
%%:輸出一個"%"字符
這是幾種常用的配置,如果覺得不夠的話可以查看官方文檔
順便這里推薦個講的很詳細的博客
打日志
在log4j2中可以這么用:
public class MyApp {
private static final Logger logger = LoggerFactory.getLogger(MyApp.class);
public static void main(String[] args) {
logger.warn("Hello, {}","slf4j");
}
}
使用{}作為占位符,性能要比log4j直接字符串拼接性能好(畢竟字符串拼接的底層原理是創建StringBuilder)
至於一大堆jar包的關系如下:

關於log4j2的如下:

