Use log4j2 with YAML + Gradle

When I was looking for a Java logger, I heard that log4j2 can write a configuration file in YAML, so I tried it. However, I was addicted to it more than I expected, so I will summarize the method to make it work with YAML. In addition, I do not think that XML is a format that human beings can read and write, so I am creating a sample application using Gradle instead of Maven.

Postscript

log4j2.yml is not loaded

Perform the following work referring to Write Log4j2 settings in YAML, try using it in Spring Boot and output the log. I tried to go.

dependencies {
    compile('org.apache.logging.log4j:log4j-core:2.7')
    compile('org.apache.logging.log4j:log4j-api:2.7')

    compile('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.5')
}

However, the following error log continues to be output ruthlessly. I couldn't find a solution by googled the wording of the error log or scrutinizing Stack Overflow.

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

Conclusion

The dependencies required jackson-core and jackson-databind. The following is the correct answer for the description of build.gradle. The sample code is available on here, so please refer to it if necessary.

dependencies {
    compile('org.apache.logging.log4j:log4j-core:2.7')
    compile('org.apache.logging.log4j:log4j-api:2.7')

    compile('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.5')
    compile('com.fasterxml.jackson.core:jackson-core:2.8.5')
    compile('com.fasterxml.jackson.core:jackson-databind:2.8.5')
}

YAML settings and log output

For reference, this is a simple YAML configuration example. Logs are output to the console at the log level according to the package hierarchy.

Configuration:
  status: debug

  Appenders:
    Console:
      name: CONSOLE
      target: SYSTEM_OUT
      PatternLayout:
        Pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %c{3} - %msg%n"

  Loggers:
    Logger:
      - name: cobot00.gs.first
        additivity: false
        level: warn
        AppenderRef:
          - ref: CONSOLE
      - name: cobot00.gs.first.second
        additivity: false
        level: info
        AppenderRef:
          - ref: CONSOLE
    Root:
      level: debug
      AppenderRef:
        ref: CONSOLE

layer.png

If it is a package or class hierarchy like the above, the log will be output as follows.

2017-01-30 22:56:49.007 [main] WARN  gs.first.FirstLayer - warning
2017-01-30 22:56:49.008 [main] ERROR gs.first.FirstLayer - error

2017-01-30 22:56:49.010 [main] INFO  first.second.SecondLayer - SecondLayer(number=2, tag=Gradle)
2017-01-30 22:56:49.011 [main] WARN  first.second.SecondLayer - warning
2017-01-30 22:56:49.011 [main] ERROR first.second.SecondLayer - error

2017-01-30 22:56:49.013 [main] INFO  second.third.ThirdLayer - ThirdLayer(number=3, tag=YAML)
2017-01-30 22:56:49.013 [main] WARN  second.third.ThirdLayer - warning
2017-01-30 22:56:49.013 [main] ERROR second.third.ThirdLayer - error

Mystery solving

This is an explanation of how I noticed the lack of dependencies. I think it will be for intermediate Java users and above.

Does the pass pass in the first place?

The following are possible reasons why log4j2.yml is not loaded.

  1. The path to the configuration file directory does not pass
  2. There is a lack of something beyond human knowledge

First, let's rename log4j2.yml to log4j2.xml to verify the case of 1. Then, the following exception is thrown.

[Fatal Error] log4j2.xml:1:1:You cannot specify content for the prologue.
ERROR StatusLogger Error parsing C:\coding\workspace\log4j2simple\bin\log4j2.xml
 org.xml.sax.SAXParseException; systemId: file:///C:/coding/workspace/log4j2simple/bin/log4j2.xml; lineNumber: 1; columnNumber: 1;You cannot specify content for the prologue.
  at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
  at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
  at org.apache.logging.log4j.core.config.xml.XmlConfiguration.<init>(XmlConfiguration.java:96)
  at org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory.getConfiguration(XmlConfigurationFactory.java:46)
  at org.apache.logging.log4j.core.config.ConfigurationFactory$Factory.getConfiguration(ConfigurationFactory.java:519)
(Omission)

It seems that it is recognized as log4j2.xml and parsed as XML. I am interested in the following logs. Isn't it possible to get some clues by examining the ConfigurationFactory class?

org.apache.logging.log4j.core.config.ConfigurationFactory$Factory.getConfiguration(ConfigurationFactory.java:519)

Let's examine the source code of log4j2

When you look at the hits by googled with log4j2`` ConfigurationFactory as a keyword, article of the URL starting with https://github.com You will find /blob/master/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java). Go to the main page of the repository from the link, check the URL for Clone, and execute git clone.

git clone [email protected]:apache/logging-log4j2.git

If you look at the project, pom.xml exists, so execute the following Maven command to import it into eclipse.

mvn eclipse:eclipse

After importing it into eclipse as a Maven project, if you check the ConfigurationFactory class and its surroundings, you will notice that there is a real package such as json xml`` yaml under the config package.

