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