在平时使用到一些软件中,比如某宝或者某书,通过记录用户的行为来构建和分析用户的行为数据,同时也能更好优化产品设计和提升用户体验。比如在一个订单系统中,需要确定追踪用户的行为,比如:

  • 登录/登出
  • 浏览商品
  • 加购商品
  • 搜索商品关键字
  • 下单

上述行为就需要使用到日志系统来存储或者记录数据,Java 有几种日志方案,简单介绍几种实现方案,以及需要注意的点。

日志添加需要注意问题

根据业务的不同,需要使用匹配到合适的日志方案。也需要注意几个问题:

  • 不能影响原来的业务逻辑。
  • 不能报错,即使报错,不能影响原有的业务代码。
  • 对于耗时的日志代码,使用异步方法
  • 侵入性低,尽量少改动原始代码

Spring AOP

Spring AOP 通过切面编程实现不修改原有代码,而动态添加功能的能力。这种方式有以下几个好处:

  • 侵入性低
  • 可重用性强

本文使用基于注解的 AOP,首先定义一个切面注解 AopTest:

/**
* 切面注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopTest {
}

再创建通知 @Around :

@Around("@annotation(com.test.annotation.AopTest)")
public Object annotationTest(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("执行前");
Object result = joinPoint.proceed(); // 执行目标方法
Object[] args = joinPoint.getArgs();
log.info("执行后");
return result;
}

一般都会在执行后添加日志即可,想要在那个方法或者接口加日志,只需要在方法上添加注解即可,比如在接口添加注解:

@GetMapping
@AopTest
public String first(String param) {
log.info("执行first方法");
return "result " + param;
}

请求接口后,就有如下的输出:

执行前
执行first方法
执行后

但是切面有一个问题,执行切面报错,方法也无法执行,就需要捕获异常,保证业务代码正常执行,改造一下上面的通知:

@Around("@annotation(com.test.annotation.AopTest)")
public Object annotationTest(ProceedingJoinPoint joinPoint) throws Throwable {log.info("执行前");
log.info("执行前");
Object result = joinPoint.proceed(); // 执行目标方法
try {
Object[] args = joinPoint.getArgs();
// 添加日志
log.info("执行后");
int a = 1/0;
} catch (Exception e) {
log.error(e.getMessage());
}
return result;
}

改造后,即使切面报错,也不会影响业务代码的执行了。

AOP 是同步执行的,如果日志添加是一个比较耗时的操作,也会影响接口的响应速度,此时可以使用异步的方式,比如消息队列。

总结一下,Spring AOP 有以下几个优点和缺点。

  • 优点:

    • 侵入性低
    • 可重用性强

缺点及解决方案:

1、切面报错可能会影响业务代码

  • 问题:在切面的异常如果没有正确处理,可能会影响业务代码的正常执行。
  • 解决方案:
    • 捕获异常,确保业务代码正常执行
    • 一般使用try catch 捕获异常,防止向上传播

2、同步执行会影响接口响应速度

  • 问题:如果切面中存在耗时的操作,同步操作会导致接口的响应速度变慢。
  • 解决方案:
    • 通过消息队列将耗时操作异步执行
    • 使用 @Async 将方法异步执行,将任务从主线程脱离出来,交给其他线程池执行

事件监听 + 异步

Spring 事件监听机制是一种发布-订阅模式,将事件的发布和监听解耦开来。通过这种机制,事件的发布者无需关注监听的逻辑,监听者也无需直接依赖发布者。

Spring 事件监听有三个部分组成:

  1. 事件

自定义一个事件,继承 ApplicationEvent:

@Getter
@Setter
public class DemoEvent extends ApplicationEvent { private String name; public DemoEvent(Object source) {
super(source);
}
}
  1. 事件监听

基于 @EventListener 注解监听事件:

@Component
@Slf4j
public class DemoEventListener { @EventListener
public void handleEvent(DemoEvent event){
log.info(event.getName());
log.info("事件监听"); }
}
  1. 事件发布

使用 applicationEventPublisher.publishEvent 方法发布事件,


@Autowired
private ApplicationEventPublisher applicationEventPublisher; @Override
public void publish() {
// 执行业务代码
log.info("执行登录,当前时间 {}",LocalTime.now());
DemoEvent event = new DemoEvent(this);
event.setName("hello");
applicationEventPublisher.publishEvent(event);
log.info("完成登录,当前时间 {}",LocalTime.now());
log.info("执行 service");
}

配置好事件的发布和监听之后,在业务代码添加事件的发布,监听方法内添加日志的记录。

Spring 事件监听虽然解耦了发布和监听,只是解耦逻辑代码,两者还是同步执行,并且都是在同一个线程执行的,所以事件监听也无法解决添加日志报错,以及耗时的问题。

  1. 验证监听方法是否同步

在事件监听添加延迟:

  @EventListener
public void handleEvent(DemoEvent event){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(event.getName());
log.info("事件监听"); }

控制台输出:

执行登录,当前时间 16:52:30.799
hello
事件监听
完成登录,当前时间 16:52:33.799
执行 service

接口执行了 3 秒多,并且 publish 方法要等待监听方法执行完毕之后才能执行,说明事件监听是同步的

  1. 验证监听方法报错是否会影响主流程

在监听方法添加错误代码:

@EventListener
public void handleEvent(DemoEvent event){
int a = 1/0;
log.info(event.getName());
log.info("事件监听"); }

控制台输出:

执行登录,当前时间 17:10:08.396
java.lang.ArithmeticException: / by zero

监听方法报错,接口也报错,业务代码无法执行,说明监听方法报错会影响事件发布方法

解决报错的问题,使用异常捕获即可。而延迟的问题,就需要使用到异步的操作,异步就是另启一个线程执行监听方法,异步除了能解决延迟的问题,也顺便解决了报错的问题。

实现异步在监听方法上添加 @Async 异步注解,监听方法添加延迟和错误代码:

执行登录,当前时间 17:21:50.971
完成登录,当前时间 17:21:50.974
执行 service

publish 方法既不会延迟,也不会因为监听报错影响执行,异步完美解决耗时和报错的问题

消息队列

海量日志场景,消息队列是一个很好的选择,它也是解耦了发布者和订阅者,如果订阅者开启了手动确认,消费者也需要使用 try catch 捕获异常信息,确保消息能正常消费。

总结

本文介绍几种日志记录的实现方案:

  • Spring AOP:

    • 优点: 侵入性低,代码可重复性强
    • 问题以及解决方案:
      • 切面中可能会报错,报错会影响业务代码的正常执行,解决方法是使用 try catch 捕获异常。
      • 日志记录会影响业务代码执行效率,可以使用消息队列异步执行日志操作
  • 事件监听 + 异步
    • 优点: 解耦业务逻辑和日志记录,提升代码的内聚性。
    • 缺点以及解决方案:
      • AOP 存在的问题,事件监听同样存在,报错和耗时都会影响业务代码。报错可以使用异常捕获,延迟问题可以使用异步方式解决,而异步另起线程也顺便解决了报错影响业务代码的问题。
  • 消息队列
    • 优点: 使用于高并发日记记录场景
    • 问题: 增加系统的复杂性和稳定性,还需要考虑消息的丢失和重复消费问题。

Java日志记录几种实现方案的更多相关文章

  1. Java 日志记录规则

    Java 日志记录规则 规则一:日志是面向读者的 我们不应该让无价值的信息使日志文件变得乱糟糟,比如说完整打印所有的实体字段. 通常,实体名字和其逻辑关键字足以识别在表格中的一条记录了. 规则二:匹配 ...

  2. Java日志记录的事儿

    一.java日志组件 1.common-logging common-logging是apache提供的一个通用的日志接口.用户可以自由选择第三方的日志组件作为具体实现,像log4j,或者jdk自带的 ...

  3. Java学习笔记(十九)——Java 日志记录 AND log4j

    [前面的话] 学习的进度应该稍微在快一点. Java日志到了必须学习怎么使用的时候了,因为在项目中要进行使用.基础性文章,选择性阅读. [结构] java日志对调试,记录运行,问题定位都起到了很重要的 ...

  4. Log4Net日志记录两种方式

     简介 log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具.     log4net是Ap ...

  5. Java日志记录的5条规则

    日志记录是在软件开发过程中常常需要考虑的关键因素. 当产品运行出错时,日志文件通常是我们进行错误分析的首要选择. 而且,在很多情况下,它们是我们手上唯一可以用来查明发生状况和问题根本原因的信息. 可见 ...

  6. (网页)Java日志记录框架Logback配置详解(企业级应用解决方案)(转)

    转自CSDN: 前言 Logback是现在比较流行的一个日志记录框架,它的配置比较简单学习成本相对较低,所以刚刚接触该框架的朋友不要畏惧,多花点耐心很快就能灵活应用了.本篇博文不会具体介绍Logbac ...

  7. Java日志记录工具SLF4J介绍

    SLF4J是什么 SLF4J是一个包装类,典型的facade模式的工具,对用户呈现统一的操作方式,兼容各种主流的日志记录框架,典型的有log4j/jdk logging/nop/simple/jaka ...

  8. Java日志记录--log4j and logback

    问题的引入: 把所有的信息打印在控制台上不行吗? 01.控制台有行数限制: 02.System.out.println()影响系统性能: 03.如果我们需要对一些用户的行为习惯进行分析,我们找不到用户 ...

  9. Java分布式锁三种实现方案

    方案一:数据库乐观锁 乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的 ...

  10. Xml日志记录文件最优方案(附源代码)

    Xml作为数据存储的一种方式,当数据非常大的时候,我们将碰到很多Xml处理的问题.通常,我们对Xml文件进行编辑的最直接的方式是将xml文件加载到XmlDocument,在内存中来对XmlDocume ...

随机推荐

  1. Angular 18+ 高级教程 – Reactive Forms

    前言 上一篇的 Ajax 和这一篇的表单 (Form) 都是前端最最最常见的需求. 为此,Angular 分别提供了两个小型库来帮助开发者实现这些需求: Ajax – HttpClient Form ...

  2. Vs Code, Visual Studio 2022, Angular and Live Server Running Through Https and IP Address

    前言 之前就写过 angular cli, vs code liveserver, vs 2019 iis express 10, vs code kestrel 使用 https + ip. 但写的 ...

  3. Go runtime 调度器精讲(六):非 main goroutine 运行

    原创文章,欢迎转载,转载请注明出处,谢谢. 0. 前言 在 Go runtime 调度器精讲(三):main goroutine 创建 介绍了 main goroutine 的创建,文中我们说 mai ...

  4. HTTP——简介

    HTTP   

  5. 2022年4月中国数据库排行榜:华为GaussDB 挺进前四,榜单前八得分扶摇直上

    四月暖阳至,行业春风来.2022年4月的 墨天轮中国数据库流行度排行榜 已在墨天轮发布,本月共有205个数据库参与排名,相比上月新增六个数据库.本月整体排名变动不大,但排行榜上数据库整体得分涨幅较大. ...

  6. 前端面试题axaios携带 cookies

    配置 axios.default.widthCredentials = true;

  7. 1. C#面试题 - Webservice和WebApi的区别

    1. Webservice : 基于SOAP协议的,数据格式时XML,只支持http协议,不是开源的,只能部署在IIS上 2. Webapi 开源的,.net 平台

  8. 手动检查 token 是否过期

    1.在 存储 token 的时候说明登录了 此时 把时间戳记录一下  js-cookies - auth.js // 导入 js-cookie 用于操作 cookies import Cookies ...

  9. ABC370 E - Avoid K Partition

    ABC370 E - Avoid K Partition 求一个序列的合法划分方案数.一种划分合法当且仅当没有一个子串的和是 \(k\). 由于是否存在子串和为 \(k\) 很重要,因此考虑将它加入状 ...

  10. 一文彻底弄懂JUC工具包的Semaphore

    Semaphore 是 Java 并发包 (java.util.concurrent) 中的重要工具,主要用于控制多线程对共享资源的并发访问量.它可以设置"许可证"(permit) ...