config.png

[YamlConfigurationFactory](https://github.com/apache/logging-log4j2/blob/master/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ in the yaml package yaml / YamlConfigurationFactory.java) The class exists. If you look at the implementation of YamlConfigurationFactory, you will see what seems to be the answer from the beginning. It seems that YamlConfigurationFactory will not take effect unless all the classes defined in the dependencies variable are complete. If you modify build.gradle so that com.fasterxml.jackson.databind com.fasterxml.jackson.core is included in the dependency, the error log in the example disappears and the contents set in log4j2.yml The log is now output with.

private static final String[] dependencies = new String[] {
    "com.fasterxml.jackson.databind.ObjectMapper",
    "com.fasterxml.jackson.databind.JsonNode",
    "com.fasterxml.jackson.core.JsonParser",
    "com.fasterxml.jackson.dataformat.yaml.YAMLFactory"
};

private final boolean isActive;

public YamlConfigurationFactory() {
    for (final String dependency : dependencies) {
        if (!Loader.isClassAvailable(dependency)) {
            LOGGER.debug("Missing dependencies for Yaml support");
            isActive = false;
            return;
        }
    }
    isActive = true;
}

Additional validation

I'm wondering why this is the implementation even though the YAML configuration file is now loaded. ConfigurationFactory.java which is the key to reading and interpreting the logger configuration file I tried to follow the process flow centering on apache / logging / log4j / core / config / ConfigurationFactory.java). Roughly speaking, the implementation was to check the existence of files by brute force according to the priority for the corresponding extensions, and use the first hit configuration file. Below are the extensions priorities.

Configuration file extension priority

priority extension
1 .properties
2 .yml
3 .yaml
4 .json
5 .jsn
6 .xml

When executing a test, the file name containing test is considered in order to give priority to the test file. Taking into account the priority of the extension and the test file, the file existence check is finally executed in the following order. And for YAML and JSON, additional classes are needed for parsing, so check the dependencies with the Factory class and ignore the existence of the configuration file if you don't have all the required libraries. It is an implementation that is done. This can lead to a situation where log4j2.xml is read but log4j2.yml and log4j2.json are ignored.

Configuration file name priority

priority config file name
1 log4j2-test.properties
2 log4j2-test.yml
3 log4j2-test.yaml
4 log4j2-test.json
5 log4j2-test.jsn
6 log4j2-test.xml
7 log4j2.properties
8 log4j2.yml
9 log4j2.yaml
10 log4j2.json
11 log4j2.jsn
12 log4j2.xml

Dependency library check is implemented as follows, but it seems that Missing dependencies for Json support is not output because of log level. It seems that another setting is required to output this log ...

for (final String dependency : dependencies) {
    if (!Loader.isClassAvailable(dependency)) {
        LOGGER.debug("Missing dependencies for Json support");
        isActive = false;
        return;
    }
}

Recommended Posts

Use log4j2 with YAML + Gradle
Use ProGuard with Gradle
Use WebJars with Gradle
Use jlink with gradle
Integration Test with Gradle
Use Puphpeteer with Docker
Use XVim2 with Xcode 12.0.1
Use CentOS with LXD
Install Gradle with ubuntu16.04
DataNucleus starting with Gradle
Use ngrok with Docker
Use webmock with Rspec
How to use Gradle
Get started with Gradle
Use Lambda Layers with Java
Java multi-project creation with Gradle
Use GDAL with Python with Docker
Gradle + Kotlin-Generate jar with DSL
Use Thymeleaf with Azure Functions
[PHP8] Install and use PECL YAML function (YAML parser) with Docker
Use Bulk API with RestHighLevelClient
Lombok not working with Gradle5
Use SDKMAN! With Git Bash
Remote debugging with Gradle test
Use Spring JDBC with Spring Boot
Use Ruby with Google Colab
Hello World with SpringBoot / Gradle
[Docker] Use whenever with Docker + Rails
Use PlantUML with Visual Studio Code
Use Basic Authentication with Spring Boot
Build a Java project with Gradle
Use java with MSYS and Cygwin
Use constructor with arguments in cucumber-picocontainer
Use Microsoft Graph with standard Java
Use PostgreSQL inet type with DbUnit
I tried using JOOQ with Gradle
Why use orchestration tools with Docker
Use bootstrap 4 with PlayFramework 2.6 (no CDN)
Let's use Amazon Textract with Ruby
Output test coverage with clover + gradle
I can't install lombok with Gradle.
Use Azure Bing SpellCheck with Java
Use JDBC with Java and Scala.
Use DataDog APM with unsupported frameworks
Use Java 11 with Google Cloud Functions
How to use mssql-tools with alpine
Beginning with Spring Boot 0. Use Spring CLI
Use cuda11.0 with pytorch using Docker
Develop Processing with IntelliJ + Kotlin + Gradle
Spring Boot gradle build with Docker