SSM中 實現日誌記錄(AOP+log4j2)
阿新 • • 發佈:2019-01-22
本人使用:
- 整合開發環境:idea
- 專案管理工具:maven
- 資料庫:oracle
- 框架:Spring+SpringMVC+myBatis
主要步驟:
- 在pom.xml中引入外掛依賴
- log4j2.xml檔案配置
- web.xml檔案配置
- 編寫日誌管理類LogInterceptor.class
- spring-mvc.xml中配置AOP【可以使用註解和自定義註解,我這使用的是xml】
- 在controller中編寫一個執行時異常
具體實現:
1,在pom.xml中引入外掛依賴
<!--slf4j與log4j的橋接包,裡面包括了:log4j-api,log4j-core和slf4j-api--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <version>2.10.0</version> <scope>runtime</scope> </dependency> <!--另外需要jar--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency>
2,log4j2.xml檔案配置
<?xml version="1.0" encoding="UTF-8"?> <!--日誌級別以及優先順序排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration後面的status,這個用於設定log4j2自身內部的資訊輸出,可以不設定,當設定成trace時,你會看到log4j2內部各種詳細輸出--> <configuration status="info"> <!-- 變數配置 應該是日誌檔案儲存路徑--> <Properties> <!-- (*必須,各應用需要修改) 部署應用的名稱,命名規則 :全部小寫字母、中短橫線、數字,與微服務命名,disconf中命名一致 --> <property name="APPNAME">park-service</property> <!-- (各應用需要修改)日誌檔案本地存放目錄路徑 建議各個環境跟生產環境統一起來,方便維護 --> <!--sys:catalina.home tomcat的根目錄--> <Property name="logBaseFolder">${sys:catalina.home}/logs/zcrTest/</Property> <!-- *必須並強制遵守 日誌檔案的編碼 --> <property name="log_charset">UTF-8</property> <!--輸出日誌格式--> <property name="log_pattern"> %d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n </property> </Properties> <!--先定義所有的appender--> <appenders> <!--這個輸出控制檯的配置--> <console name="Console" target="SYSTEM_OUT"> <!--輸出日誌的格式 %l :表示某個類--> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%t] %-5p %c{1}:%L - %msg%n"/> </console> <!-- 這個會打印出所有的info及以下級別的資訊,每次大小超過size,則這size大小的日誌會自動存入按年份-月份建立的資料夾下面並進行壓縮,作為存檔--> <RollingFile name="RollingFileInfo" fileName="${logBaseFolder}/${APPNAME}-info.log" filePattern="${logBaseFolder}/%d{yyyy-MM}/${APPNAME}-info-%d{dd HH:mm:ss}.%i.log.gz"> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 日誌輸出格式 --> <PatternLayout charset="${log_charset}" pattern="${log_pattern}" /> <!-- 以下是日誌壓縮包目錄的建議格式名稱 建議1天歸檔依次,壓縮檔案上線建議為200,這裡預估每個磁碟儲存空間200G左右,每個壓縮包最大值200MB --> <Policies> <TimeBasedTriggeringPolicy modulate="true" interval="1"/> <SizeBasedTriggeringPolicy size="20 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileError" fileName="${logBaseFolder}/${APPNAME}-error.log" filePattern="${logBaseFolder}/%d{yyyy-MM}/${APPNAME}-error-%d{dd HH:mm:ss}.%i.log.gz"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout charset="${log_charset}" pattern="${log_pattern}" /> <Policies> <TimeBasedTriggeringPolicy modulate="true" interval="1"/> <SizeBasedTriggeringPolicy size="20 MB"/> </Policies> </RollingFile> </appenders> <!--然後定義logger,只有定義了logger並引入的appender,appender才會生效--> <loggers> <!--過濾掉spring和mybatis的一些無用的DEBUG資訊--> <logger name="org.springframework" level="INFO"/> <logger name="org.mybatis" level="INFO"/> <root level="all"> <appender-ref ref="Console"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>
3,web.xml檔案配置
<!-- 日誌記錄 --> <context-param> <param-name>log4jConfiguration</param-name> <param-value>classpath:log4j2.xml</param-value> </context-param> <!--動態修改log4j2.xml:容器會每60秒掃描log4j的配置檔案--> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <!-- log4j2-begin: 監聽器和過濾器--> <listener> <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class> </listener> <filter> <filter-name>log4jServletFilter</filter-name> <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class> </filter> <filter-mapping> <filter-name>log4jServletFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <!-- log4j2-end -->
4,編寫日誌管理類LogInterceptor.class
package com.zrkj.interceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
@Component
public class LogInterceptor {
/**
* 因為我這引入的是slf4j與log4j的橋接包,即可用log4j也可用slf4j輸出日誌
* log4j:
* import org.apache.logging.log4j.LogManager;
* import org.apache.logging.log4j.Logger;
* slf4j:
* import org.slf4j.Logger;
* import org.slf4j.LoggerFactory;
*/
private static final Logger logger = LogManager.getLogger(LogInterceptor.class);
// private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
String logStr=null;
// //前置通知
// public void before(JoinPoint jp){
// System.out.println("==========aaaaaaaaaaaaaa");
// logStr=jp.getTarget().getClass().getName()+"類的"
// +jp.getSignature().getName()+"方法開始執行******Start******";
// logger.info(logStr);
// }
// //最終通知
// public void after(JoinPoint jp){
// logStr=jp.getTarget().getClass().getName()+"類的"
// +jp.getSignature().getName()+"方法執行結束******End******";
// logger.info(logStr);
// }
// //異常丟擲後通知
// public void afterThrowing(JoinPoint call){
// String className = call.getTarget().getClass().getName();
// String methodName = call.getSignature().getName();
// System.out.println(className+"."+methodName+"()方法丟擲了異常...");
// }
// //後置通知
// public void afterReturn(JoinPoint call){
// String className = call.getTarget().getClass().getName();
// String methodName = call.getSignature().getName();
// System.out.println(className+"."+methodName+"()方法正常執行結束...");
// }
//用來做環繞通知的方法可以第一個引數定義為org.aspectj.lang.ProceedingJoinPoint型別
public Object around(ProceedingJoinPoint call) throws Throwable {
Object result = null;
//取得類名和方法名
String className = call.getTarget().getClass().getName();
String methodName = call.getSignature().getName();
//相當於前置通知
logStr=className+"類的"+methodName+"方法開始執行******Start******";
logger.info(logStr);
try {
result = call.proceed();
//相當於後置通知
logStr=className+"."+methodName+"()方法正常執行結束...";
logger.info(logStr);
} catch (Throwable e) {
//相當於異常丟擲後通知
StackTraceElement stackTraceElement= e.getStackTrace()[e.getStackTrace().length-1];
Object[] args=call.getArgs();
logger.error("----------------------------------------------------------------------------------");
logger.error ( "===執行{}類的{}()方法的{}行",className,methodName,stackTraceElement.getLineNumber());
logger.error("===異常資訊為:{} ",e.fillInStackTrace().toString());
logger.error("===引數資訊為:{} ",args);
throw e;
}finally{
//相當於最終通知
logStr=className+"類的" +methodName+"方法執行結束******End******";
logger.info(logStr);
}
return result;
}
}
5,spring-mvc.xml中配置AOP,因為使用的是SSM框架,所以在SpringMvc容器中配置
<!-- 使用xml配置aop ,引入AOP的名稱空間-->
<!-- 強制使用cglib代理,如果不設定,將預設使用jdk的代理,但是jdk的代理是基於介面的 -->
<aop:config proxy-target-class="true" />
<aop:config>
<!--定義切面-->
<aop:aspect id="logAspect" ref="logInterceptor">
<!--定義切入點-->
<aop:pointcut expression="execution(* com.zrkj.controller.*.*(..))" id="logPointCut"/>
<!--方法執行之前被呼叫執行的-->
<!--<aop:before method="before" pointcut-ref="logPointCut"/><!–一個切入點的引用–>-->
<!--<aop:after method="after" pointcut-ref="logPointCut"/><!–一個切入點的引用–>-->
<!--<aop:after-throwing method="afterThrowing" pointcut-ref="logPointCut" />-->
<!--<aop:after-returning method="afterReturn" pointcut-ref="logPointCut" />-->
<aop:around method="around" pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
6,在controller中編寫一個執行時異常
@PostMapping(value = "loginUser")
@ResponseBody
public void loginUser(String phone,String password,HttpServletResponse response){
//1,驗證該電話號碼和密碼是否正確
Users users = userService.selectByPhoneAndPwd(phone,password);
if(users!=null){
//2.1 正確:修改token,last_login_time
String token = userService.updateToLogin(users);
System.out.println("製造一個執行時異常");
int i = 1/0;//製造異常
if(token!=null){
//3.1 修改成功,登入
printJson(response,result(200,"登入成功",token));
}else{
//3.2 修改失敗,提示錯誤
printJson(response,result(500,"登入異常,請重試"));
}
}else{
//2.2 不正確:提示使用者
printJson(response,result(500,"手機號或密碼不正確,請重試"));
}
}
執行結果:
1,控制檯顯示:
2,日誌檔案【tomcat的根目錄】
3,park-service-info.log內容:
4,park-service-error.log內容:
ok。。。。