基于Spring AOP的Log收集匯總
前情提要 ??
張三對(duì)于公司的日志處理系統(tǒng)不滿意,認(rèn)為其性能不佳且功能有限。為了展示自己的能力和技術(shù)實(shí)力,他決定利用Spring AOP(面向切面編程)開發(fā)一個(gè)更高效的日志處理系統(tǒng),并將其存儲(chǔ)在Redis中。
首先,張三分析了現(xiàn)有日志處理系統(tǒng)的不足之處,如性能瓶頸、日志格式不統(tǒng)一、存儲(chǔ)容量有限等。然后,他開始著手設(shè)計(jì)和實(shí)現(xiàn)一個(gè)新的日志處理系統(tǒng)。
?? 使用Spring AOP進(jìn)行日志攔截:張三利用Spring AOP的切面功能,為需要記錄日志的方法添加了一個(gè)切面。在這個(gè)切面中,他可以捕獲方法的調(diào)用信息,如方法名、參數(shù)、返回值等,并將這些信息作為日志內(nèi)容。
?? 日志格式化:為了確保日志的一致性和可讀性,張三設(shè)計(jì)了一種統(tǒng)一的日志格式。他將日志分為不同的級(jí)別,如DEBUG、INFO、WARN和ERROR,并為每個(gè)級(jí)別設(shè)置了不同的顏色和標(biāo)簽。
?? Redis存儲(chǔ):張三選擇將日志存儲(chǔ)在Redis中,因?yàn)镽edis是一個(gè)高性能的鍵值存儲(chǔ)系統(tǒng),適合存儲(chǔ)大量的日志數(shù)據(jù)。他為每個(gè)日志級(jí)別創(chuàng)建了一個(gè)Redis列表,用于存儲(chǔ)相應(yīng)級(jí)別的日志。同時(shí),他還設(shè)置了一個(gè)定時(shí)任務(wù),定期清理過期的日志數(shù)據(jù),以保持存儲(chǔ)空間的整潔。
?? 監(jiān)控與告警:為了方便監(jiān)控日志系統(tǒng)的運(yùn)行狀況,張三還開發(fā)了一個(gè)簡單的監(jiān)控界面,可以實(shí)時(shí)查看各個(gè)日志級(jí)別的數(shù)量、存儲(chǔ)空間使用情況等信息。此外,他還設(shè)置了一些告警規(guī)則,當(dāng)某個(gè)日志級(jí)別的數(shù)量超過閾值時(shí),會(huì)自動(dòng)發(fā)送告警通知給相關(guān)人員。
經(jīng)過一段時(shí)間的努力,張三成功地完成了這個(gè)基于Spring AOP的日志處理系統(tǒng),并將其部署到了生產(chǎn)環(huán)境。公司同事對(duì)他的工作表示贊賞,認(rèn)為這個(gè)新的日志處理系統(tǒng)不僅提高了性能,還提供了更多有用的功能。這無疑突顯了張三的技術(shù)能力和對(duì)公司的貢獻(xiàn)。
以下是一個(gè)簡化的代碼實(shí)現(xiàn)示例,展示了如何使用Spring AOP和Redis來實(shí)現(xiàn)日志處理系統(tǒng)。
場景實(shí)現(xiàn) ??
?? 創(chuàng)建一個(gè)日志切面類**LoggingAspect
**
在此過程中,我們創(chuàng)建了一個(gè)日志切面類LoggingAspect,它會(huì)攔截指定包路徑下的所有方法調(diào)用。在方法調(diào)用完成后,它會(huì)將方法的調(diào)用信息(如方法名、參數(shù)、返回值等)作為日志內(nèi)容,并將這些信息傳遞給LogService進(jìn)行處理。
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)建一個(gè)日志服務(wù)類**LogService
****:**
LogService負(fù)責(zé)將日志內(nèi)容存儲(chǔ)到Redis中。在這個(gè)示例中,我們使用了RedisTemplate來操作Redis。我們將日志內(nèi)容存儲(chǔ)在名為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); // 將日志存儲(chǔ)到Redis redisTemplate.opsForList().rightPush("log", logMessage); } }
還可以為不同級(jí)別的日志創(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ù)日志級(jí)別將日志存儲(chǔ)到不同的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ù)日志級(jí)別將日志存儲(chǔ)到不同的Redis列表中 String redisKey = "log:" + logLevel.name().toLowerCase(); redisTemplate.opsForList().rightPush(redisKey, logMessage); } }
?? 配置RedisTemplate:
最后,我們配置了一個(gè)RedisTemplate Bean,用于序列化和反序列化Redis的key和value值。這樣,我們就可以將日志內(nèi)容以結(jié)構(gòu)化的方式存儲(chǔ)在Redis中,并在需要時(shí)方便地進(jìn)行查詢和分析。
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; } }
?? 定時(shí)任務(wù):
我們創(chuàng)建了一個(gè)定時(shí)任務(wù)LogCleanupTask,它會(huì)定期清理過期的日志數(shù)據(jù)。我們使用了Spring的@Scheduled注解來實(shí)現(xiàn)定時(shí)任務(wù),并使用RedisTemplate來操作Redis。在cleanupLogs方法中,我們遍歷所有的日志列表,并根據(jù)日志的時(shí)間戳判斷它們是否過期。如果過期,則將其從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 * * * ?") // 每小時(shí)執(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ù)日志的時(shí)間戳和當(dāng)前時(shí)間進(jìn)行比較 // ... } }
?? 監(jiān)控界面:
我們創(chuàng)建了一個(gè)監(jiān)控界面LogMonitorController,它可以實(shí)時(shí)查看日志數(shù)據(jù)。我們使用了Spring的@RestController注解來創(chuàng)建一個(gè)RESTful API,并使用RedisTemplate來操作Redis。在getLogs方法中,我們遍歷所有的日志列表,并將它們以JSON格式返回給客戶端。客戶端可以使用這些數(shù)據(jù)來實(shí)時(shí)監(jiān)控日志系統(tǒng)的運(yùn)行狀況。
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; } }
??? 請(qǐng)注意!??! 這個(gè)示例僅用于演示如何使用Spring AOP和Redis實(shí)現(xiàn)日志處理系統(tǒng)。在實(shí)際項(xiàng)目中,需要根據(jù)具體需求進(jìn)行更多的定制和優(yōu)化。例如,可以為不同級(jí)別的日志創(chuàng)建不同的Redis列表,以便更好地管理和查詢?nèi)罩緮?shù)據(jù)。此外,還可以考慮使用更高級(jí)的日志框架,如Logback或Log4j2,以實(shí)現(xiàn)更豐富的日志功能和更好的性能。
Get知識(shí)點(diǎn) ??
?? AOP概念:AOP(面向切面編程)是一種編程范式,它允許開發(fā)者在不修改原有代碼的情況下,對(duì)程序的某些方面進(jìn)行增強(qiáng)。AOP通過將橫切關(guān)注點(diǎn)(如日志記錄、事務(wù)管理、權(quán)限控制等)與業(yè)務(wù)邏輯分離,使得代碼更加模塊化和可維護(hù)。
?? Spring AOP:Spring AOP是Spring框架中的一個(gè)重要組件,它提供了聲明式的AOP支持。Spring AOP使用代理模式來實(shí)現(xiàn)AOP,可以通過JDK動(dòng)態(tài)代理或CGLIB代理來創(chuàng)建代理對(duì)象。Spring AOP支持多種類型的切面,如前置通知、后置通知、異常通知、環(huán)繞通知等。
?? @Aspect — 此注釋將類定義為一個(gè)方面,即關(guān)注點(diǎn)的模塊化。該方面包含建議和要點(diǎn)。
?? @Joinpoint — 連接點(diǎn)是程序執(zhí)行中可以應(yīng)用方面的一個(gè)點(diǎn)。在 Spring AOP 中,連接點(diǎn)是方法調(diào)用。
?? @Advice — 建議是某個(gè)方面在特定連接點(diǎn)上采取的行動(dòng)。有幾種類型的建議,例如“之前”、“之后”、“周圍”等。
?? @Pointcut — 切點(diǎn)是一組應(yīng)應(yīng)用方面的連接點(diǎn)。它定義了一個(gè)模式,該模式與方面應(yīng)截獲的方法相匹配??梢允褂帽磉_(dá)式或注釋來定義切點(diǎn)。
下面是 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)控程序運(yùn)行狀態(tài)、排查問題和調(diào)試代碼。但是,日志使用也存在一些缺點(diǎn),如干擾員工工作、信息整理工作量大、主觀色彩和日志格式不統(tǒng)一等。因此,在使用日志時(shí),需要權(quán)衡其優(yōu)缺點(diǎn),選擇合適的日志記錄方法,并確保日志數(shù)據(jù)的準(zhǔn)確性和完整性。
到此這篇關(guān)于基于Spring AOP的Log收集的文章就介紹到這了,更多相關(guān)Spring AOP Log收集內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前后端項(xiàng)目分離解決cors錯(cuò)誤的方法詳解
隨著前后端分離技術(shù)的越來越盛行,跨域問題也逐漸凸顯了出來,下面這篇文章主要給大家介紹了關(guān)于前后端項(xiàng)目分離解決cors錯(cuò)誤的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02關(guān)于SSM框架下各層的解釋說明(Controller等)
這篇文章主要介紹了關(guān)于SSM框架下各層的解釋說明(Controller等),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java I/O深入學(xué)習(xí)之File和RandomAccessFile
這篇文章主要介紹了Java I/O深入學(xué)習(xí)之File和RandomAccessFile, I/O系統(tǒng)即輸入/輸出系統(tǒng),對(duì)于一門程序語言來說,創(chuàng)建一個(gè)好的輸入/輸出系統(tǒng)并非易事。在充分理解Java I/O系統(tǒng)以便正確地運(yùn)用之前,我們需要學(xué)習(xí)相當(dāng)數(shù)量的類。,需要的朋友可以參考下2019-06-06JDK9為何要將String的底層實(shí)現(xiàn)由char[]改成了byte[]
String 類的源碼已經(jīng)由?char[]?優(yōu)化為了?byte[]?來存儲(chǔ)字符串內(nèi)容,為什么要這樣做呢?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2022-03-03feign服務(wù)端發(fā)現(xiàn)異常客戶端處理的方法介紹
這篇文章主要給大家介紹了關(guān)于feign服務(wù)端發(fā)現(xiàn)異??蛻舳颂幚淼姆椒ǎ闹型ㄟ^示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用feign具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07Spring Boot 3.x 集成 Eureka Server/Cl
隨著SpringBoot 3.x版本的開發(fā)嘗試,本文記錄了在集成Eureka Server/Client時(shí)所遇到的問題和解決方案,文中詳細(xì)介紹了搭建服務(wù)、配置文件和測試步驟,感興趣的朋友跟隨小編一起看看吧2024-09-09SpringBoot使用PropertiesLauncher加載外部jar包
這篇文章主要介紹了SpringBoot使用PropertiesLauncher加載外部jar包,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案
在IDEA中能夠正常的預(yù)覽到圖片,但是在生成項(xiàng)目的war包時(shí),項(xiàng)目的目錄結(jié)構(gòu)卻會(huì)發(fā)生變化,所以無法訪問圖片,本文主要介紹了IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案,感興趣的可以了解一下2023-10-10解決Error:(5,55)java:程序包org.springframework.cloud.netflix.eure
這篇文章主要介紹了解決Error:(5,55)java:程序包org.springframework.cloud.netflix.eureka.server不存在問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11