首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Grace:优雅高效的的记录业务操作日志

Grace:优雅高效的的记录业务操作日志

作者头像
恒宇少年
发布于 2022-01-20 04:33:00
发布于 2022-01-20 04:33:00
68700
代码可运行
举报
运行总次数:0
代码可运行

Grace[ɡreɪs]是一款业务操作日志记录框架,让我们使用更优雅方式来记录有效的、可读性高的操作日志。

项目地址

快速集成

Grace提供了grace-bom依赖,定义了全部依赖的统一版本。

Maven项目在pom.xml文件内添加依赖如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependencies>
        <dependency>
            <groupId>org.minbox.framework</groupId>
            <artifactId>grace-expression</artifactId>
        </dependency>
        <dependency>
            <groupId>org.minbox.framework</groupId>
            <artifactId>grace-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.minbox.framework</groupId>
            <artifactId>grace-processor</artifactId>
        </dependency>
    </dependencies>
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.minbox.framework</groupId>
                <artifactId>grace-bom</artifactId>
                <version>{lastVersion}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

{lastVersion}为最新的版本号,可到Maven中央仓库查看最新版本 https://search.maven.org/search?q=grace-bom

记录日志

记录日志主要是依靠@GraceRecorder注解来配置,该注解只能在方法上使用。

@GraceRecorder注解的属性定义如下所示:

  • success:目标方法成功执行后所使用的日志模板,支持使用SpEL表达式方式配置
  • fail:目标方法执行失败后所使用的文本
  • condition:判定是否执行记录操作日志,支持使用SpEL表达式方式配置
  • bizNo:业务编号,支持使用SpEL表达式方式配置
  • operator:操作日志所关联的操作人,支持使用SpEL表达式方式配置
  • category:日志分组,可用于对操作日志进行归类

注意:SpEL表达式使用模板定义前后缀的方式,只有在{}内的字符串才会被解析。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
public class TestController {
    @Autowired
    private TestService testService;

    @GetMapping
    @GraceRecorder(success = "用户:{#name},编号:{#userId} 访问了首页.", category = "index")
    public String index(String name, String userId) {
        name = testService.getUserName(userId);
        testService.getUserList(userId);
        return "Hello, " + name;
    }
}

保存日志

操作日志根据AOP切面解析完成后会调用org.minbox.framework.grace.processor.GraceLogStorageProcessor#storage方法进行后续的数据存储处理,

需要实现GraceLogStorageProcessor接口来自定义进行日志的存储。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
@Slf4j
public class GraceLogStorageProcessorService implements GraceLogStorageProcessor {
    @Override
    public void storage(GraceLogObject graceLogObject) {
        log.info("位置:{},日志内容:{}", graceLogObject.getGeneratedLocation(), graceLogObject.getContent());
    }
}

配置全局操作人

如果项目中使用了认证框架,比如:SpringSecurityOAuth2,一般会线程安全的存储登录人的相关信息,如果我们再在@GraceRecorder注解内重复配置operator就显得太过于繁琐。

针对这种情况Grace提供了全局配置操作人的接口GraceLoadOperatorService,我们只需要实现该接口即可,优先级要低于@GraceRecorder#operator

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class GlobalOperatorService implements GraceLoadOperatorService {
    @Override
    public String getOperatorName() {
        return "恒宇少年";
    }

    @Override
    public String getOperatorId() {
        return "123";
    }

    @Override
    public Map<String, Object> getExtra() {
        Map<String, Object> map = new HashMap<>();
        map.put("age", 11);
        return map;
    }
}

getExtra方法的返回值会写入到表达式解析上下文变量集合内,可以用于SpEL表达式的解析变量。

使用参数

方法参数是格式化SpEL表达式数据的重要来源,可以使用方法的全部参数作为格式化日志的变量。

基本类型(byte/short/int/long/String)的使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceRecorder(category = "User", success = "用户:{#userId} 密码更新完成,更新后的密码:{#newPassword}.")
public void changePwd(String userId, String newPassword) {
  // ...
}

封装类型使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceRecorder(category = "User", success = "用户:{#request.userId} 密码更新完成,更新后的密码:{#request.newPassword}.")
public void changePwd(ChangeUserPwdRequest request){
  // ...
}

Map类型使用:

请求参数:http://127.0.0.1:8080/changePassword?userId=123456&newPassword=111111

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping("/changePassword")
@GraceRecorder(category = "Test", success = "修改用户:{#map.get('userId')}的密码,改后为:{#map.get('newPassword')}")
public String useMap(@RequestParam HashMap<String, Object> map) {
  return "The password changed.";
}

注意事项:JDK1.8及以前的版本反射时无法获取源码参数的名称,可以通过#p?的格式化来获取参数对应值,其中?为参数的索引,从0开始。 如:#p0.get('userId')#p0.userId

自定义变量

