1. 程式人生 > >java程式日誌管理

java程式日誌管理

初入軟體開發這一行的人,可能對日誌管理的概念並不是很明確,大概是由於經驗所限,以至於根本還考慮不到這個問題。
而從某種意義上來說,日誌管理實際上也不需要初入這一行的人來管,他們只需要負責實現自己的主要業務邏輯和功能就好了。
我當初剛入行的時候就有很長一段時間完全不用去關心日誌,到後來偶爾涉及到的時候,也都是從其他地方採用cv大法直接搬用。
不過,隨著工作時間的變化,隨著手頭上任務重要程度的變化,也隨著接觸到的專案數量的變化,讓我越來越意識到日誌的重要性,它在整個系統中發揮著至關重要的作用!
尤其是涉及到需要後期維護的專案,更是經常需要依靠日誌來定位問題,可以說他是執行中的專案出問題時,找問題最好的手段。
java中日誌管理的技術有很多,像java自身的java.util.logging,apache的commons-logging,以及slf4j、log4j、logback等等。
其中java.util.logging在日常開發中用的不是很多,用的比較多的後邊四個,commons-logging和slf4j是介面,log4j和logback是具體的實現,在我所接觸的專案中就用到了這幾個。
因為java推薦的就是面向介面程式設計,所以一般推薦使用的就是那兩個介面,但是又由於commons-logging的動態繫結造成了一些問題,因此這兩個裡邊又推薦使用slf4j。
同樣的,在兩種實現中,logback和log4j是由同一個作者開發,logback出現的更晚,更好,因為也就更推薦用logback。
那麼綜上而言,目前最推薦的java中的日誌管理,就是使用slf4j+logback。
實際上,說了這麼多,真正用起來是很簡單的,只需要匯入相關jar包,寫好相關配置,然後需要的地方呼叫就好了,學習的過程中為了比較不同,我也寫了一個簡單的額例子。
因為目前大部分的專案都是maven管理,spring框架,所以這個例子中也算是順便聯絡spring的最基礎配置,就也用了spring。
maven的導包配置pom.xml如下,為了比較這四項技術,所以相關的包我全都導了進來,commons-logging是其他jar依賴的,所以便沒有手動再導一次:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>logTest</groupId>
  <artifactId>logTest</artifactId
>
<packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>logTest Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId
>
<version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.12</version> </dependency> </dependencies> <build> <finalName>logTest</finalName> </build> </project>

然後寫了簡單的spring.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd"  >

    <!-- 引入屬性檔案 -->
    <!-- --> 
    <context:property-placeholder location="classpath:log4j.properties" ignore-unresolvable="true" />

    <!-- 配置spring註解掃描 -->
  <context:component-scan base-package="logService.service.impl" />
</beans>

很簡單,就是配置引入配置檔案和spring裝配掃描路徑,然後是兩個不同的日誌配置檔案,從命名就很容易知道哪個對應的是哪個,首先是log4j.properties:

`log4j.properties:
log4j.rootLogger=WARN, CONSOLE, FILE  
## for console  
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender  
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout  
log4j.appender.CONSOLE.layout.ConversionPattern=%d{MM-ddHH:mm:ss}[%c-%L][%t][%-4r] - %m%n  
log4j.appender.CONSOLE.Encoding=utf-8
## for file  
log4j.appender.FILE=org.apache.log4j.RollingFileAppender  
log4j.appender.FILE.File=C:/Users/Think/Desktop/log4j.log 
log4j.appender.FILE.MaxFileSize=1MB  
log4j.appender.FILE.Append = true  
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout  
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-ddHH\:mm\:ss} [%t] %-5p  %-4r %x - %m%n
log4j.appender.FILE.Encoding=utf-8`

然後是logback.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定義日誌檔案的儲存地址 勿在 LogBack 的配置中使用相對路徑-->
<property name="LOG_HOME" value="C:/Users/Think/Desktop" />
<!-- 控制檯輸出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日誌檔案 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日誌檔案輸出的檔名-->
<FileNamePattern>${LOG_HOME}/logback.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日誌檔案保留天數-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日誌檔案最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>

<!-- 日誌輸出級別 -->
<root level="WARN">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
</configuration>

這些配置基本上都是最最基礎的配置,具體的代表什麼意思,網上也有很多很多的說明。
然後分別寫了兩個使用了common-logging和slf4j的介面:

package logService.service;

/**
 * 使用common-logger
 * 
 * @author tuzongxun
 * @date 2017年2月20日 下午3:36:19
 */
public interface CommonLogService {

    public void printLog(String msg);

}
package logService.service;

/***
 * Slf4j日誌列印
 * 
 * @author tuzongxun
 * 
 * @date 2017年2月20日 下午3:39:11
 */
public interface Slf4jLogService {
    public void printLog(String msg);
}

還有對應的實現類,實現類實際上什麼都沒做,就是呼叫日誌介面隨便列印一條日誌而已:

package logService.service.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

import logService.service.CommonLogService;

/**
 * 使用commons-logger和log4j列印日誌
 * 
 * @author tuzongxun
 * @date 2017年2月20日 下午3:36:33
 */
@Component
public class CommonLogServiceImp implements CommonLogService {
    private Log logger = LogFactory.getLog(CommonLogServiceImp.class);

    public void printLog(String msg) {
        logger.warn("Commonlogger日誌列印:" + msg);

    }
}
package logService.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import logService.service.Slf4jLogService;

/***
 * Slf4j日誌列印
 * 
 * @author tuzongxun
 * @date 2017年2月20日 下午3:38:55
 */
@Component
public class Slf4jLogServiceImpl implements Slf4jLogService {

    private Logger log = LoggerFactory.getLogger(Slf4jLogServiceImpl.class);

    @Override
    public void printLog(String msg) {
        log.warn("slf4j日誌列印:" + msg);

    }
}

從上邊的程式碼中,實際上根本看不出什麼問題,只看到呼叫了兩個介面而已,方法幾乎都是一模一樣,至於具體用了哪個實現,有什麼區別呢,完全不知道,所以我寫了對應的test類:

package logTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import logService.service.CommonLogService;
import logService.service.Slf4jLogService;

/**
 * 日誌管理測試
 * 
 * @author tuzongxun
 * @date 2017年2月22日 下午3:40:17
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class LogTest {
    @Autowired
    private CommonLogService commonLogService;

    @Autowired
    private Slf4jLogService slf4jLogService;

    @Test
    public void commonLogTest() {
        commonLogService.printLog("commonlog日誌列印");
    }

    @Test
    public void slf4jLogTest() {
        slf4jLogService.printLog("slf4j日誌列印");
    }

}

那麼現在有了程式碼,我就可以說一說區別了,實際上一開始我說的pom.xml並不是一次匯入的,可能有的專案中只有其中幾個,而有的專案中我剛才匯入的jar包他們也全都匯入了。
經過我的測試發現,當使用common-logging的時候,是隻能使用log4j的,如果去掉log4j的jar包,那麼結果就是執行junit後沒有生成對應的日誌檔案。
而如果用slf4j就可以使用兩個實現,只不過和common-logging不同的是,使用slf4j時除開log4j的包,還需要slf4j連線log4j的包。
使用slf4j和logback要匯入logback的包自然就不必說了,但是同時到如log4j和logback的包就導致了另一個問題存在,就是使用slf4j的時候不僅會用log4j,還會用logback,導致結果每次都會有兩份日誌檔案。
因此呢,在這種情況下就需要匯入另一個包,也就是我匯入的

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.12</version>
    </dependency>