Log4j2 소스 코드 분석 시리즈 :(가) 구성이로드

머리말

실제 개발 프로젝트에서 로그는 항상 주제에 열려 있지 않습니다. 기사의이 시리즈는 소스 로그 작품의 관점에서, SLF4J 및 log4j2日志体系为例을 시도합니다.

로깅 프레임 워크를 학습, 우리가 처음 로깅 프레임 워크의 모든 유형에 대해 잘 알고 있어야합니다, 우리가 그들을 반복하지 않을 것이다,이 개 기사를 추천합니다.

https://www.cnblogs.com/rjzheng/p/10042911.html

https://www.cnblogs.com/chanshuyi/p/something_about_java_log_framework.html

속성, XML, JSON / JSN 및 YAML / YML, 일반적으로 우리가 사용하는 XML의 대부분 : log4j2를 들어, 구성 파일은 여러 종류가 있습니다.

정상적인 상황에서, 우리는 / 프로젝트 폴더에 리소스 파일 log4j2.xml을 생성합니다. 대부분의 그것은 / 정보 / $ {ENV} / 폴더에 일반적으로, 또한 다른 log4j2이 파일을 읽을 다른 환경에서 분할 될 수있는 사용 받는다는 프로젝트 관리 환경 구성에 의존하고 있습니다.

대부분의 사람들은 일을 다시 땜질을 통해 구성을 복사 할 다른 항목을 "학습"한다. 그러나, 당신이 생각하는 경우 :

  1. 왜이 프로필을 쓰기? 문제가 될 것입니다 무슨 다음, 쓰지 말라?
  2. 기본 규칙의 프로필 이름? 왜 우리는 보통 log4j2.xml 오히려 다른 이름보다입니다 볼 수 있습니까?
  3. 이 구성 파일은 어떻게 넣었습니까?

이 질문에 대답,이 문서의 원래 의도입니다.

신속한

1.이 방법은 자신의 업무 프로세스를, 메인 라인으로 log4j2로드 프로세스를 구성 설명하는 디버그에 사용됩니다, 작은 효과 파생 세부 사항은 무시됩니다, 관심있는 독자는 소스 코드 자체에 액세스 할 수 있습니다.

그림 2. 멀티 경고! 컴퓨터 잘 확인하십시오.

3. 이해를 깊게하기 위해, 손에보십시오.

 

환경 준비

소스 코드를 읽기 전에 확인이 SLF4J 및 log4j2 의존성의 도입뿐만 아니라 적응 패키지로 제공된다. 받는다는 예로, 본 논문에서는 샘플 프로그램을 소개합니다

        <!-- slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!-- bridge --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.7</version> </dependency> <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.7</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.7</version> </dependency>

근원

첫째, 우리는 디버깅을 시작하는 지점을 휴식, 새로운 자바 파일을 만듭니다.

getLogger 메소드를 입력합니다. 그것은 LoggerFactory의 특정 로거 공장에 대해 알 수있다.

getILoggerFactory 방법을 입력합니다.

다음은 제어하지 않는 제 1 논리의 무리입니다, 우리는 결국 418 줄을 입력합니다.

接下来进入真正的日志绑定环节。由于我们只引入了log4j2,这里会直接找到它,继而绑定。StaticLoggerBinder就在log4j2的包中。

程序走到61行,可以看到这里使用饿汉方式实现了单例。41行实例化StaticLoggerBinder,会跳到53行,我们进去看看细节。

可以看出Log4jLoggerFactory继承了AbstractLoggerAdapter这个抽象的日志适配器。这个抽象适配器中定义了若干方法,别急,马上会提及。

回到LoggerFactory,通过方法getLoggerFactory,我们会得到刚刚创建出来的Log4jLoggerFactory:

接下来,我们进入log4j2的getLogger环节。

可以看到getLogger是个接口方法,并且有3个实现。

还记得我们刚才获取到的Log4jLoggerFactory吗?AbstractLoggerAdapter是它的父类,由此我们会走到AbstractLoggerAdapter的getLogger中。

getContext是AbstractLoggerAdapter的抽象方法,因此,我们下一步会走到Log4jLoggerFactory的getContext方法中。

这里用反射定位到我们的日志(anchor中文译为"锚",可以理解为类似文件句柄一类的东西),这里得出的anchor为"",因此会进入后面的语句。