如果格式化日志所需要的变量不是参数也不是返回值,这时我们需要自定义变量并加入到格式化日志的变量集合内,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceRecorder(category = "User", success = "用户:{#request.userId} 密码由{#oldPassword}改为{#request.newPassword}")
public void changePassword(ChangeUserPwdRequest request) {
  GraceVariableContext.setVariable("oldPassword", "admin123");
  // ...
}

GraceVariableContext内是一个多线程副本的HashMap集合,如果相同Key的变量设置多次会被覆盖使用最后一次设置的值。

使用Bean定义的函数

SpEL表达式支持通过@bean的方式来访问IOC容器内注册的Bean实例,也可以直接访问Bean定义的方法,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class UserService {
  public String getUserRealName(String userId) {
    return "恒宇少年";
  }
}
-------
  @GetMapping("/changePassword")
  @GraceRecorder(category = "Test", success = "修改用户:{@userService.getUserRealName(#map.get('userId'))}的密码,改后为:{#map.get('newPassword')}")
  public String useMap(@RequestParam HashMap<String, Object> map) {
  return "The password changed.";
}

格式:@ + Bean名称,如果没有特殊处理使用注解注册到IOC容器内的Bean名称首字母都为小写,所以@userService就代表了UserService类的Bean实例。

表达式函数

表达式函数必须是static修饰的方法才可以定义,如果不是static在访问时会报错,主要是因为反射调用方法时如果不是静态的需要方法所属类的实例才可以。

配置ExpressionFunctionFactory类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean
ExpressionFunctionFactory expressionFunctionFactory() {
  // 可以传递多个basePackage
  return new ExpressionFunctionFactory(Arrays.asList("org.minbox.framework.grace.sample","com.yuqiyu"));
}

通过配置实例化ExpressionFunctionFactory类来加载指定包名下配置@GraceFunctionDefiner注解的类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceFunctionDefiner
public class StringUtils {
    @GraceFunction
    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}

@GraceFunctionDefiner注解只是起到了一个过滤表达式函数定义的作用,只要使用该注解的类才可以执行进一步解析表达式函数的逻辑。

@GraceFunction注解则是标识方法为表达式函数,ExpressionFunctionFactory在实例化后会把表达式函数缓存到内存集合中,在解析操作日志的SpEL表达式时进行注册使用。

使用返回值

每次执行@GraceRecorder配置的方法时,AOP拦截器都会在目标方法执行完成后将结果添加到上下文的变量集合内,使用result作为Key,如果我们需要使用返回值的内容来格式化日志可以直接使用#result来访问数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceRecorder(category = "User", success = "用户:{#userId} 查询到的昵称为:{#result}")
public String getUserRealName(String userId) {
  return "恒宇少年";
}

@GraceRecorder(category = "User", success = "用户:{#userId},年龄:{#result.age}")
public User getUserById(String userId) {
  return new User();
}

@Data
public static class User {
  private String userId;
  private String userName;
  private int age;
}

条件判断

