關(guān)于Spring?Cloud實現(xiàn)日志管理模塊
簡介
無論在什么系統(tǒng)中,日志管理模塊都屬于十分重要的部分,接下來會通過注解+AOP+MQ的方式實現(xiàn)一個簡易的日志管理系統(tǒng)
思路
- 注解:標記需要記錄日志的方法
- AOP:通過AOP增強代碼,利用后置/異常通知的方式獲取相關(guān)日志信息,最后使用MQ將日志信息發(fā)送到專門處理日志的系統(tǒng)
- RabbitMQ:利用解耦、異步的特性,協(xié)調(diào)完成各個微服務系統(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 '日志類型', `log_message` varchar(255) DEFAULT NULL COMMENT '日志信息(具體方法名)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COMMENT='系統(tǒng)日志表';
實體類(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;
/**
* 日志類型
*/
private String logType;
/**
* 日志信息(具體方法名)
*/
private String logMessage;
/**
* 創(chuàng)建時間
*/
private Date createTime;
}2、注解
注解(SystemLog):
僅作為標記的作用,目的讓JVM可以識別,然后可以從中獲取相關(guān)信息
@Target:定義注解作用的范圍,這里是方法@Retention:定義注解生命周期,這里是運行時
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
SystemLogEnum type();
}枚舉(SystemLogEnum):
限定日志類型范圍
public enum SystemLogEnum {
SAVE_LOG("保存"),
DELETE_LOG("刪除"),
REGISTER_LOG("注冊"),
LOGIN_LOG("登錄"),
LAUD_LOG("點贊"),
COLLECT_LOG("收藏"),
THROW_LOG("異常"),
;
private String type;
SystemLogEnum(String type) {
this.type = type;
}
public String getType() {
return type;
}
}3、AOP切面
AOP(SysLogAspect):
實現(xiàn)代碼的增強,主要通過動態(tài)代理方式實現(xiàn)的代碼增強。
攔截注解,并獲取攔截到的相關(guān)信息,封裝成日志對象發(fā)送到MQ隊列(生產(chǎn)端)
Component
@Aspect
@Slf4j
public class SysLogAspect {
@Autowired
MqStream stream;
//切點
@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對象
* @param joinPoint
* @return
*/
public SysLog wrapSysLog(JoinPoint joinPoint){
//獲取請求響應對象
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消息隊列
MQ:
這里主要是通過Spring Cloud Stream集成的RabbitMQ
Spring Cloud Stream:
作為MQ的抽象層,已屏蔽各種MQ的各自名詞,統(tǒ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切面的微服務,都屬于MQ生產(chǎn)者服務
引入依賴:
這里沒有版本號的原因是spring cloud已經(jīng)幫我們管理好各個版本號,已無需手動定義版本號
<!--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>在程序入口開啟MQ的Input/Output綁定:
@EnableBinding(MqStream.class)
@SpringBootApplication(scanBasePackages = {"cn.zdxh.user","cn.zdxh.commons"})
@EnableEurekaClient
@MapperScan("cn.zdxh.user.mapper")
@EnableBinding(MqStream.class) //開啟綁定
@EnableFeignClients
public class YouquServiceProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(YouquServiceProviderUserApplication.class, args);
}
}yml配置:
在生產(chǎn)者端設(shè)置output
destination:相當于rabbitmq的exchangegroup:相當于rabbitmq的queue,不過是和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 #自定義名稱
binders:
youqu_rabbit: #自定義名稱
type: rabbit
environment:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: 25802580注:完成以上操作,即完成MQ生產(chǎn)端的所有工作
(3)MQ消費者
引入依賴、開啟Input/Output綁定:均和生產(chǎn)者的設(shè)置一致
yml配置:
在生產(chǎn)者端設(shè)置input
spring:
cloud: # Spring Cloud Stream 相關(guān)配置
stream:
bindings: # exchange與queue綁定
log_input: # 日志消費者設(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消費者監(jiān)聽(LogMqListener):
監(jiān)聽生產(chǎn)者發(fā)過來的日志信息,將信息添加到數(shù)據(jù)庫即可
@Service
@Slf4j
public class LogMqListener {
@Autowired
SysLogService sysLogService;
@StreamListener(MqStream.LOG_INPUT)
public void input(SysLog sysLog) {
log.info("開始記錄日志========================");
sysLogService.save(sysLog);
log.info("結(jié)束記錄日志========================");
}
}注:完成以上操作,即完成MQ消費端的所有工作
5、應用
簡述:
只需將@SystemLog(type = SystemLogEnum.REGISTER_LOG),標記在需要記錄的方法上,當有客戶端訪問該方法時,就可以自動完成日志的記錄

6、總結(jié)
流程:
注解標記--->AOP攔截--->日志發(fā)送到MQ--->專門處理日志的系統(tǒng)監(jiān)聽MQ消息 --->日志插入到數(shù)據(jù)庫
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
openEuler?搭建java開發(fā)環(huán)境的詳細過程
這篇文章主要介紹了openEuler?搭建java開發(fā)環(huán)境,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
java selenium 常見web UI 元素操作及API使用
本文主要介紹java selenium 常見web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下2016-08-08
mybatis中實現(xiàn)枚舉自動轉(zhuǎn)換方法詳解
在使用mybatis的時候經(jīng)常會遇到枚舉類型的轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于mybatis中實現(xiàn)枚舉自動轉(zhuǎn)換的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧。2017-08-08
Java利用Sping框架編寫RPC遠程過程調(diào)用服務的教程
這篇文章主要介紹了Java利用Sping框架編寫RPC遠程過程調(diào)用服務的教程,包括項目管理工具Maven的搭配使用方法,需要的朋友可以參考下2016-06-06
java實現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù)
今天小編就為大家分享一篇java實現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07