最终,我们会拿到一个AppClassLoader,LogManager会利用这个类加载器获取上下文。

进入getContext看看:

请注意:这里的factory是Log4jContextFactory,它是在LogManager中的静态代码块中初始化的,具体细节后面会补充。

现在,我们先进入getContext看看:

这里的getContext是ContextSelector接口中的方法,下一步会进入ClassLoaderContextSelector中的getContext中。至于slector是怎么初始化的,我们放在后面一起说。

下面几步都是上下文相关操作,不再贴出,最终会回到这里:

然后走到152行的ctx.start方法,进去看下:

到现在,终于要开始加载配置了!!!

接下来几步比较直观,贴图示意:

在这里,先创建ConfigurationFactory的实例,然后获取配置。至于ConfigurationFactory的实例创建,这里不再说明,可自行查看。

接下来,进入getConfiguration方法:

进入该方法:

请注意,这里的getFactories已经很明显地告诉我们,这里有4个工厂(均继承自ConfigurationFactory ),分别处理前文提到的四类配置文件类型:properties、xml、json/jsn以及yaml/yml。调用factory.getSupportedTypes()方法即可获取到各类后缀。以xml为例:

其他类型文件同理。

好了,回到加载配置的方法,可以看到426行代码判断是否支持所有文件类型。其实最终的核心代码是453~467行:

这里尝试用不同的条件获取config,如果最终config为null,就会打印error日志,告诉你没有找到配置文件。由于目前我们还没有配置,就会走到466行。

 

现在,你可以在/resources路径下增加一个log4j2文件,填写一下简单配置,就会在459行得到config了。我们来看看getConfiguration的细节:

 

 可以看出,这里就是按照各种条件拼接处配置文件的名字。

 以最常见的log4j2.xml为例:

上图中,我们已经得到了配置文件的名字:log4j2.xml。

同时可以看到,prefix为log4j2,suffix为文件后缀。

其中prefix(505行)是写死在ConfigurationFactory中的:

所以,我们配置时定义的文件名,需要遵循规范,而不能随意命名。 

现在有了配置文件名,就可以加载了:

进入方法内部: 

现在,url已经获取到了。它的值是"项目路径/target/classes/log4j2.xml"。

后面的事情就是从文件加载内容( 517行,涉及到类加载器的知识,请自行查看)。

再然后,就是读取xml文件的内容啦:

走到这里,就开始读取xml文件了。这部分内容且待下回分解。 

 

遗留问题:LoggerManager的factory及其内部的selector是怎么初始化的?

其实,在调用LogManager.getContext(cl, false);之前,LoggerManager中的静态代码块会提前被调用,我们看一下:

 

我们看89~100行代码即可:

进入方法ProviderUtil.getProviders()内部查看:

可以看到provider是使用懒汉方式实现的单例(你会发现89行代码中ProviderUtil.hasProviders()方法执行时已经创建过了,因此这里直接返回。注意创建过程有个细节,后面要用到),用于确定各个factory的优先级。

我们重点看91行代码内部细节:

在96行,加载class,98行又将其转换为LoggerContextFactory的子类(也就是Log4jContextFactory)。

那么问题来了,className是啥,为啥它指定了Log4jContextFactory?

其实,在前面创建Provider实例时,构造器中会读取log4j-core中的配置文件,其中就包含className对应的属性:

就这样,得到了className:org.apache.logging.log4j.core.impl.Log4jContextFactory。

接着往下走:

可以看到这里会用反射的方式实例化Log4jContextFactory对象,会调用Log4jContextFactory的无参构造器:

createContextSelector方法,就会初始化selector啦:

后续的初始化细节就不再展开啦。

最后会走到这里:

至此,factory创建完毕。

 

现在,你应该可以回答文首的三个问题了吧?

总结

本文通过调试,描述了log4j2日志配置加载的主线(忽略了很多细节,比如可以配置path等等),后续的文章将会进一步描述配置文件的解析过程。 

希望读者通过本文,能够对log4j2的配置加载过程有更为深入的理解。

 

最后,作者水平有限,难免错漏,欢迎指正及交流,共同进步。

추천

출처www.cnblogs.com/xiaoxi666/p/11426259.html