1. 程式人生 > >系統日誌框架搭建

系統日誌框架搭建

1. 市面是最通用的是 log4j2+slf4j。理由是用slf4j實現藉口便於日後可能的日誌框架遷移,用log4j2實現日誌核型。畢竟是2.0版本,取長補短過。需要注意的是, log4j2 和 log4j的版本groupId、artifactId都是一樣的,區別是version不一樣-- 1.X / 2.X。讓我疑惑了很久。官網也找不到1.X的介紹了,看來是不再維護了。

用log4j2+slf4j配置的寫法是 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private Logger logger = LoggerFactory.getLogger(getClass());

如果只用log4j2,不用slf4j,寫法是

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
private static Logger logger = LogManager.getLogger(getClass());

2.需要用到的最低配置jar包下。 需要注意的事,大概率會出現jar包衝突,如slf4j-simple、slf4j-log4j-imp等,會顯示Class path contains multiple SLF4J bindings。找到衝突的jar包排除即可。

<!--日誌 slf4j 核心包  -->
 <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.7.12</version>
 </dependency>
 <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>jcl-over-slf4j</artifactId>
     <version>1.7.1</version>
 </dependency>
 <!--日誌 log4j2 核心包  -->
 <dependency>
     <groupId>org.apache.logging.log4j</groupId>
     <artifactId>log4j-core</artifactId>
     <version>2.5</version>
 </dependency>
 <!--日誌 log4j-slf4j 集合包  -->
 <dependency>
     <groupId>org.apache.logging.log4j</groupId>
     <artifactId>log4j-slf4j-impl</artifactId>
     <version>2.5</version>
 </dependency>

如果想用非同步記錄日誌,還需要

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.2</version>
</dependency>

3. 官網的文件很坑,找了半天沒有找到log4j2.xml的預設路徑。對於maven、gradle專案,百度找到了預設路徑是src-main-resources下。

4. log4j2配置項的意義中 <Appenders> <Loggers>

Appenders是定義一個日誌輸出格式,需要在loggers裡使用ref進行引號才是一個日誌系統的完整閉環。這麼做的好處是appender可以重複使用了,可以應付複雜的日誌配置。

Loggers裡面有個很有用的配置項Logger,可以對不同包路徑的下的logger差異化配置。比如我引用了spring的包,只想看到spring的info級別的,就用下面這樣配置。

<Loggers>
    <Logger name="org.springframework" level="info">
        <AppenderRef ref="Console"/>
    </Logger>
    <Root level="debug">
        <AppenderRef ref="console"/>
        <AppenderRef ref="log"/>
    </Root>
</Loggers>

5. 非同步配置

使用非同步配置代替同步,提高伺服器效能

用<asyncRoot>代替<Root> ,用<asyncLogger>代替<Logger>

6. slf4j的設計模式--門面模式

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

static Set<URL> findPossibleStaticLoggerBinderPathSet() {
    // use Set instead of list in order to deal with bug #138
    // LinkedHashSet appropriate here because it preserves insertion order
    // during iteration
    Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
    try {
        ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
        Enumeration<URL> paths;
        if (loggerFactoryClassLoader == null) {
            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
        } else {
            paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
        }
        while (paths.hasMoreElements()) {
            URL path = paths.nextElement();
            staticLoggerBinderPathSet.add(path);
        }
    } catch (IOException ioe) {
        Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
}

約定各個日誌系統的開發者需要實現org/slf4j/impl/StaticLoggerBinder.class,即在各自的jar包實現這個類,對LoggerFactory類進行繫結。

約定各個日誌系統的使用者只使用LoggerFactory。門面模式帶來的好處就是對使用者來說,系統相當簡單,容易上手。

7.最後,附上2個log4j.xml的配置檔案,最簡單的和專案實用的。

最簡單的

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日誌級別 OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- status屬性配置log4j2自身的日誌資訊列印級別 -->
<!-- packages外掛類搜尋目錄 -->
<!-- monitorInterval配置檔案重新載入間隔,單位為秒,最小間隔5秒 -->
<Configuration status="warn">
    <Appenders>
        <!-- 輸出到控制檯 -->
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="console"/>
        </Root>
    </Loggers>
</Configuration>

專案實用的:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日誌級別 OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- status屬性配置log4j2自身的日誌資訊列印級別 -->
<!-- monitorInterval配置檔案重新載入間隔,單位為秒,最小間隔5秒 -->
<Configuration status="info" packages="utry.core.log.appender" monitorInterval="5">
  <!-- 自定義屬性 -->
  <Properties>
    <!-- 日誌檔案統一存放路徑 -->
    <Property name="LOG_HOME">D://</Property>
    <!-- 模組名,使用子站名稱 -->
    <Property name="SERVER_NAME">moduleName</Property>
  </Properties>
  <Appenders>
    <!-- 輸出到控制檯 -->  
    <Console name="console" target="SYSTEM_OUT">
        <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
    </Console>
    <!-- 錯誤日誌,輸出到滾動儲存的檔案,按天切分 -->
    <RollingRandomAccessFile name="errorLog" immediateFlush="true" fileName="${LOG_HOME}/${SERVER_NAME}.log"  
            filePattern="${LOG_HOME}/${SERVER_NAME}.%d{yyyy-MM-dd}.log.gz">  
        <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" /> 
        <Policies>
            <TimeBasedTriggeringPolicy interval="1" modulate="true" />
        </Policies>
    </RollingRandomAccessFile>
    <!-- 除錯日誌,輸出到滾動儲存的檔案,按大小切分 -->
    <RollingRandomAccessFile name="debugLog" immediateFlush="true" fileName="${LOG_HOME}/${SERVER_NAME}-debug.log"  
            filePattern="${LOG_HOME}/${SERVER_NAME}-debug.%i.log.gz">  
        <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" /> 
        <Policies>
            <SizeBasedTriggeringPolicy size="50 MB"/>
        </Policies>
        <DefaultRolloverStrategy max="10"/>
    </RollingRandomAccessFile>
  </Appenders>
  <Loggers>
    <!-- spring日誌 -->
    <AsyncLogger name="org.springframework" level="info"></AsyncLogger>
    <!-- mybaits日誌,debug級別可以看到執行的sql及引數 -->
    <AsyncLogger name="org.mybatis" level="error"></AsyncLogger>
    <AsyncRoot level="all">
        <!-- 控制檯 -->
        <AppenderRef ref="console" level="off"/>
        <!-- 錯誤日誌 -->
        <AppenderRef ref="errorLog" level="info"/>
        <!-- 除錯日誌,生產環境請註釋 -->
        <!--<AppenderRef ref="debugLog" level="debug"/>-->
    </AsyncRoot>
  </Loggers>
</Configuration>