07.java日志之logback内部状态数据Status

630 阅读3分钟

本篇文章中涉及到的所有代码都已经上传到gitee中: gitee.com/sss123a/log…

java日志系列全解

# 00.java⽇志之发展史

# 01.java日志之原生的日志框架jul

# 02.java日志之log4j入门及xml配置详解

# 03.java日志之log4j的基础组件和各种Appender

# 04.java日志之jcl门面

# 05.java日志之slf4j门面

# 06.java日志之logback源码和xml解析

# 07.java日志之logback内部状态数据Status

# 08.java日志之logabck各环境配置

# 09.java日志之logback的appender标签及其子标签全解析

# logback官方手册

logback内部日志status

记重点

  1. 通过statusListener标签去自定义StatusListener
<!-- logback内部日志自定义输出,具体实现见下文 -->
<statusListener class="com.matio.logback.statuslistener.CusStatusListener"/>

<!-- logback内部日志输出到标准console中 -->
<!-- 这种方式和<configuration debug="true">等价,具体参考ConfigurationModelHandler -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>

<!-- logback内部日志输出到指定文件中 -->
<statusListener class="ch.qos.logback.core.status.OnFileStatusListener">
    <filename>E:/test/logback.log</filename>
</statusListener>
  1. 系统属性logback.statusListenerClass去自定义StatusListener,比如:

System.setProperty("logback.statusListenerClass","com.matio.logback.statuslistener.CusStatusListener");

或者

System.setProperty("logback.statusListenerClass","ch.qos.logback.core.status.OnConsoleStatusListener");

  1. 禁用logback内部日志

System.setProperty("logback.statusListenerClass","ch.qos.logback.core.status.NopStatusListener");

  1. 通过 Java 代码手动注册StatusListener
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
   StatusManager statusManager = lc.getStatusManager();
   OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
   statusManager.add(onConsoleListener);

logback内部日志结构

如果在解析配置文件或者打印日志的过程中出现警告或错误,logback会自动在控制台上打印其内部状态数据,也就是今天的主人翁status,它允许方便地访问 logback 的内部状态。

Status

logback每一条内部输出日志都抽象成一个status,根据其紧急程度依次分为InfoStatusWarnStatusErrorStatus,其类图如下: image.png 每一个status都由以下几个部分组成:

int level; // 紧急程度:info、warn和error
final String message; // 日志内容,就是错误信息
final Object origin; // 由哪个类输出该日志
List<Status> childrenList;
Throwable throwable; // 错误error
long timestamp; // 记录status的时间戳

StatusManager

现在内部日志实体有了,那怎么样才能输出日志呢?

logback提供了一个StatusManager接口专门管理status

image.png

StatusManager默认只有一个实现BasicStatusManager

调用其add(Status);就可以输出一个内部日志status


List<Status> statusList = new ArrayList<Status>();
CyclicBuffer<Status> tailBuffer = new CyclicBuffer<Status>(150);

public void add(Status newStatus) {
    // 回调StatusListener
    fireStatusAddEvent(newStatus);

    count++;
    if (newStatus.getLevel() > level) {
        level = newStatus.getLevel();
    }
    synchronized (statusListLock) {
        if (statusList.size() < 150) {
            statusList.add(newStatus);
        } else {
            tailBuffer.add(newStatus);
        }
    }
}

// 回调StatusListener
private void fireStatusAddEvent(Status status) {
    synchronized (statusListenerListLock) {
        for (StatusListener sl : statusListenerList) {
            sl.addStatusEvent(status);
        }
    }
}

// 注册StatusListener
public boolean add(StatusListener listener) {
    synchronized (statusListenerListLock) {
        if (listener instanceof OnConsoleStatusListener) {
            boolean alreadyPresent = checkForPresence(statusListenerList, listener.getClass());
            if (alreadyPresent)
                return false;
        }
        statusListenerList.add(listener);
    }
    return true;
}

主要逻辑见

fireStatusAddEvent(newStatus);

其实statusmanager还维护了一组statuslistener,正是由这些statuslistener来输出日志

List statusListenerList = new ArrayList();

StatusListener

StatusListener接口:

public interface StatusListener {
    void addStatusEvent(Status status);
    default boolean isResetResistant() {
        return false;
    }
}

它主要有以下实现:

  • OnFileStatusListener:以追加方式输出到一个指定文件中
  • OnErrorConsoleStatusListener:以System.err方式输出到console上
  • OnConsoleStatusListener:以System.out方式输出到console上
  • NopStatusListener:无操作

用户如何在自己的程序中添加logback内部日志呢?

StatusViaSLF4JLoggerFactory

自定义StatusListener

logback配置文件中

<!-- statusListener:StatusListenerAction 解析成 StatusListenerModel 交给 StatusListenerModelHandler
    class:非空,反射生成StatusListener子实现,注册到Context.getStatusManager()中,顺便回调其(LifeCycle)start()方法
-->
<statusListener class="com.matio.logback.statuslistener.CusStatusListener"/>

statusListener标签的解析交由StatusListenerModelHandler实现,感兴趣可以去看看

自定义StatusListener实现类如下:

package com.matio.logback.statuslistener;

import ch.qos.logback.core.status.Status;
import ch.qos.logback.core.status.StatusListener;

public class CusStatusListener implements StatusListener {
    @Override
    public void addStatusEvent(Status status) {
        String message = status.getMessage();
        Throwable throwable = status.getThrowable();
        System.out.println(throwable);
        System.out.println(message);
    }
}

启动后会发现CusStatusListener成功输出logback内部日志了,如下:

null
Added status listener of type [com.matio.logback.statuslistener.CusStatusListener]
null
End of configuration.
null
Registering current configuration as safe fallback point
null
No appenders present in context [default] for logger [com.matio.logback.statuslistener.StatusListenerTest].

当然也可以通过系统属性logback.statusListenerClass去自定义StatusListener

System.setProperty("logback.statusListenerClass","com.matio.logback.statuslistener.CusStatusListener");

其生效原理可以参考StatusListenerConfigHelper.installIfAsked(Context);,原理很简单

在没有警告或错误的情况下,如果您仍然希望检查 logback 的内部状态,那么您可以通过调用类StatusPrinter.print()来打印logback内部状态数据

  // 假设SLF4J在当前环境中绑定了logback
  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
  // 打印logback的内部状态
  StatusPrinter.print(lc);
}

logback关于配置这块可以参考logback官方文档: logback.qos.ch/manual/conf…