自己做微商想做個網(wǎng)站設(shè)計外包網(wǎng)站
前情提要 📌
張三對于公司的日志處理系統(tǒng)不滿意,認為其性能不佳且功能有限。為了展示自己的能力和技術(shù)實力,他決定利用Spring AOP(面向切面編程)開發(fā)一個更高效的日志處理系統(tǒng),并將其存儲在Redis中。
首先,張三分析了現(xiàn)有日志處理系統(tǒng)的不足之處,如性能瓶頸、日志格式不統(tǒng)一、存儲容量有限等。然后,他開始著手設(shè)計和實現(xiàn)一個新的日志處理系統(tǒng)。
📟 使用Spring AOP進行日志攔截:張三利用Spring AOP的切面功能,為需要記錄日志的方法添加了一個切面。在這個切面中,他可以捕獲方法的調(diào)用信息,如方法名、參數(shù)、返回值等,并將這些信息作為日志內(nèi)容。
📟 日志格式化:為了確保日志的一致性和可讀性,張三設(shè)計了一種統(tǒng)一的日志格式。他將日志分為不同的級別,如DEBUG、INFO、WARN和ERROR,并為每個級別設(shè)置了不同的顏色和標(biāo)簽。
📟 Redis存儲:張三選擇將日志存儲在Redis中,因為Redis是一個高性能的鍵值存儲系統(tǒng),適合存儲大量的日志數(shù)據(jù)。他為每個日志級別創(chuàng)建了一個Redis列表,用于存儲相應(yīng)級別的日志。同時,他還設(shè)置了一個定時任務(wù),定期清理過期的日志數(shù)據(jù),以保持存儲空間的整潔。
📟 監(jiān)控與告警:為了方便監(jiān)控日志系統(tǒng)的運行狀況,張三還開發(fā)了一個簡單的監(jiān)控界面,可以實時查看各個日志級別的數(shù)量、存儲空間使用情況等信息。此外,他還設(shè)置了一些告警規(guī)則,當(dāng)某個日志級別的數(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 {@Autowiredprivate 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)建一個日志服務(wù)類**LogService
****:**
LogService負責(zé)將日志內(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 {@Autowiredprivate 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);// 將日志存儲到RedisredisTemplate.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 {@Beanpublic 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;}
}
💵 定時任務(wù):
我們創(chuàng)建了一個定時任務(wù)LogCleanupTask,它會定期清理過期的日志數(shù)據(jù)。我們使用了Spring的@Scheduled注解來實現(xiàn)定時任務(wù),并使用RedisTemplate來操作Redis。在cleanupLogs方法中,我們遍歷所有的日志列表,并根據(jù)日志的時間戳判斷它們是否過期。如果過期,則將其從Redis中移除。
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class LogCleanupTask {@Autowiredprivate 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ù)日志的時間戳和當(dāng)前時間進行比較// ...}
}
💵 監(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 {@Autowiredprivate 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)注點(如日志記錄、事務(wù)管理、權(quán)限控制等)與業(yè)務(wù)邏輯分離,使得代碼更加模塊化和可維護。
📣 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í)行中可以應(yīng)用方面的一個點。在 Spring AOP 中,連接點是方法調(diào)用。
📣 @Advice — 建議是某個方面在特定連接點上采取的行動。有幾種類型的建議,例如“之前”、“之后”、“周圍”等。
📣 @Pointcut — 切點是一組應(yīng)應(yīng)用方面的連接點。它定義了一個模式,該模式與方面應(yīng)截獲的方法相匹配。可以使用表達式或注釋來定義切點。
下面是 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ù)的準(zhǔn)確性和完整性。
?
?