logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Remko Popma (JIRA)" <j...@apache.org>
Subject [jira] [Comment Edited] (LOG4J2-589) Allow the use of custom levels in configuration
Date Thu, 25 Sep 2014 01:22:34 GMT

    [ 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 1:22 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 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?


was (Author: remkop@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="infoAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d %-5p INFOAPPENDER - %m%n" />
    </Console>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="errorAppender" level="error" />
      <AppenderRef ref="diagAppender" level="diag" />
      <AppenderRef ref="infoAppender" level="debug" />
    </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 the level does not only exist in configuration: the plugin ensures the Level object with
the correct name/intLevel is instantiated. After that this Level can be used by components
that are part of the configuration process, as well as by the user application.

If the level is not defined in the configuration, then
* the configuration process will choke if other config elements refer to this level, like
 {{<AppenderRef ref="diagAppender" level="diag" />}}
* user code that uses Level.forName will work, but user code that uses Level.getName will
fail with a NPE in Logger$PrivateConfig.filter(Logger.java:314)

> Allow the use of 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.
> Classes that would need to be modified:
> BaseConfiguration, NullConfiguration, and DefaultConfiguration.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Mime
View raw message