[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14147080#comment-14147080 ]
Remko Popma edited comment on LOG4J2-589 at 9/25/14 4:04 AM:
-------------------------------------------------------------
I'm assuming that users want to write code like this:
{code}
public class Driver {
public static void main(String[] args) throws Exception {
final Logger logger = LogManager.getLogger("LOG4J2-589");
logger.error("an error message");
logger.info("an info message");
logger.log(Level.getLevel("DIAG"), "a DIAG message"); // either use Level.getLevel
logger.log(Level.forName("NOTICE", 450), "a NOTICE message"); // or Level.forName should work too
}
}
{code}
I'm also assuming that users want to filter on custom levels in configuration (the goal of this Jira):
{code}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace">
<!-- define custom levels so that loggers & appenders can filter on them -->
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
<CustomLevel name="VERBOSE" intLevel="550" />
<Appenders>
<Console name="errorAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p ERRORAPPENDER - %m%n" />
</Console>
<Console name="diagAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p DIAGAPPENDER - %m%n" />
</Console>
<Console name="noticeAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p NOTICEAPPENDER - %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<!-- filter on custom level -->
<AppenderRef ref="errorAppender" level="error" />
<AppenderRef ref="diagAppender" level="diag" />
<AppenderRef ref="noticeAppender" level="notice" />
</Root>
</Loggers>
</Configuration>
{code}
Currently the configuration process ignores the {{<CustomLevel>}} elements and chokes on the unknown level in {{<AppenderRef ref="diagAppender" level="diag" />}}:
{noformat}
2014-09-25 08:09:12,317 WARN Error while converting string [diag] to type [class org.apache.logging.log4j.Level]. Using default value [null]. java.lang.IllegalArgumentException: Unknown level constant [DIAG].
at org.apache.logging.log4j.Level.valueOf(Level.java:283)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter.convert(TypeConverters.java:230)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter.convert(TypeConverters.java:226)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters.convert(TypeConverters.java:336)
at org.apache.logging.log4j.core.config.plugins.visitors.AbstractPluginVisitor.convert(AbstractPluginVisitor.java:130)
at org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor.visit(PluginAttributeVisitor.java:44)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.generateParameters(PluginBuilder.java:246)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:135)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:756)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:691)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:683)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:683)
at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:358)
at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:159)
at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:385)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:444)
at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:151)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:85)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:37)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:176)
at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:427)
at log4j2_589_levelconfig.Driver.main(Driver.java:33)
{noformat}
A solution would be to have this plugin in core:
{code}
@Plugin(name = "CustomLevel", category = "Core")
public final class CustomLevelPlugin {
private CustomLevelPlugin() {
}
@PluginFactory
public static Level createLevel(
@PluginAttribute("name") final String levelName,
@PluginAttribute("intLevel") final int intLevel) {
Level result = Level.forName(levelName, intLevel);
return result;
}
}
{code}
So it is not the case that the level only exists in configuration.
The plugin instantiates the Level object with a call to Level.forName().
After the <CustomLevel> element is processed this Level can be used by the configuration process, as well as by the user application.
Of course, a custom level needs to be defined _before_ it can be used.If the user makes a mistake and does not define the level in the configuration, then
* the configuration process chokes if config elements refer to undefined levels, like {{<AppenderRef ref="diagAppender" level="diag" />}} - that appender/logger will not do anything
* user code that uses Level.getName instead of Level.forName will fail with a NPE in Logger$PrivateConfig.filter(Logger.java:314)
An alternative to this plugin is to ask users to manually call {{Level.forName("DIAG", 350)}} etc before the first call to LogManager.getLogger. This is the workaround I mentioned in my first comment on 06/Aug/14 14:21. That also solves the problem that a custom level must be defined before it can be used in config elements like {{<AppenderRef ref="diagAppender" level="diag" />}}.
But isn't the configuration alternative more elegant?
was (Author: ***@yahoo.com):
I'm assuming that users want to write code like this:
{code}
public class Driver {
public static void main(String[] args) throws Exception {
final Logger logger = LogManager.getLogger("LOG4J2-589");
logger.error("an error message");
logger.info("an info message");
logger.log(Level.getLevel("DIAG"), "a DIAG message"); // either use Level.getLevel
logger.log(Level.forName("NOTICE", 450), "a NOTICE message"); // or Level.forName should work too
}
}
{code}
I'm also assuming that users want to use custom levels in configuration (the goal of this Jira):
{code}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
<CustomLevel name="VERBOSE" intLevel="550" />
<Appenders>
<Console name="errorAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p ERRORAPPENDER - %m%n" />
</Console>
<Console name="diagAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p DIAGAPPENDER - %m%n" />
</Console>
<Console name="noticeAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p NOTICEAPPENDER - %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="errorAppender" level="error" />
<AppenderRef ref="diagAppender" level="diag" />
<AppenderRef ref="noticeAppender" level="notice" />
</Root>
</Loggers>
</Configuration>
{code}
Currently the configuration process ignores the {{<CustomLevel>}} elements and chokes on the unknown level in {{<AppenderRef ref="diagAppender" level="diag" />}}:
{noformat}
2014-09-25 08:09:12,317 WARN Error while converting string [diag] to type [class org.apache.logging.log4j.Level]. Using default value [null]. java.lang.IllegalArgumentException: Unknown level constant [DIAG].
at org.apache.logging.log4j.Level.valueOf(Level.java:283)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter.convert(TypeConverters.java:230)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter.convert(TypeConverters.java:226)
at org.apache.logging.log4j.core.config.plugins.convert.TypeConverters.convert(TypeConverters.java:336)
at org.apache.logging.log4j.core.config.plugins.visitors.AbstractPluginVisitor.convert(AbstractPluginVisitor.java:130)
at org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor.visit(PluginAttributeVisitor.java:44)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.generateParameters(PluginBuilder.java:246)
at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:135)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:756)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:691)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:683)
at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:683)
at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:358)
at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:159)
at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:385)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:444)
at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:151)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:85)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:37)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:176)
at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:427)
at log4j2_589_levelconfig.Driver.main(Driver.java:33)
{noformat}
A solution would be to have this plugin in core:
{code}
@Plugin(name = "CustomLevel", category = "Core")
public final class CustomLevelPlugin {
private CustomLevelPlugin() {
}
@PluginFactory
public static Level createLevel(
@PluginAttribute("name") final String levelName,
@PluginAttribute("intLevel") final int intLevel) {
Level result = Level.forName(levelName, intLevel);
return result;
}
}
{code}
So it is not the case that the level only exists in configuration.
The plugin instantiates the Level object with a call to Level.forName().
After the <CustomLevel> element is processed this Level can be used by the configuration process, as well as by the user application.
Of course, a custom level needs to be defined _before_ it can be used.If the user makes a mistake and does not define the level in the configuration, then
* the configuration process chokes if config elements refer to undefined levels, like {{<AppenderRef ref="diagAppender" level="diag" />}} - that appender/logger will not do anything
* user code that uses Level.getName instead of Level.forName will fail with a NPE in Logger$PrivateConfig.filter(Logger.java:314)
An alternative to this plugin is to ask users to manually call {{Level.forName("DIAG", 350)}} etc before the first call to LogManager.getLogger. This is the workaround I mentioned in my first comment on 06/Aug/14 14:21. That also solves the problem that a custom level must be defined before it can be used in config elements like {{<AppenderRef ref="diagAppender" level="diag" />}}.
But isn't the configuration alternative more elegant?
Post by Remko Popma (JIRA)Allow filtering on custom levels in configuration
-------------------------------------------------
Key: LOG4J2-589
URL: https://issues.apache.org/jira/browse/LOG4J2-589
Project: Log4j 2
Issue Type: Improvement
Components: Configurators
Affects Versions: 2.0-rc1
Reporter: James Hutton
Labels: configuration, custom, level
Fix For: 2.2
Previous title: Use forName instead of getLevel and valueOf for configuration
Without this one cannot use custom log levels in configuration without forking a large amount of code. Either the forName method needs to be removed and custom log levels should be explicitly forbidden, or support should be consistent.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)