一款给日志打标签实现精肯定位的日志切面利器

背景

不知道各位在生产日志中定位问题时有没有碰到这样的场景:因为coding的时候日志输出的比较少,出现问题时,很难经过日志去定位到问题。又或者是,你明明coding的时候有输出日志。可是在庞大的日志文件中,因为业务线程并发比较多,你输出的日志又没有加关键信息。你也很难定位到你所须要的日志信息。java

以前在公司里写业务代码时,为了使每一个RPC调用能被日志记录下来,咱们在公司基础组件里定义了一个切面,拦截全部的RPC调用,方法开始以前,在日志里输出调用的服务和方法以及参数,方法结束时输出方法的耗时。我相信不少人也是这么作的。git

这样一来 ,全部的调用都能经过参数里的关键信息被搜索到。也能定位到调用是什么结束的以及耗时。spring

可是在有些业务方法中,也打上了不少的业务日志。因为核心业务的tps和qps很高,日志是互相穿插的。若是你的日志没有打上关键的业务信息(好比订单号,业务ID),那就很难在日志中被定位出来。编程

也许有些童鞋会说,用线程号呀。经过搜索业务ID定位到调用开始的地方,再搜索这条线程的线程号,就能够定位整个请求的全部日志。其实以前我也是这么干的,可是线程通常都是由线程池进行管理的,在tps很高的业务中,同一个线程号有可能短期会出现屡次,可是倒是不一样的请求。并且业务方法中可能也会有异步线程,致使了线程号会变。这样对于定位日志就又增长了难度。固然最后能够经过对时间戳的分析,仍是能够定位到具体日志。可是这样就增长了定位的时间成本。springboot

若是你的公司对微服务使用了分布式追踪,那么定位日志能够经过traceId来解决。若是没有在生产上应用分布式追踪,又想在并发比较高的应用的日志上快速定位到所须要的日志。其中一个比较有效的办法就是:规范日志的输出格式架构

在每行日志输出时尽量的加上关键的业务信息,而后定位起来就比较清晰了,例如:并发

1.png

这里每一行日志都加上了订单号和请求ID,咱们把这样的日志头信息称之为日志标签,有了这些标签订位起来就比较容易了。app

那这样是否是意味着每写一行日志,都必须加上这样日志标签信息呢?这是否是也麻烦了?框架

答案是不用的。异步

推荐一款自动给日志打标签实现精肯定位的日志切面框架,你使用了这个,你的日志能够实现自定义的业务标签!

2.png

Aspect-log介绍

这是一款小巧,轻量级,对业务几乎无侵入的日志切面框架。特性为:

  • 使用简单,不侵入业务代码。只须要在方法上配置标注
  • 支持log4j,logback,log4j2三种常见的日志框架
  • 配置极其简单。提供“一键配置”,自动识别日志框架
  • 在方法上配置,不管调用多深或者异步调用。也能够统一加上日志标签
  • 无性能损耗

使用方法很简单,若是你是springboot,aspect-log提供了自动装配,GAV为:

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>aspect-log-spring-boot-starter</artifactId>
  <version>1.2</version>
</dependency>

若是你是spring,GAV为:

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>aspect-log-core</artifactId>
  <version>1.2</version>
</dependency>

配置方式

若是你使用spring,须要在项目里的application.xml里定义:

<bean class="com.yomahub.aspectlog.aop.AspectLogAop"/>

若是你使用springboot,AspectLog切面会自动装配好。

一键配置方法

这种方式用javassit实现,只须要一句话就能够实现。自动识别当前主流的日志框架。

@SpringBootApplication
public class Runner {

    static {AspectLogEnhance.enhance();}//进行日志加强,自动判断日志框架

