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.
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.
log4j2.yml
under the src / main / resources
folderbuild.gradle
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.
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')
}
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
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
This is an explanation of how I noticed the lack of dependencies. I think it will be for intermediate Java users and above.
The following are possible reasons why log4j2.yml
is not loaded.
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)
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.
[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;
}
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.
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.
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