close

點擊關注公眾號,實用技術文章及時了解


簡介

無論在什麼系統中,日誌管理模塊都屬於十分重要的部分,接下來會通過註解+AOP+MQ的方式實現一個簡易的日誌管理系統

思路

註解: 標記需要記錄日誌的方法

AOP: 通過AOP增強代碼,利用後置/異常通知的方式獲取相關日誌信息,最後使用MQ將日誌信息發送到專門處理日誌的系統

RabbitMQ: 利用解耦、異步的特性,協調完成各個微服務系統之間的通信

1、日誌表結構

表結構(sys_log):

CREATETABLE`sys_log`(`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'唯一ID',`opt_id`int(11)DEFAULTNULLCOMMENT'操作用戶id',`opt_name`varchar(50)DEFAULTNULLCOMMENT'操作用戶名',`log_type`varchar(20)DEFAULTNULLCOMMENT'日誌類型',`log_message`varchar(255)DEFAULTNULLCOMMENT'日誌信息(具體方法名)',`create_time`datetimeDEFAULTNULLCOMMENT'創建時間',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=17DEFAULTCHARSET=utf8COMMENT='系統日誌表';

實體類(SysLog):

@DatapublicclassSysLog{privatestaticfinallongserialVersionUID=1L;/***唯一ID*/@TableId(value="id",type=IdType.AUTO)privateIntegerid;/***操作用戶id*/privateIntegeroptId;/***操作用戶名*/privateStringoptName;/***日誌類型*/privateStringlogType;/***日誌信息(具體方法名)*/privateStringlogMessage;/***創建時間*/privateDatecreateTime;}2、註解註解(SystemLog):

僅作為標記的作用,目的讓JVM可以識別,然後可以從中獲取相關信息

@Target: 定義註解作用的範圍,這裡是方法

@Retention: 定義註解生命周期,這裡是運行時

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSystemLog{SystemLogEnumtype();}枚舉(SystemLogEnum):

限定日誌類型範圍

publicenumSystemLogEnum{SAVE_LOG("保存"),DELETE_LOG("刪除"),REGISTER_LOG("註冊"),LOGIN_LOG("登錄"),LAUD_LOG("點讚"),COLLECT_LOG("收藏"),THROW_LOG("異常"),;privateStringtype;SystemLogEnum(Stringtype){this.type=type;}publicStringgetType(){returntype;}}3、AOP切面AOP(SysLogAspect):

實現代碼的增強,主要通過動態代理方式實現的代碼增強。攔截註解,並獲取攔截到的相關信息,封裝成日誌對象發送到MQ隊列(生產端)

Component@Aspect@Slf4jpublicclassSysLogAspect{@AutowiredMqStreamstream;//切點@Pointcut("@annotation(cn.zdxh.commons.utils.SystemLog)")publicvoidlogPointcut(){}//後置通知@After("logPointcut()")publicvoidafterLog(JoinPointjoinPoint){//一般日誌SysLogsysLog=wrapSysLog(joinPoint);log.info("Log值:"+sysLog);//發送mq消息stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());}//異常通知@AfterThrowing(value="logPointcut()",throwing="e")publicvoidthrowingLog(JoinPointjoinPoint,Exceptione){//異常日誌SysLogsysLog=wrapSysLog(joinPoint);sysLog.setLogType(SystemLogEnum.THROW_LOG.getType());sysLog.setLogMessage(sysLog.getLogMessage()+"==="+e);log.info("異常Log值:"+sysLog);//發送mq消息stream.logOutput().send(MessageBuilder.withPayload(sysLog).build());}/***封裝SysLog對象*@paramjoinPoint*@return*/publicSysLogwrapSysLog(JoinPointjoinPoint){//獲取請求響應對象ServletRequestAttributesattributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequestrequest=attributes.getRequest();MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();SysLogsysLog=newSysLog();//獲取方法全路徑StringmethodName=signature.getDeclaringTypeName()+"."+signature.getName();//獲取註解參數值SystemLogsystemLog=signature.getMethod().getAnnotation(SystemLog.class);//從header取出tokenStringtoken=request.getHeader("token");if(!StringUtils.isEmpty(token)){//操作人信息IntegeruserId=JwtUtils.getUserId(token);Stringusername=JwtUtils.getUsername(token);sysLog.setOptId(userId);sysLog.setOptName(username);}if(!StringUtils.isEmpty(systemLog.type())){sysLog.setLogType(systemLog.type().getType());}sysLog.setLogMessage(methodName);sysLog.setCreateTime(newDate());returnsysLog;}}3、RabbitMQ消息隊列MQ:

這裡主要是通過Spring Cloud Stream集成的RabbitMQ

Spring Cloud Stream:

作為MQ的抽象層,已屏蔽各種MQ的各自名詞,統稱為input、output兩大塊。可以更方便靈活地切換各種MQ,如 kafka、RocketMQ等

(1)定義Input/Ouput接口(MqStream)@ComponentpublicinterfaceMqStream{StringLOG_INPUT="log_input";StringLOG_OUTPUT="log_output";@Input(LOG_INPUT)SubscribableChannellogInput();@Output(LOG_OUTPUT)MessageChannellogOutput();}(2)MQ生產者

註:這裡使用到AOP切面的微服務,都屬於MQ生產者服務

引入依賴:

這裡沒有版本號的原因是spring cloud已經幫我們管理好各個版本號,已無需手動定義版本號

<!--SpringCloudStream--><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>

在程序入口開啟MQ的Input/Output綁定:

@SpringBootApplication(scanBasePackages={"cn.zdxh.user","cn.zdxh.commons"})@EnableEurekaClient@MapperScan("cn.zdxh.user.mapper")@EnableBinding(MqStream.class)//開啟綁定@EnableFeignClientspublicclassYouquServiceProviderUserApplication{publicstaticvoidmain(String[]args){SpringApplication.run(YouquServiceProviderUserApplication.class,args);}}

yml配置:

在生產者端設置output

destination: 相當於rabbitmq的exchange
group: 相當於rabbitmq的queue,不過是和destination一起組合成的queue名
binder: 需要綁定的MQ
#SpringCloudStream相關配置spring:cloud:stream:bindings:#exchange與queue綁定log_output:#日誌生產者設置outputdestination:log.exchangecontent-type:application/jsongroup:log.queuebinder:youqu_rabbit#自定義名稱binders:youqu_rabbit:#自定義名稱type:rabbitenvironment:spring:rabbitmq:host:localhostport:5672username:guestpassword:25802580

註:完成以上操作,即完成MQ生產端的所有工作

(3)MQ消費者

引入依賴、開啟Input/Output綁定:均和生產者的設置一致

yml配置:

在生產者端設置input

spring:cloud:#SpringCloudStream相關配置stream:bindings:#exchange與queue綁定log_input:#日誌消費者設置inputdestination:log.exchangecontent-type:application/jsongroup:log.queuebinder:youqu_rabbitbinders:youqu_rabbit:type:rabbitenvironment:spring:rabbitmq:host:localhostport:5672username:guestpassword:25802580

消費者監聽(LogMqListener):

監聽生產者發過來的日誌信息,將信息添加到數據庫即可

@Service@Slf4jpublicclassLogMqListener{@AutowiredSysLogServicesysLogService;@StreamListener(MqStream.LOG_INPUT)publicvoidinput(SysLogsysLog){log.info("開始記錄日誌========================");sysLogService.save(sysLog);log.info("結束記錄日誌========================");}}

註:完成以上操作,即完成MQ消費端的所有工作

4、應用

簡述:

只需將@SystemLog(type = SystemLogEnum.REGISTER_LOG),標記在需要記錄的方法上,當有客戶端訪問該方法時,就可以自動完成日誌的記錄

5、總結

流程:

註解標記--->AOP攔截--->日誌發送到MQ--->專門處理日誌的系統監聽MQ消息 --->日誌插入到數據庫

來源:blog.csdn.net/weixin_38802061/article/details/105458047

推薦

Java面試題寶典

技術內卷群,一起來學習!!

PS:因為公眾號平台更改了推送規則,如果不想錯過內容,記得讀完點一下「在看」,加個「星標」,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點「在看」支持我們吧!

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()