当前位置: 首页 > 后端技术 > Java

如何优雅地记录操作日志?

时间:2023-04-02 09:28:57 Java

1。后台日志几乎存在于所有系统中。我们有log4j、logback等记录开发和调试日志,但是我还没有找到一个简单通用的实现方案让日志显示给用户。所以决定为以后的开发项目提供一个通用的操作日志组件。2、系统日志和操作日志所有的系统都会有日志,但是我们区分系统日志和操作日志系统日志:主要用于开发者调试和排查系统问题,不需要固定的格式和可读性是的,要求简单并且易于理解,反映了用户的操作。通过操作日志可以追溯到某个人在某个时间做了某事,比如:租户操作员时间操作内容租户小明在2022/2/2720:15:00添加了一个新用户:Mr.WangB租客大米2022/2/2810:35:00更新修改订单[xxxxxx]价格为xx元C租客老王2022/2/2822:55:00查询所有交易名为:[xx]3.What需要功能3.1要求:基于SpringBoot,可快速访问,对业务代码侵入性低。3.2解决思路:基于以上两点,我们来思考一下如何实现。spingboot的快速接入需要我们自定义springbootstarter;业务侵入性低,首先想到的是AOP,一般的操作日志都在增删改查的方??法中,所以我们可以在这些方法上使用注解,通过AOP的方法进行拦截。3.3待实现:因此需要实现如下功能:自定义springbootstarter定义日志注解AOP拦截日志注解方法定义日志动态内容模板模板需要实现:动态模板表达式解析:使用强大的SpEL解析表达式自定义function:Supporttargetmethodpre/postcustomfunction3.4show所以我们最终的期望大概是这样的:@EasyLog(module="UserModule",type="New",content="Test{functionName{#userDto.name}}",condition="#userDto.name=='easylog'")publicStringtest(UserDtouserDto){return"test";}4.实现步骤4.1定义日志注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceEasyLog{Stringtenant()default"";Stringoperator()默认"";字符串模块()默认“”;字符串类型()默认"";StringbizNo()默认"";字符串内容();字符串失败()默认“”;Stringdetail()默认"";Stringcondition()default"";}字段含义支持SpEl表达式强制租户tenant,SAAS系统不同类型,如:增、删、改、查、否、bizNo商号,方便查询是否有nt日志模板的内容是fail操作失败时的模板内容吗?详细附加记录信息是否为条件?是否记录条件(默认:truerecord)。4.2自定义函数这里的自定义函数不是指SpELFunction中的自定义函数,因为SpEL中的自定义函数必须是静态方法才可以注册,因为静态方法不方便我们定义方法,所以这里的自定义函数只是指我们定义的一个普通方法publicinterfaceICustomFunction{/***在目标方法执行之前执行自定义函数*@return是否是前置函数*/booleanexecuteBefore();/***自定义函数名*@return自定义函数名*/StringfunctionName();/***自定义函数*@paramparamparameter*@return执行结果*/Stringapply(Stringparam);}我们定义自定义函数接口,交给用户。用户将实现类交给Spring容器管理,我们解析的时候可以从Spring容器中获取。4.3SpEL表达式解析主要涉及以下核心类:ParserExpressionParser,用于将字符串表达式转换为Expression表达式对象。Expression表达式,最后通过它的getValue方法计算表达式的值。上下文EvaluationContext通过结合上下文对象和表达式来计算最终结果。ExpressionParser解析器=newSpelExpressionParser();//创建一个表达式解析器StandardEvaluationContextex=newStandardEvaluationContext();//创建上下文ex.setVariables("name","easylog");//添加自定义参数到上下文Expressionexp=parser.parseExpression("'Welcome!'+#name");//模板解析Stringval=exp.getValue(ex,String.class);//获取值,我们只需要获取日志注解即可动态模板可以被SpEL解析。4.4自定义函数分析我们使用{functionName{param}}的形式在模板中显示自定义函数。在解析整个模板之前,我们先对自定义函数进行分析,将解析出的值替换为模板中的字符串即可。if(template.contains("{")){Matchermatcher=PATTERN.matcher(template);while(matcher.find()){StringfuncName=matcher.group(1);字符串参数=matcher.group(2);如果(customFunctionService.executeBefore(funcName)){Stringapply=customFunctionService.apply(funcName,param);}}}4.5获取operator信息一般情况下,我们会将登录信息保存在applicationcontext中,所以不必在日志注释中指出,我们可以统一设置,定义一个获取operators的接口,可以是由用户实施。publicinterfaceIOperatorService{//获取当前操作符StringgetOperator();//CurrenttenantStringgetTenant();}4.6定义日志内容接收我们要将解析后的日志内容实体信息发送给我们的用户,所以我们需要定义一个日志接收的接口,具体实现交给用户要意识到,无论接收到的日志存储在数据库还是MQ的哪个位置,都由用户自己决定。publicinterfaceILogRecordService{/***保存日志*@parameasyLogInfo日志实体*/voidrecord(EasyLogInfoeasyLogInfo);}4.7定义AOP拦截@Aspect@Component@AllArgsConstructorpublicclassEasyLogAspect{@Pointcut("@annotation(**.EasyLog))")publicvoidpointCut(){}//包围通知@Around("pointCut()&&@annotation(easyLog)")publicObjectaround(ProceedingJoinPointjoinPoint,EasyLogeasyLog)throwsThrowable{//预定义函数解析try{结果=joinPoint.proceed();}catch(Throwablee){}//SpEL解析//Post自定义函数解析返回结果;}}4.8自定义springbootstarter创建一个自动配置类,里面会定义一些对Spring容器管理:@Configuration@ComponentScan("**")publicclassEasyLogAutoConfiguration{@Bean@ConditionalOnMissingBean(ICustomFunction.class)@Role(BeanDefinition.ROLE_APPLICATION)publicICustomFunctioncustomFunction(){返回新的DefaultCustomFunction();}@Bean@ConditionalOnMissingBean(IOperatorService.class)@Role(BeanDefinition.ROLE_APPLICATION)publicIOperatorServiceoperatorGetService(){returnnewDefaultOperatorServiceImpl();}@Bean@ConditionalOnMissingBean(ILogRecordService.class)@Role(BeanDefinition.ROLE_APPLICATIONLogRec)publicrecordService(){returnnewDefaultLogRecordServiceImpl();}}在上一篇文章中,我已经完整介绍了如何自定义springbootstarter。可以参考:如何自定义springbootstarter?5、我们能学到什么?大家可以拉取easy-log的源码进行学习。通过easy-log可以学习到:注解的定义和SpEL表达式的解析使用AOPCustomSpringbootstarterdesignpattern6.源码GitHub:https://github.com/flyhero/easy-logGitee:https://gitee.com/flyhero/easy-log