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

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

上述行为就需要使用到日志系统来存储或者记录数据,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. .NET 开源工业级移动端仓库管理系统

    前言 在工业生产中,定制化的软件对于每个环节都至关重要.对于仓库管理,推荐一款开源的仓库管理系统(WMS)解决方案. 这款基于.NET 框架开发的移动应用,提供了全面的仓库操作.订单处理.主数据管理. ...

  2. Spring —— bean实例化

    bean 实例化 bean本质上就是对象,创建bean使用构造方法完成(反射)      构造方法(常用)        静态工厂*        实例工厂*        FactoryBean(实 ...

  3. .NET 工具库高效生成 PDF 文档

    前言 QuestPDF 是一个开源 .NET 库,用于生成 PDF 文档.使用了C# Fluent API方式可简化开发.减少错误并提高工作效率.利用它可以轻松生成 PDF 报告.发票.导出文件等. ...

  4. CentOS开放端口的方法(转载)

    CentOS开放端口的方法 转载:https://www.itfeichai.com/centos-open-porter/ Centos升级到7之后,内置的防火墙已经从iptables变成了fire ...

  5. ptmalloc、tcmalloc与jemalloc对比分析

    背景介绍 在开发微信看一看期间,为了进行耗时优化,基础库这层按照惯例使用tcmalloc替代glibc标配的ptmalloc做优化,CPU消耗和耗时确实有所降低.但在晚上高峰时期,在CPU刚刚超过50 ...

  6. 对抗生成网络(GAN)简单介绍

    对抗生成网络主要由生成网络和判别网络构成,GAN在图像领域使用较多.利用生成网络生成假的图像,然后利用判别器是否能判断该图像是假的. 1.用于医学图像分割,一般我们可以利用一个U-Net网络生成分割结 ...

  7. 区分::after和:before中的单冒号和双冒号的作用

    单冒号:一般指的是伪类,如鼠标悬停状态设置样式:选择器:hover {设置样式} 双冒号一般指伪元素,给元素的前面/后面添加内容.内容数据按堆栈数据结构存储.

  8. 5.15 相约上海!2021 年度首届云原生 Meetup | KubeSphere & Friends

    时至今日,Kubernetes 虽然变成了云原生这套系统化方法论和开源技术的核心一环,但已经无法独立存在,而是与云原生生态中所有的技术形态息息相关.为了将云原生生态中的各个技术形态结合起来,帮助企业最 ...

  9. 快速部署mysql并开启binlog

    curl -fsSL https://get.docker.com | bash yum -y install docker-ce sudo systemctl start docker sudo s ...

  10. Redisson 工作原理-源码分析

    时间不在于你拥有多少,而在于你怎样使用. 1:Redisson 是什么 个人理解:一种 可重入.持续阻塞.独占式的 分布式锁协调框架,可从 ReentrantLock 去看它. ①:可重入锁 拿到锁的 ...