@GraceRecorder注解有个condition条件属性,支持SpEL表达式配置,如果配置了该属性,只有表达式解析结果为true时才会记录操作日志。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GraceRecorder(category = "User", condition = "{#age>20 and #age<60}", success = "用户:{@userService.getUserRealName(#userId)},年龄超过{#age}")
public void updateAgeById(String userId, int age) {
  System.out.println(age);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021.12.30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【详解】Spring-Cachekey设置注意事项
在现代的Web应用中,缓存是提高系统性能和响应速度的重要手段之一。Spring框架提供了强大的缓存支持,通过​​@Cacheable​​、​​@CachePut​​、​​@CacheEvict​​等注解可以方便地实现缓存功能。然而,正确设置缓存键(Cache Key)对于确保缓存的有效性和准确性至关重要。本文将探讨在使用Spring Cache时设置缓存键的一些注意事项。
大盘鸡拌面
2025/01/02
1720
一个注解,搞定 SpringBoot 操作日志
tenant是代表租户的标识,一般一个服务或者一个业务下的多个服务都写死一个 tenant 就可以
业余草
2021/03/04
1K0
一个注解,搞定 SpringBoot 操作日志
SpringBoot 操作日志
tenant 是代表租户的标识,一般一个服务或者一个业务下的多个服务都写死一个 tenant 就可以
南风
2021/08/24
1.1K0
SpringBoot 操作日志
使用ApiBoot Logging进行统一管理请求日志
ApiBoot Logging通过集成minbox-logging来进行管理每一次请求的日志信息,包含头信息、参数、主体内容、路径、发生的服务器相关信息等,根据接口的响应状态还可以记录响应的头信息、响应的内容以及发生异常时的堆栈信息。
恒宇少年
2019/10/18
6800
使用ApiBoot Logging进行统一管理请求日志
log-record正式版本发布:自定义函数、手动传递上下文 、本地监听支持
之前写了两篇文章,来介绍我的log-record开源项目(优雅记录操作日志)是如何诞生的。
Rude3Knife的公众号
2022/01/20
1.3K0
log-record正式版本发布:自定义函数、手动传递上下文 、本地监听支持
Spring cloud 之多种方式限流(实战)
在频繁的网络请求时,服务有时候也会受到很大的压力,尤其是那种网络攻击,非法的。这样的情形有时候需要作一些限制。例如:限制对方的请求,这种限制可以有几个依据:请求IP、用户唯一标识、请求的接口地址等等。
程序猿Damon
2020/05/25
3.4K0
Spring cache 使用Redis做分布式缓存
DemoApplication启动类头部加入@EnableCaching开启缓存 redis.config配置
阿提说说
2022/12/02
6060
分布式调度框架Quartz衍生出的三种任务类型,你用过几个?
Quartz内部没有明确的任务类型的概念,在ApiBoot中对其进行封装后才确切的定义了这个概念,可以根据业务场景按需选择适合的任务类型来构建执行的任务。
恒宇少年
2019/12/26
4220
spring redis实现注解缓存
使用缓存的方式很多,有基于工具类手动操作的,也有基于注解的,各有千秋,接下来将借助spring+redis实现基于注解的缓存使用.
叔牙
2020/11/19
8100
spring redis实现注解缓存
SpringBoot整合SpringCache的简单使用和介绍
SpringBoot整合SpringCache做缓存操作,以下操作基于SpringBoot 2.4.5版本
是小张啊喂
2021/05/13
6170
springBoot高级
1、在springBoot中可以使用注解式开发缓存,默认没有开启缓存中间件,那么使用的就是存储在Map中的原理,但是我们还可以配置自己的缓存中间件,比如redis
爱撒谎的男孩
2019/12/31
6700
SpringSecurity
认证 是为用户建立一个他所声明的主体。主体一般是指用户,设备或可以在你系 统中执行动作的其他系统。 登录过程
JokerDJ
2023/11/27
2090
SpringSecurity
Spring-SpEL表达式[通俗易懂]
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说Spring-SpEL表达式[通俗易懂],希望能够帮助大家进步!!!
Java架构师必看
2022/08/26
1K0
ApiBoot Logging使用RestTemplate透传链路信息
在上一篇文章【ApiBoot Logging使用SpringCloud Openfeign透传链路信息】中我们详细的讲解了ApiBoot Logging整合SpringCloud通过Openfeign进行透传链路信息,包括traceId(链路编号)、parentSpanId(上级单元编号)等信息。 ApiBoot Logging不仅仅可以使用Openfeign传递链路信息,还支持RestTemplate方式,本篇文章来详细的讲解下具体的使用方式。
恒宇少年
2019/11/11
5790
如何优雅地记录操作日志?
操作日志几乎存在于每个系统中,而这些系统都有记录操作日志的一套 API。操作日志和系统日志不一样,操作日志必须要做到简单易懂。所以如何让操作日志不跟业务逻辑耦合,如何让操作日志的内容易于理解,如何让操作日志的接入更加简单?上面这些都是本文要回答的问题。我们主要围绕着如何“优雅”地记录操作日志展开描述,希望对从事相关工作的同学能够有所帮助或者启发。
美团技术团队
2021/10/12
2.4K2
如何优雅地记录操作日志?
修改ApiBoot Logging日志采集的路径前缀
ApiBoot Logging支持指定单个或者多个路径的前缀进行采集,也就是我们可以指定/user/**或者/order/**下的单个或者同时指定多个路径进行采集请求日志,其他不符合Ant表达式的路径就会被忽略掉。
恒宇少年
2019/10/29
7520
SpringCache与redis集成,优雅的缓存解决方案
缓存可以说是加速服务响应速度的一种非常有效并且简单的方式。在缓存领域,有很多知名的框架,如EhCache 、Guava、HazelCast等。
java思维导图
2020/08/24
9160
SpringCache与redis集成,优雅的缓存解决方案
Spring Boot2 学习二 应用使用:
SpringBoot 对一些配置都是有默认值的但也可以直接通过 application.properties或 application.yml 直接修改 application.yml
Java_慈祥
2024/08/06
2160
Spring Boot2 学习二 应用使用:
Spring学习总结(四)——表达式语言 Spring Expression Language
Spring表达式语言(简称SpEL)是一个支持查询并在运行时操纵一个对象图的功能强大的表达式语言。SpEL语言的语法类似于统一EL,但提供了更多的功能,最主要的是显式方法调用和基本字符串模板函数。
张果
2022/05/09
1K0
Spring学习总结(四)——表达式语言 Spring Expression Language
使用Swagger2作为文档来描述你的接口信息
接口文档在前后分离的项目中是必不可少的一部分,文档的编写一直以来都是一件头疼的事情,写程序不写注释、不写文档这几乎是程序员的通病,Swagger2的产生给广大的程序员们带来了曙光,只需要在接口类或者接口的方法上添加注解配置,就可以实现文档效果,除了可以应用到单体应用,在微服务架构中也是可以使用的,只需要整合zuul就可以实现各个服务的文档整合。
恒宇少年
2019/12/20
5420
使用Swagger2作为文档来描述你的接口信息
推荐阅读
相关推荐
【详解】Spring-Cachekey设置注意事项
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档