    public static void main(String[] args) {
        try {
            SpringApplication.run(Runner.class, args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        while (true) {
            try {
                Thread.sleep(60000);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
}
针对于主流的Log日志框架做了适配(推荐用这种)

针对于每种Log日志框架提供了适配,具体使用方法请参照文末提供的项目主页里的文档。

如何使用

接下来在你的方法上加上@AspectLog标注,简单的例子以下:

@AspectLog({"id"})
public void demo1(String id,String name){
  log.info("这是第一条日志");
  log.info("这是第二条日志");
  log.info("这是第三条日志");
  new Thread(() -> log.info("这是异步日志")).start();
}

假设id的值为'NO1234',日志打出来的样子以下:

2020-02-08 20:22:33.945 [main] INFO  Demo - [NO1234] 这是第一条日志
2020-02-08 20:22:33.945 [main] INFO  Demo - [NO1234] 这是第二条日志
2020-02-08 20:22:33.945 [main] INFO  Demo - [NO1234] 这是第三条日志
2020-02-08 20:22:33.948 [Thread-3] INFO  Demo - [NO1234] 这是异步日志
@AspectLog支持多个标签:
@AspectLog({"id","name"})
public void demo1(String id,String name){
  log.info("这是第一条日志");
  log.info("这是第二条日志");
  log.info("这是第三条日志");
  new Thread(() -> log.info("这是异步日志")).start();
}

假设传入id的值为'NO1234',name为'jenny',日志打出来的样子以下:

2020-02-08 22:09:40.101 [main] INFO  Demo - [NO1234-jenny] 这是第一条日志
2020-02-08 22:09:40.101 [main] INFO  Demo - [NO1234-jenny] 这是第二条日志
2020-02-08 22:09:40.102 [main] INFO  Demo - [NO1234-jenny] 这是第三条日志
2020-02-08 22:09:40.103 [Thread-3] INFO  Demo - [NO1234-jenny] 这是异步日志
@AspectLog支持自定pattern和多个参数的链接符
@AspectLog(value = {"id","name"},pattern = "<-{}->",joint = "_")
public void demo(String id,String name){
  log.info("加了patter和joint的示例");
}

日志打出来的样子以下:

2020-02-08 22:09:40.103 [main] INFO  Demo - <-NO1234_jenny-> 加了patter和joint的示例
@AspectLog支持点操做符,适用于对象的取值,支持类型为业务对象和Map
@AspectLog({"person.id","person.age","person.company.department.dptId"})
public void demo(Person person){
  log.info("多参数加多层级示例");
}

日志打出来的样子以下:

2020-02-08 22:09:40.110 [main] INFO  Demo - [31-25-80013] 多参数加多层级示例
@AspectLog支持自定义Convert,适用于更复杂的业务场景
@AspectLog(convert = CustomAspectLogConvert.class)
public void demo(Person person){
  log.info("自定义Convert示例");
}
public class CustomAspectLogConvert implements AspectLogConvert {
    @Override
    public String convert(Object[] args) {
        Person person = (Person)args[0];
        return "PERSON(" + person.getId() + ")";
    }
}

日志打印出来的样子以下:

2020-02-20 17:05:12.414 [main] INFO  Demo - [PERSON(31] 自定义Convert示例
@AspectLog支持编程式设值
public void demo(){
  AspectLogContext.putLogValue("[SO1001]");
  log.info("代码控制示例");
}

日志打出来的样子:

2020-02-08 22:09:40.110 [main] INFO  Demo - [SO1001] 代码控制示例

结尾

日志输出对于一个服务来讲,便是执行轨迹的记录,也是寻错找源的根本。日志打的全面而工整,对于排查问题来讲,是事半功倍的,相信小伙伴都有看过乱糟糟的日志吧,在一堆这样的日志文件中取寻根溯源,那是极其痛苦的。

我相信不少开发者都有着代码洁癖,工程的架构,代码的结构,变量的定义,都有着苛刻的要求。这是种好习惯,这种好习惯会使代码的阅读和理解事半功倍。

因此Aspect-log就是这样一款能让你的日志也变得工整和赏心悦目的工具。

最后附上Aspect-log的工程主页,欢迎关注和提出issue

https://bryan31.gitee.io/aspe...

但愿这款小工具能让你的日志变得工整,能让你的排查变得简单而快捷。

联系做者

offIical-wx.jpg

相关文章
相关标签/搜索