異常:通過Spring aop 記錄日誌重複多次記錄問題
異常現象
最近發現新上線的一個專案中日誌記錄有問題,正常每次呼叫應該只記錄一次日誌的,在日誌記錄表內發現有許多請求都有十來條記錄,並且記錄的更新時間距離寫入時間有十來分鐘的,也有超過一天的。
異常分析
分析原始碼發現,整個記錄日誌的模組通過Spring aop 的方式記錄Service層所有Service實現類中部分方法的請求和響應報文,在記錄日誌的切面類中,定義了切點對應整個service層,前置通知和後置返回通知,用ThreadLocal對應來傳遞前置通知和後置返回通知間傳遞的日誌物件,最後在後置返回通知內把日誌落庫。
發現在前置通知和後置返回通知內,都是先判斷是否是需要記錄日誌的方法名,若是的話就記錄,不是就不做操作;判斷方法名使用的是黑名單排除法,即在名單內的就不記錄日誌,但看過service層中部分實現類中新增了許多方法並且是不需要記錄日誌的,這些方法並沒有加到黑名單內,這樣導致部分方法多記了日誌。
以上不是這次要查詢的原因,因為這樣只會多記部分方法的日誌,並不會重複記錄日誌。再細看原始碼發現,在將從ThreadLocal中取出的日誌物件操作入庫後,並沒有清除該日誌物件,並且在後置返回通知中只判斷了當前執行緒中ThreadLocal中是否有日誌物件導致下一個請求使用該執行緒時,該日誌物件還在,最後引起重複落庫記錄。即:
第一個請求====》前置通知,判斷需要記錄日誌,日誌物件放入ThreadLocal中====>方法a()====》後置返回通知,日誌物件從ThreadLocal中取出,日誌物件存在則記錄日誌,操作入庫;不存在則不操作=====》日誌物件沒有銷燬,繼續在該執行緒中
第二個請求====》前置通知,判斷不需要記錄日誌====>方法b()====》後置返回通知,日誌物件從ThreadLocal中取出,日誌物件存在則記錄日誌,操作入庫;不存在則不操作=====》日誌物件沒有銷燬,繼續在該執行緒中
第一個請求記錄日誌正常,第二個請求中若方法b在黑名單內,則也會記錄一條日誌,這條日誌實際是第一個請求的請求報文和第二個請求中的響應報文,這樣就導致了重複記錄日誌。
解決辦法
解決辦法有多種,以下任選其一
1:自定義註解,在需要記錄日誌的方法上使用該註解
2:在後置返回通知中,每次記錄入庫後,使用ThreadLocal remove方法,清除該日誌物件
3:後置返回通知中不依據從TheadLocal取出日誌物件是否存在做為判斷條件,而使用和前置通知一樣的判斷條件,即判斷方法名是否在黑名單內,在則不記錄,不在則記錄