Spring日誌記錄+執行緒池
本文將介紹在Spring框架下如何利用攔截器做日誌記錄,簡化我們的日誌處理,
1 首先我們需要在Spring-mvc.xml中註冊這個攔截器,程式碼如下:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*/*" /><!-- 需攔截的地址 --> <mvc:exclude-mapping path="/login/loginIn" /><!-- 登入不攔截 --> <bean class="com.zsq.cn.common.sys.interceptor.LogInterceptor" /> </mvc:interceptor> </mvc:interceptors>
2 新建一個日誌類 (setter、getter方法就沒有拷貝了,但是重寫了setParams方法)
3 新建一個攔截器類,即 1中bean的class類public class Log { private static final long serialVersionUID = 1L; private String type; // 日誌型別(1:操作日誌;2:錯誤日誌) private String title; // 日誌標題 private String remoteAddr; // 操作使用者的IP地址 private String requestUri; // 操作的URI private String method; // 操作的方式 private String params; // 操作提交的資料 private String userAgent; // 操作使用者代理資訊 private String exception; // 異常資訊 // 日誌型別(1:接入日誌;2:錯誤日誌) public static final String TYPE_ACCESS = "1"; public static final String TYPE_EXCEPTION = "2"; public void setParams(String params) { this.params = params; } /** * 設定請求引數 * @param paramMap */ @SuppressWarnings({ "unchecked", "deprecation" }) public void setParams(Map paramMap){ if(paramMap == null){ return; } StringBuilder sb = new StringBuilder(); for(Map.Entry<String, String[]> para : (((Map<String, String[]>) paramMap).entrySet())){ sb.append(para.getKey().endsWith("password") ? para.getKey()+"="+"...":para.getKey()+"="+Arrays.toString(para.getValue())+"::"); } setParams(sb.toString()); } @Override public String toString(){ return ReflectionToStringBuilder.toString(this); } }
這裡需要說明下,這個類的最後一個方法的程式段
String title = (String)request.getAttribute(Constant.LOG_TITLE);
if(title != null){
LogUtils.saveNormalLog(request, handler, ex);
}
因為有很多請求我們是不需要做日誌記錄的,所以我判斷了下 如果有日誌資訊,則進行日誌的持久化操作。這裡的Constant.LOG_TITLE一般寫在Controller的方法裡,我將會在第5個程式段中給出例項。
public class LogInterceptor implements HandlerInterceptor{ private Logger logger = LoggerFactory.getLogger(LogInterceptor.class); //每次請求都會保留一個HttpServletRequest副本 public static ThreadLocal<HttpServletRequest> curentHttpRequest = new ThreadLocal<>(); public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { curentHttpRequest.set(request);//設定本次請求的HttpServletRequest return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // System.out.println(LocalDate.now()+"訪問檢視"+modelAndView.getViewName()); if(modelAndView != null){ logger.info(LocalDate.now()+"訪問檢視"+modelAndView.getViewName()); } } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String title = (String)request.getAttribute(Constant.LOG_TITLE); if(title != null){ LogUtils.saveNormalLog(request, handler, ex); } } }
4 新建一個日誌工具類來處理日誌
public class LogUtils {
/**
* 儲存日誌
* @param request
* @param handler
* @param ex
* @param title
*/
public static void saveLog(HttpServletRequest request, Object handler, Exception ex,String title) {
User user = SessionUtils.getSession(request, SessionNames.SESSION_USER);
if (user != null){
Log log = new Log();
log.setTitle(title);
log.setType(ex == null ? Log.TYPE_ACCESS : Log.TYPE_EXCEPTION);
log.setRemoteAddr(request.getRemoteAddr());
log.setUserAgent(request.getHeader("user-agent"));
log.setRequestUri(request.getRequestURI());
log.setParams(request.getParameterMap());
log.setMethod(request.getMethod());
// 非同步儲存日誌
new Thread(new SaveLogThread(log)).start();
}
}
/**儲存異常日誌
* @param exceptionMessage
*/
public static void saveExceptionLog(String exceptionMessage,Exception ex){
HttpServletRequest request = LogInterceptor.curentHttpRequest.get();
String title = (String) request.getAttribute(Constant.LOG_TITLE);
saveLog(request,null,ex,title+"--"+exceptionMessage);
}
/**
* 正常儲存日誌
* @param request
* @param handler
* @param ex
*/
public static void saveNormalLog(HttpServletRequest request, Object handler, Exception ex) {
saveLog(request,handler,ex,(String)request.getAttribute(Constant.LOG_TITLE));
}
/**
* 單獨開啟一個執行緒持久化日誌
*
*/
static class SaveLogThread implements Runnable{
private Log log;
public SaveLogThread(Log log){
this.log = log;
}
@Override
public void run() {
if(log != null){
//這裡進行持久化操作
System.out.println("-----------請進行日誌持久化操作,下面將輸出日誌資訊--------------");
System.out.println(log.toString());
}
}
}
}
5 新建一個測試請求類
@Controller
@RequestMapping("test")
public class TestController {
@Autowired
private TestService testService;
@RequestMapping("test")
public String test(HttpServletRequest request,User user,Model model){
//注意只有這裡request設定日誌資訊後,該請求才會持久化操作,因為我在3中的最後一個方法判斷了如果沒有日誌資訊則不持久化
request.setAttribute(Constant.LOG_TITLE, "測試");
User user2 = testService.getUser();
model.addAttribute("user", user2);
return "index/test";
}
}
但是需要說明的是,只要異常的地方,並且你想記錄異常日誌,呼叫saveExceptionLog(String exceptionMessage,Exception ex)方法即可,即使該請求的Controller沒有設定request.setAttribute(Constant.LOG_TITLE, "測試");異常日誌也會持久化。
當然,如果每一次日誌記錄系統都開啟一個執行緒的話,那將會很耗費系統資源的,因為單個執行緒的開啟和銷燬都是需要時間的,在這裡我們需要對上面的實現方法進行改進,使用執行緒去做日誌持久化記錄。
2.1 首先在spring-context.xml中註冊一個執行緒池的bean
<!-- 使用執行緒池管理執行緒 -->
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" /> <!-- 執行緒池維護執行緒的最少數量 -->
<property name="keepAliveSeconds" value="300" /> <!-- 執行緒池維護執行緒所允許的空閒時間 -->
<property name="maxPoolSize" value="10" /> <!-- 執行緒池維護執行緒的最大數量 -->
<property name="queueCapacity" value="25" /> <!-- 執行緒池所使用的緩衝佇列大小,不設定的話 預設為Integer.MAX_VALUE -->
</bean>
說明:你可以看到,註冊bean的class 是spring提供的ThreadPoolTaskExecutor,我看了下這個類的原始碼如下
原始碼中,預設為某些屬性賦了預設值,請注意上圖中劃紅線的一個屬性,ThreadPoolExecutor。這個類是jdk提供的java.util.concurrent.ThreadPoolExecutor,其實,spring的ThreadPoolTaskExecutor只是對ThreadPoolExecutor完成了一次封裝,其最終的實現仍然是ThreadPoolExecutor這個類。到底ThreadPoolTaskExecutor是怎樣封裝ThreadPoolExecutor的呢?,我們就拿corePoolSize為例,在xml檔案設定ThreadPoolTaskExecutor類的屬性 :
<property name="corePoolSize" value="5" /> <!-- 執行緒池維護執行緒的最少數量 -->
在spring註冊這個bean的時候,將會執行下面程式碼。這段程式碼也是ThreadPoolTaskExecutor的原始碼,ThreadPoolTaskExecutor中的每個屬性都有這樣的一段設定屬性值得程式碼
public void setCorePoolSize(int corePoolSize) {
synchronized (this.poolSizeMonitor) {
this.corePoolSize = corePoolSize;
if (this.threadPoolExecutor != null) {
this.threadPoolExecutor.setCorePoolSize(corePoolSize);
}
}
}
好了,迴歸主題。繼續講一下如何用執行緒池來做日誌服務
2.3 新建一個上下文工具,用於執行時獲取spring註冊的bean,這個類需要在spring-context.xml檔案中配置如下:
<bean id="ApplicationContextUtils" class="com.zsq.cn.common.sys.utils.ApplicationContextUtils" />
public class ApplicationContextUtils implements ApplicationContextAware,DisposableBean{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void destroy() throws Exception {
this.applicationContext = null;
}
/**
* 根據類獲取bean
* @param t
* @return
*/
public static <T> T getBean(Class<T> t){
if(null != applicationContext){
T object = null;
object = applicationContext.getBean(t);
return object;
}
return null;
}
/**
* 根據bean的名字獲取bean
* @param beanName
* @return
*/
public static Object getBean(String beanName){
if(null != applicationContext){
return applicationContext.getBean(beanName);
}
return null;
}
}
2.2 新建一個執行緒池工具類,所有實現Runable介面的執行緒都可以用這個執行緒池
public class ThreadPoolUtils {
/**
* 獲取執行緒池物件,其已經在 spring-context.xml中註冊。
*/
private static ThreadPoolTaskExecutor tpte =
(ThreadPoolTaskExecutor) ApplicationContextUtils.getBean(ThreadPoolTaskExecutor.class);
public static void execute(Runnable task){
tpte.execute(task);
}
}
2.3 將 程式碼段 4 中的
//非同步報錯日誌
new Thread(new SaveLog(log)).start()
這行程式碼改為
ThreadPoolUtils.execute(new LogTask(log));
這樣,所有的日誌服務都可以交給執行緒池去處理了。關於執行緒池這篇文章沒有太深入研究原理,還有一些特性並沒有提到。比如。任務快取佇列及排隊策略、任務拒絕策略、執行緒池的關閉、執行緒池容量的動態調整。感興趣的朋友可以參考下這篇博文,很詳細地闡述了執行緒池的原理。
http://www.importnew.com/19011.html
相關推薦
Spring日誌記錄+執行緒池
本文將介紹在Spring框架下如何利用攔截器做日誌記錄,簡化我們的日誌處理, 1 首先我們需要在Spring-mvc.xml中註冊這個攔截器,程式碼如下: <mvc:interceptors> <mvc:interceptor> <
踩坑 Spring Cloud Hystrix 執行緒池佇列配置
背景: 有一次在生產環境,突然出現了很多筆還款單被掛起,後來排查原因,發現是內部系統呼叫時出現了Hystrix呼叫異常。在開發過程中,因為核心執行緒數設定的比較大,沒有出現這種異常。放到了測試環境,偶爾有出現這種情況,後來在網上查詢解決方案,網上的方案是調整maxQueueSize屬性就好了,當時調整了一下
spring mvc tomcat 執行緒池的坑
1 配置tomcat 執行緒池設定為20個執行緒處理請求 2 後臺框架是springmvc 3 模擬10個請求 4 發現tomcat執行緒池沒一個幹活的 5 幹活的是spring自己建立的執行緒 為什麼springmvc
使用Java佇列來處理日誌資訊(執行緒池的使用)
阿里的規範是使用new ThreadPoolExecutor()來建立執行緒池,二不是使用Excutor的靜態工具類來建立執行緒池,具體可以檢視部落格(兩篇): https://blog.csdn.net/angus_Lucky/article/details/798
2.1 Spring boot/cloud 執行緒池
Step 1:ExecutePool配置,開啟@EnableAsync支援非同步任務 package com.springboot.begin.threadPool; import org.springframework.context.annotation.Bean; import org.
Spring中的執行緒池
前言: Java SE 5.0引入了ThreadPoolExecutor、ScheduledThreadPoolExecutor。Spring 2.x藉助ConcurrentTaskExecutor和ThreadPoolTaskExecutor能夠通過IoC配置形式自定義它
2、使用SPRING中的執行緒池ThreadPoolTaskExecutor實現JAVA併發
new Thread的弊端如下:a. 每次new Thread新建物件效能差。b. 執行緒缺乏統一管理,可能無限制新建執行緒,相互之間競爭,及可能佔用過多系統資源導致宕機或oom。c. 缺乏更多功能,如定時執行、定期執行、執行緒中斷。相比new Thread,Java提供的四種執行緒池的好處在於:a
使用SPRING中的執行緒池ThreadPoolTaskExecutor實現JAVA併發
使用SPRING中的執行緒池ThreadPoolTaskExecutor實現併發。 一:不需要返回值的情況 1,初始化執行緒池 Java程式碼 ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPool
Spring Boot系列二 Spring @Async非同步執行緒池用法總結
Spring非同步執行緒池的介面類,其實質是java.util.concurrent.ExecutorSpring 已經實現的異常執行緒池: 1. SimpleAsyncTaskExecutor:不是真的執行緒池,這個類不重用執行緒,每次呼叫都會建立一個新的執行緒。 2. SyncTaskExecutor:這
Spring+TaskExecutor實現執行緒池管理
目錄 目錄 寫在前面 執行緒池引數 程式碼實現 部落格參考 寫在前面 執行緒池可以很好的幫助我們管理執行緒,它會預先建立若干數量的執行緒,並且不能由開發者直接對執行緒的建立進行控制,這樣,消除了頻繁建立和消亡執行緒的系統資源開銷。
spring中使用執行緒池
bean 配置applicationContext.xml <bean id="threadPoolTaskExecutor" class="org.springframework.sched
Spring--springmvc配執行緒池Executor做多執行緒併發操作
載入xml檔案在ApplicationContext.xml檔案裡面新增[java] view plain copy print?xmlns:task="http://www.springframework.org/schema/task"xmlns:task="http
使用SPRING中的執行緒池ThreadPoolTaskExecutor並且得到任務執行的結果
XML配置 <bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 核心執行
spring框架管理執行緒池
今天來記錄一下,如何通過 spring框架 簡單配置並管理 執行緒池 1、首先需要搭建好一個簡單的spring框架(當然知道是屁話) 如果不知道怎麼搭建spring框架的同學可以看我的上一篇文章,也
在spring中引入執行緒池,設定執行緒優先順序。
在spring.xml 檔案中配置: <!-- 執行緒--> <bean id="taskExecutor" class="org.springframework.sc
Spring中配置執行緒池ThreadPoolExecutor參考
<bean id="executorService" class="java.util.concurrent.ThreadPoolExecutor"> <constructor-
spring+ActiveMQ+JMS+執行緒池實現簡單的分散式,多執行緒,多工的非同步任務處理系統
前言:隨著系統的業務功能不斷增強,傳統的單機、單任務,單執行緒的執行模式已經逐漸的被淘汰,取而代之的是分散式,多工,多執行緒,當然,現在開源的這方面的框架也非常的多,大概的思想也都類似,下面就結合我這一年多的工作心得,分享一個簡單易實現的分散式,多工,多執行緒的非同步任務處理系統的基本實現。 1.系統部署圖
Quartz應用——Spring和Quartz加執行緒池實際應用
公司最近開發需要用到定時任務,引用了微服務的概念,但是開發還是spring相關的專案,沒有用到SpringCloud相關的微服務框架。定時任務我就沒選擇xxx-job,Elastic-Job。選擇了Quartz,簡單方便而且拓展行也高。 Spring是一個很優秀的框架,它無縫的集成了Quartz
1.1 Spring 執行緒池 --- ThreadPoolTaskExecutor
Spring 擅長對元件的封裝和整合, Spring-context對JDK的併發包做了功能增強。 step 1 :Spring-context.xml 中增加如下程式碼 <bean id="poolTaskExecutor" class="org.springframe
Junit單元測試+aop+spring+執行緒池,在進行Junit測試時切面中執行緒池內呼叫的方法不執行
一、問題背景: 寫了一個切面,指向某service包下的所有類及方法,當該service包下方法被呼叫時切面執行,切面中用了執行緒池ExecutorService pool = Executors.newFixedThreadPool(5);執行緒池內呼叫了dao層的方法。 二、問題描述:單