基于Spring AOP的Log收集匯總
前情提要 ??
張三對于公司的日志處理系統(tǒng)不滿意,認為其性能不佳且功能有限。為了展示自己的能力和技術(shù)實力,他決定利用Spring AOP(面向切面編程)開發(fā)一個更高效的日志處理系統(tǒng),并將其存儲在Redis中。
首先,張三分析了現(xiàn)有日志處理系統(tǒng)的不足之處,如性能瓶頸、日志格式不統(tǒng)一、存儲容量有限等。然后,他開始著手設計和實現(xiàn)一個新的日志處理系統(tǒng)。
?? 使用Spring AOP進行日志攔截:張三利用Spring AOP的切面功能,為需要記錄日志的方法添加了一個切面。在這個切面中,他可以捕獲方法的調(diào)用信息,如方法名、參數(shù)、返回值等,并將這些信息作為日志內(nèi)容。
?? 日志格式化:為了確保日志的一致性和可讀性,張三設計了一種統(tǒng)一的日志格式。他將日志分為不同的級別,如DEBUG、INFO、WARN和ERROR,并為每個級別設置了不同的顏色和標簽。
?? Redis存儲:張三選擇將日志存儲在Redis中,因為Redis是一個高性能的鍵值存儲系統(tǒng),適合存儲大量的日志數(shù)據(jù)。他為每個日志級別創(chuàng)建了一個Redis列表,用于存儲相應級別的日志。同時,他還設置了一個定時任務,定期清理過期的日志數(shù)據(jù),以保持存儲空間的整潔。
?? 監(jiān)控與告警:為了方便監(jiān)控日志系統(tǒng)的運行狀況,張三還開發(fā)了一個簡單的監(jiān)控界面,可以實時查看各個日志級別的數(shù)量、存儲空間使用情況等信息。此外,他還設置了一些告警規(guī)則,當某個日志級別的數(shù)量超過閾值時,會自動發(fā)送告警通知給相關(guān)人員。
經(jīng)過一段時間的努力,張三成功地完成了這個基于Spring AOP的日志處理系統(tǒng),并將其部署到了生產(chǎn)環(huán)境。公司同事對他的工作表示贊賞,認為這個新的日志處理系統(tǒng)不僅提高了性能,還提供了更多有用的功能。這無疑突顯了張三的技術(shù)能力和對公司的貢獻。
以下是一個簡化的代碼實現(xiàn)示例,展示了如何使用Spring AOP和Redis來實現(xiàn)日志處理系統(tǒng)。
場景實現(xiàn) ??
?? 創(chuàng)建一個日志切面類**LoggingAspect
**
在此過程中,我們創(chuàng)建了一個日志切面類LoggingAspect,它會攔截指定包路徑下的所有方法調(diào)用。在方法調(diào)用完成后,它會將方法的調(diào)用信息(如方法名、參數(shù)、返回值等)作為日志內(nèi)容,并將這些信息傳遞給LogService進行處理。
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Autowired private LogService logService; @Pointcut("execution(* com.example.service.*.*(..))") public void logPointcut() {} @AfterReturning(pointcut = "logPointcut()", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { logService.log(joinPoint, result); } }
?? 創(chuàng)建一個日志服務類**LogService
****:**
LogService負責將日志內(nèi)容存儲到Redis中。在這個示例中,我們使用了RedisTemplate來操作Redis。我們將日志內(nèi)容存儲在名為log的Redis列表中。
import org.aspectj.lang.JoinPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service public class LogService { @Autowired private RedisTemplate<String, Object> redisTemplate; public void log(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); String logMessage = String.format("Method: %s, Args: %s, Result: %s", methodName, Arrays.toString(args), result); // 將日志存儲到Redis redisTemplate.opsForList().rightPush("log", logMessage); } }
還可以為不同級別的日志創(chuàng)建不同的Redis列表:
public class LogService { // ... public void log(JoinPoint joinPoint, Object result, LogLevel logLevel) { String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); String logMessage = String.format("Method: %s, Args: %s, Result: %s", methodName, Arrays.toString(args), result); // 根據(jù)日志級別將日志存儲到不同的Redis列表中 String redisKey = "log:" + logLevel.name().toLowerCase(); redisTemplate.opsForList().rightPush(redisKey, logMessage); } }
也可以修改日志格式化:
public class LogService { // ... public void log(JoinPoint joinPoint, Object result, LogLevel logLevel) { String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); String logMessage = String.format("[%s] Method: %s, Args: %s, Result: %s", logLevel, methodName, Arrays.toString(args), result); // 根據(jù)日志級別將日志存儲到不同的Redis列表中 String redisKey = "log:" + logLevel.name().toLowerCase(); redisTemplate.opsForList().rightPush(redisKey, logMessage); } }
?? 配置RedisTemplate:
最后,我們配置了一個RedisTemplate Bean,用于序列化和反序列化Redis的key和value值。這樣,我們就可以將日志內(nèi)容以結(jié)構(gòu)化的方式存儲在Redis中,并在需要時方便地進行查詢和分析。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值 GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); template.setValueSerializer(genericJackson2JsonRedisSerializer); template.setHashValueSerializer(genericJackson2JsonRedisSerializer); // 使用StringRedisSerializer來序列化和反序列化redis的key值 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); template.setHashKeySerializer(stringRedisSerializer); template.afterPropertiesSet(); return template; } }
?? 定時任務:
我們創(chuàng)建了一個定時任務LogCleanupTask,它會定期清理過期的日志數(shù)據(jù)。我們使用了Spring的@Scheduled注解來實現(xiàn)定時任務,并使用RedisTemplate來操作Redis。在cleanupLogs方法中,我們遍歷所有的日志列表,并根據(jù)日志的時間戳判斷它們是否過期。如果過期,則將其從Redis中移除。
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class LogCleanupTask { @Autowired private RedisTemplate<String, Object> redisTemplate; @Scheduled(cron = "0 0 * * * ?") // 每小時執(zhí)行一次 public void cleanupLogs() { // 清理過期的日志數(shù)據(jù),例如保留最近7天的日志 String redisKeyPattern = "log:*"; Set<String> keys = redisTemplate.keys(redisKeyPattern); for (String key : keys) { List<Object> logs = redisTemplate.opsForList().range(key, 0, -1); List<Object> logsToRemove = logs.stream() .filter(log -> isExpired(log)) .collect(Collectors.toList()); redisTemplate.opsForList().remove(key, 0, logsToRemove); } } private boolean isExpired(Object log) { // 判斷日志是否過期,例如根據(jù)日志的時間戳和當前時間進行比較 // ... } }
?? 監(jiān)控界面:
我們創(chuàng)建了一個監(jiān)控界面LogMonitorController,它可以實時查看日志數(shù)據(jù)。我們使用了Spring的@RestController注解來創(chuàng)建一個RESTful API,并使用RedisTemplate來操作Redis。在getLogs方法中,我們遍歷所有的日志列表,并將它們以JSON格式返回給客戶端。客戶端可以使用這些數(shù)據(jù)來實時監(jiān)控日志系統(tǒng)的運行狀況。
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class LogMonitorController { @Autowired private RedisTemplate<String, Object> redisTemplate; @GetMapping("/monitor/logs") public Map<String, Object> getLogs() { Map<String, Object> logs = new HashMap<>(); String redisKeyPattern = "log:*"; Set<String> keys = redisTemplate.keys(redisKeyPattern); for (String key : keys) { List<Object> logList = redisTemplate.opsForList().range(key, 0, -1); logs.put(key, logList); } return logs; } }
??? 請注意?。?! 這個示例僅用于演示如何使用Spring AOP和Redis實現(xiàn)日志處理系統(tǒng)。在實際項目中,需要根據(jù)具體需求進行更多的定制和優(yōu)化。例如,可以為不同級別的日志創(chuàng)建不同的Redis列表,以便更好地管理和查詢?nèi)罩緮?shù)據(jù)。此外,還可以考慮使用更高級的日志框架,如Logback或Log4j2,以實現(xiàn)更豐富的日志功能和更好的性能。
Get知識點 ??
?? AOP概念:AOP(面向切面編程)是一種編程范式,它允許開發(fā)者在不修改原有代碼的情況下,對程序的某些方面進行增強。AOP通過將橫切關(guān)注點(如日志記錄、事務管理、權(quán)限控制等)與業(yè)務邏輯分離,使得代碼更加模塊化和可維護。
?? Spring AOP:Spring AOP是Spring框架中的一個重要組件,它提供了聲明式的AOP支持。Spring AOP使用代理模式來實現(xiàn)AOP,可以通過JDK動態(tài)代理或CGLIB代理來創(chuàng)建代理對象。Spring AOP支持多種類型的切面,如前置通知、后置通知、異常通知、環(huán)繞通知等。
?? @Aspect — 此注釋將類定義為一個方面,即關(guān)注點的模塊化。該方面包含建議和要點。
?? @Joinpoint — 連接點是程序執(zhí)行中可以應用方面的一個點。在 Spring AOP 中,連接點是方法調(diào)用。
?? @Advice — 建議是某個方面在特定連接點上采取的行動。有幾種類型的建議,例如“之前”、“之后”、“周圍”等。
?? @Pointcut — 切點是一組應應用方面的連接點。它定義了一個模式,該模式與方面應截獲的方法相匹配??梢允褂帽磉_式或注釋來定義切點。
下面是 Spring AOP 注解的示例:
@Aspect @Component public class LoggingAspect { @Before("execution(public * com.example.myapp.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("Before " + joinPoint.getSignature().getName() + " method"); } @After("execution(public * com.example.myapp.service.*.*(..))") public void logAfter(JoinPoint joinPoint) { System.out.println("After " + joinPoint.getSignature().getName() + " method"); } @Around("execution(public * com.example.myapp.service.*.*(..))") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Before " + joinPoint.getSignature().getName() + " method"); Object result = joinPoint.proceed(); System.out.println("After " + joinPoint.getSignature().getName() + " method"); return result; } @Pointcut("execution(public * com.example.myapp.service.*.*(..))") public void serviceMethods() {} }
寫在最后 ??
日志使用在現(xiàn)代軟件開發(fā)中非常重要,它可以幫助開發(fā)者和系統(tǒng)管理員監(jiān)控程序運行狀態(tài)、排查問題和調(diào)試代碼。但是,日志使用也存在一些缺點,如干擾員工工作、信息整理工作量大、主觀色彩和日志格式不統(tǒng)一等。因此,在使用日志時,需要權(quán)衡其優(yōu)缺點,選擇合適的日志記錄方法,并確保日志數(shù)據(jù)的準確性和完整性。
到此這篇關(guān)于基于Spring AOP的Log收集的文章就介紹到這了,更多相關(guān)Spring AOP Log收集內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于SSM框架下各層的解釋說明(Controller等)
這篇文章主要介紹了關(guān)于SSM框架下各層的解釋說明(Controller等),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Java I/O深入學習之File和RandomAccessFile
這篇文章主要介紹了Java I/O深入學習之File和RandomAccessFile, I/O系統(tǒng)即輸入/輸出系統(tǒng),對于一門程序語言來說,創(chuàng)建一個好的輸入/輸出系統(tǒng)并非易事。在充分理解Java I/O系統(tǒng)以便正確地運用之前,我們需要學習相當數(shù)量的類。,需要的朋友可以參考下2019-06-06JDK9為何要將String的底層實現(xiàn)由char[]改成了byte[]
String 類的源碼已經(jīng)由?char[]?優(yōu)化為了?byte[]?來存儲字符串內(nèi)容,為什么要這樣做呢?本文就詳細的介紹一下,感興趣的可以了解一下2022-03-03feign服務端發(fā)現(xiàn)異常客戶端處理的方法介紹
這篇文章主要給大家介紹了關(guān)于feign服務端發(fā)現(xiàn)異??蛻舳颂幚淼姆椒?,文中通過示例代碼介紹的非常詳細,對大家學習或者使用feign具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-07-07Spring Boot 3.x 集成 Eureka Server/Cl
隨著SpringBoot 3.x版本的開發(fā)嘗試,本文記錄了在集成Eureka Server/Client時所遇到的問題和解決方案,文中詳細介紹了搭建服務、配置文件和測試步驟,感興趣的朋友跟隨小編一起看看吧2024-09-09SpringBoot使用PropertiesLauncher加載外部jar包
這篇文章主要介紹了SpringBoot使用PropertiesLauncher加載外部jar包,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07解決Error:(5,55)java:程序包org.springframework.cloud.netflix.eure
這篇文章主要介紹了解決Error:(5,55)java:程序包org.springframework.cloud.netflix.eureka.server不存在問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11