Exploring
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
  • Java

    • 基础

    • 并发与多线程

    • 日志系统

      • 初识Java中的日志
      • 两种常用日志框架搭配快速上手
      • logback介绍
      • logback的架构
      • logback的配置
      • logback配置文件语法
      • logback中的Appender
      • logback配置中的Encoder
      • logback中的Layouts
      • logback中的Filter
      • logback中使用MDC
      • logback源码分析
      • 好看的彩色日志输出
    • 单元测试

    • JVM

    • Spring

    • SpringBoot

    • 一些工具

  • 语言
  • Java
  • 日志系统
unclezs
2020-12-05
0

logback源码分析

从一个Logger获取说起

public static Logger getLogger(Class<?> clazz) {
    //获取Logger
    Logger logger = getLogger(clazz.getName());
    if (DETECT_LOGGER_NAME_MISMATCH) {
        Class<?> autoComputedCallingClass = Util.getCallingClass();
        if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
            Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
                            autoComputedCallingClass.getName()));
            Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
        }
    }
    return logger;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

在跟进Logger logger = getLogger(clazz.getName());方法

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}
1
2
3
4

这个方法就是初始化LoggerContext,如果没有初始化则调用performInitialization进行初始化,初始化了就直接返回

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

再看看performInitialization()方法,里面就一个绑定和校验

private final static void performInitialization() {
    bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
        versionSanityCheck();
    }
}
1
2
3
4
5
6

