關(guān)于Spring?Cloud實(shí)現(xiàn)日志管理模塊
簡(jiǎn)介
無(wú)論在什么系統(tǒng)中,日志管理模塊都屬于十分重要的部分,接下來(lái)會(huì)通過(guò)注解+AOP+MQ的方式實(shí)現(xiàn)一個(gè)簡(jiǎn)易的日志管理系統(tǒng)
思路
- 注解:標(biāo)記需要記錄日志的方法
- AOP:通過(guò)AOP增強(qiáng)代碼,利用后置/異常通知的方式獲取相關(guān)日志信息,最后使用MQ將日志信息發(fā)送到專(zhuān)門(mén)處理日志的系統(tǒng)
- RabbitMQ:利用解耦、異步的特性,協(xié)調(diào)完成各個(gè)微服務(wù)系統(tǒng)之間的通信
1、日志表結(jié)構(gòu)
表結(jié)構(gòu)(sys_log):
CREATE TABLE `sys_log` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '唯一ID', `opt_id` int(11) DEFAULT NULL COMMENT '操作用戶id', `opt_name` varchar(50) DEFAULT NULL COMMENT '操作用戶名', `log_type` varchar(20) DEFAULT NULL COMMENT '日志類(lèi)型', `log_message` varchar(255) DEFAULT NULL COMMENT '日志信息(具體方法名)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COMMENT='系統(tǒng)日志表';
實(shí)體類(lèi)(SysLog):
@Data public class SysLog { private static final long serialVersionUID = 1L; /** * 唯一ID */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 操作用戶id */ private Integer optId; /** * 操作用戶名 */ private String optName; /** * 日志類(lèi)型 */ private String logType; /** * 日志信息(具體方法名) */ private String logMessage; /** * 創(chuàng)建時(shí)間 */ private Date createTime; }
2、注解
注解(SystemLog):
僅作為標(biāo)記的作用,目的讓JVM可以識(shí)別,然后可以從中獲取相關(guān)信息
@Target
:定義注解作用的范圍,這里是方法@Retention
:定義注解生命周期,這里是運(yùn)行時(shí)
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SystemLog { SystemLogEnum type(); }
枚舉(SystemLogEnum):
限定日志類(lèi)型范圍
public enum SystemLogEnum { SAVE_LOG("保存"), DELETE_LOG("刪除"), REGISTER_LOG("注冊(cè)"), LOGIN_LOG("登錄"), LAUD_LOG("點(diǎn)贊"), COLLECT_LOG("收藏"), THROW_LOG("異常"), ; private String type; SystemLogEnum(String type) { this.type = type; } public String getType() { return type; } }
3、AOP切面
AOP(SysLogAspect):
實(shí)現(xiàn)代碼的增強(qiáng),主要通過(guò)動(dòng)態(tài)代理方式實(shí)現(xiàn)的代碼增強(qiáng)。
攔截注解,并獲取攔截到的相關(guān)信息,封裝成日志對(duì)象發(fā)送到MQ隊(duì)列(生產(chǎn)端)
Component @Aspect @Slf4j public class SysLogAspect { @Autowired MqStream stream; //切點(diǎn) @Pointcut("@annotation(cn.zdxh.commons.utils.SystemLog)") public void logPointcut(){} //后置通知 @After("logPointcut()") public void afterLog(JoinPoint joinPoint) { //一般日志 SysLog sysLog = wrapSysLog(joinPoint); log.info("Log值:"+sysLog); //發(fā)送mq消息 stream.logOutput().send(MessageBuilder.withPayload(sysLog).build()); } //異常通知 @AfterThrowing(value = "logPointcut()", throwing = "e") public void throwingLog(JoinPoint joinPoint, Exception e) { //異常日志 SysLog sysLog = wrapSysLog(joinPoint); sysLog.setLogType(SystemLogEnum.THROW_LOG.getType()); sysLog.setLogMessage(sysLog.getLogMessage()+"==="+e); log.info("異常Log值:"+sysLog); //發(fā)送mq消息 stream.logOutput().send(MessageBuilder.withPayload(sysLog).build()); } /** * 封裝SysLog對(duì)象 * @param joinPoint * @return */ public SysLog wrapSysLog(JoinPoint joinPoint){ //獲取請(qǐng)求響應(yīng)對(duì)象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); MethodSignature signature = (MethodSignature)joinPoint.getSignature(); SysLog sysLog = new SysLog(); //獲取方法全路徑 String methodName = signature.getDeclaringTypeName()+"."+signature.getName(); //獲取注解參數(shù)值 SystemLog systemLog = signature.getMethod().getAnnotation(SystemLog.class); //從header取出token String token = request.getHeader("token"); if (!StringUtils.isEmpty(token)) { //操作人信息 Integer userId = JwtUtils.getUserId(token); String username = JwtUtils.getUsername(token); sysLog.setOptId(userId); sysLog.setOptName(username); } if (!StringUtils.isEmpty(systemLog.type())){ sysLog.setLogType(systemLog.type().getType()); } sysLog.setLogMessage(methodName); sysLog.setCreateTime(new Date()); return sysLog; } }
4、RabbitMQ消息隊(duì)列
MQ:
這里主要是通過(guò)Spring Cloud Stream集成的RabbitMQ
Spring Cloud Stream:
作為MQ的抽象層,已屏蔽各種MQ的各自名詞,統(tǒng)稱(chēng)為input、output兩大塊??梢愿奖沆`活地切換各種MQ,如 kafka、RocketMQ等
(1)定義Input/Ouput接口(MqStream)
@Component public interface MqStream { String LOG_INPUT = "log_input"; String LOG_OUTPUT = "log_output"; @Input(LOG_INPUT) SubscribableChannel logInput(); @Output(LOG_OUTPUT) MessageChannel logOutput(); }
(2)MQ生產(chǎn)者
注:這里使用到AOP切面的微服務(wù),都屬于MQ生產(chǎn)者服務(wù)
引入依賴(lài):
這里沒(méi)有版本號(hào)的原因是spring cloud已經(jīng)幫我們管理好各個(gè)版本號(hào),已無(wú)需手動(dòng)定義版本號(hào)
<!--Spring Cloud Stream--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency>
在程序入口開(kāi)啟MQ的Input/Output綁定:
@EnableBinding(MqStream.class)
@SpringBootApplication(scanBasePackages = {"cn.zdxh.user","cn.zdxh.commons"}) @EnableEurekaClient @MapperScan("cn.zdxh.user.mapper") @EnableBinding(MqStream.class) //開(kāi)啟綁定 @EnableFeignClients public class YouquServiceProviderUserApplication { public static void main(String[] args) { SpringApplication.run(YouquServiceProviderUserApplication.class, args); } }
yml配置:
在生產(chǎn)者端設(shè)置output
destination
:相當(dāng)于rabbitmq的exchangegroup
:相當(dāng)于rabbitmq的queue,不過(guò)是和destination一起組合成的queue名binder
:需要綁定的MQ
#Spring Cloud Stream相關(guān)配置 spring: cloud: stream: bindings: # exchange與queue綁定 log_output: # 日志生產(chǎn)者設(shè)置output destination: log.exchange content-type: application/json group: log.queue binder: youqu_rabbit #自定義名稱(chēng) binders: youqu_rabbit: #自定義名稱(chēng) type: rabbit environment: spring: rabbitmq: host: localhost port: 5672 username: guest password: 25802580
注:完成以上操作,即完成MQ生產(chǎn)端的所有工作
(3)MQ消費(fèi)者
引入依賴(lài)、開(kāi)啟Input/Output綁定:均和生產(chǎn)者的設(shè)置一致
yml配置:
在生產(chǎn)者端設(shè)置input
spring: cloud: # Spring Cloud Stream 相關(guān)配置 stream: bindings: # exchange與queue綁定 log_input: # 日志消費(fèi)者設(shè)置input destination: log.exchange content-type: application/json group: log.queue binder: youqu_rabbit binders: youqu_rabbit: type: rabbit environment: spring: rabbitmq: host: localhost port: 5672 username: guest password: 25802580
消費(fèi)者監(jiān)聽(tīng)(LogMqListener):
監(jiān)聽(tīng)生產(chǎn)者發(fā)過(guò)來(lái)的日志信息,將信息添加到數(shù)據(jù)庫(kù)即可
@Service @Slf4j public class LogMqListener { @Autowired SysLogService sysLogService; @StreamListener(MqStream.LOG_INPUT) public void input(SysLog sysLog) { log.info("開(kāi)始記錄日志========================"); sysLogService.save(sysLog); log.info("結(jié)束記錄日志========================"); } }
注:完成以上操作,即完成MQ消費(fèi)端的所有工作
5、應(yīng)用
簡(jiǎn)述:
只需將@SystemLog(type = SystemLogEnum.REGISTER_LOG),標(biāo)記在需要記錄的方法上,當(dāng)有客戶端訪問(wèn)該方法時(shí),就可以自動(dòng)完成日志的記錄
6、總結(jié)
流程:
注解標(biāo)記--->AOP攔截--->日志發(fā)送到MQ--->專(zhuān)門(mén)處理日志的系統(tǒng)監(jiān)聽(tīng)MQ消息 --->日志插入到數(shù)據(jù)庫(kù)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
openEuler?搭建java開(kāi)發(fā)環(huán)境的詳細(xì)過(guò)程
這篇文章主要介紹了openEuler?搭建java開(kāi)發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06JavaSE程序邏輯控制實(shí)現(xiàn)詳細(xì)圖文教程
JavaSE是為了開(kāi)發(fā)桌面應(yīng)用程序和控制臺(tái)應(yīng)用程序而設(shè)計(jì)的,使用JavaSE可以編寫(xiě)?yīng)毩⑦\(yùn)行的Java應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于JavaSE程序邏輯控制實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-04-04Spring整合Mybatis具體代碼實(shí)現(xiàn)流程
這篇文章主要介紹了Spring整合Mybatis實(shí)操分享,文章首先通過(guò)介紹Mybatis的工作原理展開(kāi)Spring整合Mybatis的詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-05-05java selenium 常見(jiàn)web UI 元素操作及API使用
本文主要介紹java selenium 常見(jiàn)web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下2016-08-08mybatis中實(shí)現(xiàn)枚舉自動(dòng)轉(zhuǎn)換方法詳解
在使用mybatis的時(shí)候經(jīng)常會(huì)遇到枚舉類(lèi)型的轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于mybatis中實(shí)現(xiàn)枚舉自動(dòng)轉(zhuǎn)換的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-08-08Java利用Sping框架編寫(xiě)RPC遠(yuǎn)程過(guò)程調(diào)用服務(wù)的教程
這篇文章主要介紹了Java利用Sping框架編寫(xiě)RPC遠(yuǎn)程過(guò)程調(diào)用服務(wù)的教程,包括項(xiàng)目管理工具M(jìn)aven的搭配使用方法,需要的朋友可以參考下2016-06-06java實(shí)現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù)
今天小編就為大家分享一篇java實(shí)現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07