SLF4J與Log4j實現日誌記錄

1 Introduction

沒什麼介紹…只是記錄一下自己的使用習慣

Maven

<slf4j.version>1.7.7</slf4j.version>

<dependency>
    <groupId>org.slf4j</groupId>        <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
    </dependency>
<!-- common-logging 實際調用slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<!-- java.util.logging 實際調用slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>${slf4j.version}</version>
</dependency>

2 Log4J的配置

其中tabook爲項目的名字

# log4j.properties
# Output pattern : date thread priority [category] - message   FATAL 0  ERROR 3  WARN 4  INFO 6  DEBUG 7
log4j.rootLogger=WARN, Console, RollingFile, ErrorRollingFile

#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d %-5p [%c{5}] - %m%n

#RollingFile
log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.RollingFile.File=../logs/tabook/tabook.log
log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

#ErrorRollingFile
log4j.appender.ErrorRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ErrorRollingFile.Threshold=Error
log4j.appender.ErrorRollingFile.File=../logs/tabook/tabook_error.log
log4j.appender.ErrorRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.ErrorRollingFile.layout.ConversionPattern=%d [%t] %-5p [%c] -%m%n


log4j.logger.com.nevercome.tabook=DEBUG
log4j.logger.com.nevercome.tabook.common.security.shiro=WARN
log4j.logger.com.nevercome.tabook.common.utils.JedisUtils=WARN
log4j.logger.com.nevercome.tabook.modules.sys.web.LoginController=WARN
#log4j.logger.com.thinkgem.jeesite.modules.oa.dao.OaNotifyDao.findCount=WARN

3 測試使用

分別在log4j的配置文件中定義日誌級別的包下進行測試。
觀察控制檯與兩個日誌文件的輸出

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author: sun
 * @date: 2019/4/5
 */
public class LogTest {
    private Logger logger = LoggerFactory.getLogger(getClass());

    // 如果你直接運行了Test,可能會問
    // 爲什麼沒有輸出到配置的目錄?
    // 其實它是輸出到配置的目錄了
    // 因爲你以爲配置的目錄的相對路徑是web容器啓動之後的logs
    // 它不是沒有輸出,而是在別的地方
    @Test
    public void logTest() {
        logger.info("I'm {} message", "info");
        logger.debug("I'm {} message", "debug");
        logger.warn("I'm {} message", "warn");
        logger.error("I'm {} message", "error");
    }
}

4 攔截器Interceptor與日誌

與日誌記錄說在一起的技術通常有切面AOP、過濾器Filter,攔截器Interceptor。每種技術當然各有特點…攔截也好過濾也罷在技術與應用細節上有許多不同,但其核心思想都是在一個或系列動作、事件的一個或某些時刻插入進去進行自己的操作。

代碼出自JeeSite快速開發框架,請求攔截,進行日誌記錄。

import com.nevercome.tabook.common.mapper.JsonMapper;
import com.nevercome.tabook.common.service.BaseService;
import com.nevercome.tabook.common.utils.DateUtils;
import com.nevercome.tabook.modules.sys.utils.LogUtils;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * 日誌攔截器
 */
public class LogInterceptor extends BaseService implements HandlerInterceptor {

    private static final ThreadLocal<Long> startTimeThreadLocal =
            new NamedThreadLocal<Long>("ThreadLocal StartTime");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        if (logger.isDebugEnabled()) {
            // 開始時間
            long beginTime = System.currentTimeMillis();
            // 線程綁定變量(該數據只有當前請求的線程可見)
            startTimeThreadLocal.set(beginTime);
            // sun 20190405 添加對請求參數的打印
            Map paramMap = request.getParameterMap();
            logger.debug("開始計時: {}  URI: {} Params: {}", new SimpleDateFormat("hh:mm:ss.SSS")
                    .format(beginTime), request.getRequestURI(), JsonMapper.toJsonString(paramMap));
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        if (modelAndView != null) {
            logger.info("ViewName: " + modelAndView.getViewName());
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {

        // 保存日誌
        LogUtils.saveLog(request, handler, ex, null);

        // 打印JVM信息。
        if (logger.isDebugEnabled()) {
            long beginTime = startTimeThreadLocal.get(); // 得到線程綁定的局部變量(開始時間)
            long endTime = System.currentTimeMillis(); // 結束時間
            logger.debug("計時結束:{}  耗時:{}  URI: {}  最大內存: {}m  已分配內存: {}m  已分配內存中的剩餘空間: {}m  最大可用內存: {}m",
                    new SimpleDateFormat("hh:mm:ss.SSS").format(endTime), DateUtils.formatDateTime(endTime - beginTime),
                    request.getRequestURI(), Runtime.getRuntime().maxMemory() / 1024 / 1024, Runtime.getRuntime().totalMemory() / 1024 / 1024, Runtime.getRuntime().freeMemory() / 1024 / 1024,
                    (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() + Runtime.getRuntime().freeMemory()) / 1024 / 1024);
            // 刪除線程變量中的數據,防止內存泄漏
            startTimeThreadLocal.remove();
        }
    }
}

這其中的BaseService是否繼承並不十分重要。

配置MVC的攔截器

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="${adminPath}/**" />
        <mvc:exclude-mapping path="${adminPath}/"/>
        <mvc:exclude-mapping path="${adminPath}/login"/>
        <mvc:exclude-mapping path="${adminPath}/sys/menu/tree"/>
        <mvc:exclude-mapping path="${adminPath}/sys/menu/treeData"/>
        <mvc:exclude-mapping path="${adminPath}/oa/oaNotify/self/count"/>
        <bean class="com.nevercome.tabook.modules.sys.interceptor.LogInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

5 關於MyBatis

Log4j會自動按照配置打印Mybatis的Sql語句,如果你不想打印某一個模塊的Sql語句,只需要控制這個dao層的package的打印級別爲DEBUG以上就可以了。比如:

com.nevercome.tabook.modules.sys.dao=WARN
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章