比较核心的方法bind():

 private final static void bind() {
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            //这里通过findPossibleStaticLoggerBinderPathSet查找类路径下的org/slf4j/impl/StaticLoggerBinder.class
            //如果有多个或者没有则记录错误信息
            if (!isAndroid()) {
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            //这个是直接引入StaticLoggerBinder方式进行加注,这个初始化的时候会在static代码块中执行init方式加载配置
            //如果Jar包中没有这个类则会报错NoClassDefFoundError,多个的话Jvm优先选择先被加载的那个实现。
            StaticLoggerBinder.getSingleton();
            //初始化成功
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            //显示真实选择的实现类
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstituteLoggers();
            replayEvents();
            // release all resources in SUBST_FACTORY
            SUBST_FACTORY.clear();
        } catch (NoClassDefFoundError ncde) {
            String msg = ncde.getMessage();
            if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
                INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
                Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
                Util.report("Defaulting to no-operation (NOP) logger implementation");
                Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
            } else {
                failedBinding(ncde);
                throw ncde;
            }
        } catch (java.lang.NoSuchMethodError nsme) {
            String msg = nsme.getMessage();
            if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                INITIALIZATION_STATE = FAILED_INITIALIZATION;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }
            throw nsme;
        } catch (Exception e) {
            failedBinding(e);
            throw new IllegalStateException("Unexpected initialization failure", e);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

StaticLoggerBinder.getSingleton();看看具体做了些什么

//静态代码块初始化
static {
        SINGLETON.init();
}

void init() {
      try {
          try {
              //执行真正的配置自动选择与加载
              new ContextInitializer(defaultLoggerContext).autoConfig();
          } catch (JoranException je) {
              Util.report("Failed to auto configure default logger context", je);
          }
          //这里判断如果没有监听状态信息的话就 查看状态列表有没有warn以上的日志,有就打印到控制台
          if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
              StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
          }
          contextSelectorBinder.init(defaultLoggerContext, KEY);
          initialized = true;
      } catch (Exception t) { // see LOGBACK-1159
          Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
      }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

看看 new ContextInitializer(defaultLoggerContext).autoConfig();到底是怎么做的

public void autoConfig() throws JoranException {
    StatusListenerConfigHelper.installIfAsked(loggerContext);
    //找到具体的配置文件 logback-test.xml>logback.groovy>logback.xml
    URL url = findURLOfDefaultConfigurationFile(true);
    if (url != null) {
        //执行装载配置
        configureByResource(url);
    } else {
        //如果没有上面的xml、groovy配置到执行则查询SPI,找Configurator的实现类
        Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
        if (c != null) {
            try {
                c.setContext(loggerContext);
                c.configure(loggerContext);
            } catch (Exception e) {
                throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
                                .getCanonicalName() : "null"), e);
            }
        } else {
            //确实没有用户自定义配置,使用默认配置
            BasicConfigurator basicConfigurator = new BasicConfigurator();
            basicConfigurator.setContext(loggerContext);
            basicConfigurator.configure(loggerContext);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

配置完成之后回到init方法,回去执行contextSelectorBinder.init(defaultLoggerContext, KEY);

这里面主要是解决日志分离问题,通过VM参数指定logback.ContextSelector来配置ContextSelector实现日志分离

public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
                IllegalAccessException, InvocationTargetException {
    if (this.key == null) {
        this.key = key;
    } else if (this.key != key) {
        throw new IllegalAccessException("Only certain classes can access this method.");
    }

    String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
    if (contextSelectorStr == null) {
        contextSelector = new DefaultContextSelector(defaultLoggerContext);
    } else if (contextSelectorStr.equals("JNDI")) {
        // if jndi is specified, let's use the appropriate class
        contextSelector = new ContextJNDISelector(defaultLoggerContext);
    } else {
        contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

到这里就初始化完成了。状态也变为了SUCCESSFUL_INITIALIZATION,performInitialization方法执行完成。返回了StaticLoggerBinder.getSingleton().getLoggerFactory();

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();
            }
        }
    }
    switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case NOP_FALLBACK_INITIALIZATION:
            return NOP_FALLBACK_FACTORY;
        case FAILED_INITIALIZATION:
            throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
        case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

返回了指定的logback自定义ILoggerFactory的实现类LoggerContext

public ILoggerFactory getLoggerFactory() {
    if (!initialized) {
        return defaultLoggerContext;
    }

    if (contextSelectorBinder.getContextSelector() == null) {
        throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
    }
    return contextSelectorBinder.getContextSelector().getLoggerContext();
}
1
2
3
4
5
6
7
8
9
10

在执行了LoggerContext.getLogger()方法,可以看到他在创建Logger的时候会根据.分割的,从左到右依次查找,如果不存在就创建并保存,存在直接返回

@Override
public final Logger getLogger(final String name) {
    if (name == null) {
        throw new IllegalArgumentException("name argument cannot be null");
    }
    //名字为ROOT直接返回 根LOGGER
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
        return root;
    }
    int i = 0;
    Logger logger = root;
    //查询已经创建了的Logger
    Logger childLogger = (Logger) loggerCache.get(name);
    // 存在直接返回
    if (childLogger != null) {
        return childLogger;
    }

    //不存在则按照规则创建,配置日志等级,继承祖先配置
    String childName;
    while (true) {
        //通过 . 分割Logger等级 划分父子节点
        int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
        if (h == -1) {
            childName = name;
        } else {
            childName = name.substring(0, h);
        }
        // 记录当前的分割位置(祖先节点到哪里了)
        i = h + 1;
        synchronized (logger) {
            //这里是遍历子节点,拿到Logger,只找一级
            childLogger = logger.getChildByName(childName);
            //没有子节点了,创建Logger
            if (childLogger == null) {
                childLogger = logger.createChildByName(childName);
                //添加缓存
                loggerCache.put(childName, childLogger);
                incSize();
            }
        }
        //返回logger
        logger = childLogger;
        if (h == -1) {
            return childLogger;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

到这里就拿到了Logger进行了打印日志了,但是注意日志记录加载过程中,会经过turbofilter进行初步过滤,再经过RegularFilter进行过滤Appender,如果Appender都通过了再通过layout格式化日志,在经过Encoder把日志写入输出流显示。

在 GitHub 编辑此页 (opens new window)
上次更新: 2024/02/25, 12:11:11
logback中使用MDC
好看的彩色日志输出

← logback中使用MDC 好看的彩色日志输出→

Theme by Vdoing | Copyright © 2018-2024 unclezs
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式