Discussion:
[jira] [Created] (LOG4J2-589) Use forName instead of getLevel and valueOf for configuration
James Hutton (JIRA)
2014-03-31 12:14:15 UTC
Permalink
James Hutton created LOG4J2-589:
-----------------------------------

Summary: Use forName instead of getLevel and valueOf for 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


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.2#6252)
Gary Gregory (JIRA)
2014-03-31 13:10:15 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13955159#comment-13955159 ]

Gary Gregory commented on LOG4J2-589:
-------------------------------------

Please feel free to provide a patch with unit tests :)
Post by James Hutton (JIRA)
Use forName instead of getLevel and valueOf for 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
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.2#6252)
Remko Popma (JIRA)
2014-08-06 05:21:13 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-589:
-------------------------------

Description:
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.

was:
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.

Summary: Allow the use of custom levels in configuration (was: Use forName instead of getLevel and valueOf for configuration)

Configuration elements like Logger and AppenderRef that accept a Level attribute will use a {{LevelConverter}} (subclass of {{TypeConverter}}) to convert the specified string to a {{Level}} instance.

LevelConverter uses {{Level.valueOf(str)}} to do the conversion. The {{valueOf}} method will return a custom level instance if it exists, but throws an exception if not. So the configuration fails if it encounters custom levels that have not been registered yet.

Registration is done with the {{Level.forName(String name, int intValue)}} method. The {{intValue}} is necessary to determine where the custom level exists in relation to the built-in levels. (This is now [better documented|http://logging.apache.org/log4j/2.x/manual/customloglevels.html].)

I need to think a little about how to improve this. Perhaps configuration could have a custom levels section? For example:
{code}
<Configuration status="warn">
<!-- just an idea -->
<CustomLevels>
<Level name="DIAG" intLevel="350" />
<Level name="NOTICE" intLevel="450" />
<Level name="VERBOSE" intLevel="550" />
</CustomLevels>
...
{code}

As a workaround, you can manually register your custom levels before the configuration is loaded. One way to achieve this is to have a static initializer block in the first class that uses log4j2 loggers (or a class that is loaded before that):
{code}
static {
// register custom log levels before log4j2 configuration is loaded
Level.forName("DIAG", 350);
Level.forName("NOTICE", 450);
Level.forName("VERBOSE", 550);
}
{code}
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
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.2#6252)
Remko Popma (JIRA)
2014-08-10 02:02:15 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-589:
-------------------------------

Fix Version/s: 2.1
Post by Remko Popma (JIRA)
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.1
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.2#6252)
Remko Popma (JIRA)
2014-09-24 02:37:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-589:
-------------------------------
Fix Version/s: (was: 2.1)
2.2

This proposal is the missing piece that will make custom levels just as powerful as built-in levels. The [code generator|http://logging.apache.org/log4j/2.x/manual/customloglevels.html#CodeGen] addressed ease-of-use in the application code, this will address configuration.

Before starting on an implementation I would like feedback on the idea and on the configuration syntax (see comment above).

Thoughts?
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 15:21:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146417#comment-14146417 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

An even simpler syntax:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
<CustomLevel name="VERBOSE" intLevel="550" />
...
{code}
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 15:46:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146441#comment-14146441 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

I see some code in AbstractConfiguration that looks like it intends to load custom Levels from the configuration. However, it is not clear to me how this is supposed to work. If I create a plugin with {{category="Level"}} its factory method is never invoked...

Am I doing something wrong, or is the code in AbstractConfiguration used for something else? (Or is it broken?)

{code}
//AbstractConfiguration
public void start() {
LOGGER.debug("Starting configuration {}", this);
this.setStarting();
pluginManager.collectPlugins(pluginPackages);
final PluginManager levelPlugins = new PluginManager(Level.CATEGORY); // <--- this constant has value "Level"
levelPlugins.collectPlugins(pluginPackages);
final Map<String, PluginType<?>> plugins = levelPlugins.getPlugins();
if (plugins != null) {
for (final PluginType<?> type : plugins.values()) {
try {
// Cause the class to be initialized if it isn't already.
Loader.initializeClass(type.getPluginClass().getName(), type.getPluginClass().getClassLoader());
} catch (final Exception e) {
LOGGER.error("Unable to initialize {} due to {}", type.getPluginClass().getName(), e.getClass()
.getSimpleName(), e);
}
}
}
{code}

A simple custom Level plugin:
{code}
// TBD: if category="Level", the factory method is never invoked... Why?
@Plugin(name = "CustomLevel", category = "Core") // if category="Core" it works
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}
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Ralph Goers (JIRA)
2014-09-24 16:34:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146495#comment-14146495 ]

Ralph Goers commented on LOG4J2-589:
------------------------------------

I will have to refresh my memory. That code was committed as part of LOG4J2-41 - commit 891085e in git.
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 16:57:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146526#comment-14146526 ]

Remko Popma edited comment on LOG4J2-589 at 9/24/14 4:57 PM:
-------------------------------------------------------------

I see.

I think it may have to do with this test class:
{code}
package org.apache.logging.log4j.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.plugins.Plugin;

@Plugin(name="ExtendedLevel", category="Level")
public class ExtendedLevels {
public static final Level NOTE = Level.forName("NOTE", 350);
public static final Level DETAIL = Level.forName("DETAIL", 450);
}
{code}

Just doing a {{Class.forName("org.apache.logging.log4j.test.ExtendedLevels")}} would create these custom levels in memory. So a configuration that contains an {{<ExtendedLevel />}} element would create the DIAG and NOTICE levels. Would that be it?

If that was the intention I think it would be more user-friendly if users can just do this in configuration without having to write any java code:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
...
{code}



was (Author: ***@yahoo.com):
I see. That was the early days of custom levels...

I think it may have to do with this test class:
{code}
package org.apache.logging.log4j.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.plugins.Plugin;

@Plugin(name="ExtendedLevel", category="Level")
public class ExtendedLevels {
public static final Level NOTE = Level.forName("NOTE", 350);
public static final Level DETAIL = Level.forName("DETAIL", 450);
}
{code}

Just doing a {{Class.forName("org.apache.logging.log4j.test.ExtendedLevels")}} would create these custom levels in memory. Would that be it?

If that was the intention I think it would be more user-friendly if users can just do this in configuration without having to write any java code:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
...
{code}
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 16:55:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146526#comment-14146526 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

I see. That was the early days of custom levels...

I think it may have to do with this test class:
{code}
package org.apache.logging.log4j.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.plugins.Plugin;

@Plugin(name="ExtendedLevel", category="Level")
public class ExtendedLevels {
public static final Level NOTE = Level.forName("NOTE", 350);
public static final Level DETAIL = Level.forName("DETAIL", 450);
}
{code}

Just doing a {{Class.forName("org.apache.logging.log4j.test.ExtendedLevels")}} would create these custom levels in memory. Would that be it?

If that was the intention I think it would be more user-friendly if users can just do this in configuration without having to write any java code:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
...
{code}
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 16:59:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146526#comment-14146526 ]

Remko Popma edited comment on LOG4J2-589 at 9/24/14 4:58 PM:
-------------------------------------------------------------

I see.

I think it may have to do with this test class:
{code}
package org.apache.logging.log4j.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.plugins.Plugin;

@Plugin(name="ExtendedLevel", category="Level")
public class ExtendedLevels {
public static final Level NOTE = Level.forName("NOTE", 350);
public static final Level DETAIL = Level.forName("DETAIL", 450);
}
{code}

Just doing a {{Class.forName("org.apache.logging.log4j.test.ExtendedLevels")}} would create these custom levels in memory. So a configuration that contains an {{<ExtendedLevel />}} element would create the NOTE and DETAIL levels. Would that be it?

If that was the intention I think it would be more user-friendly if users can just do this in configuration without having to write any java code:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
...
{code}



was (Author: ***@yahoo.com):
I see.

I think it may have to do with this test class:
{code}
package org.apache.logging.log4j.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.plugins.Plugin;

@Plugin(name="ExtendedLevel", category="Level")
public class ExtendedLevels {
public static final Level NOTE = Level.forName("NOTE", 350);
public static final Level DETAIL = Level.forName("DETAIL", 450);
}
{code}

Just doing a {{Class.forName("org.apache.logging.log4j.test.ExtendedLevels")}} would create these custom levels in memory. So a configuration that contains an {{<ExtendedLevel />}} element would create the DIAG and NOTICE levels. Would that be it?

If that was the intention I think it would be more user-friendly if users can just do this in configuration without having to write any java code:
{code}
<Configuration status="warn">
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
...
{code}
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Ralph Goers (JIRA)
2014-09-24 18:23:35 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14146675#comment-14146675 ]

Ralph Goers commented on LOG4J2-589:
------------------------------------

How do these get used in an application? The the declaration in code you can reference the Level as a parameter to a logger method. How do you do that when the level only exists in configuration? What happens if the Level isn't defined in the configuration?
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-24 23:27:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14147080#comment-14147080 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

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)
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-25 01:22:34 UTC
Permalink
[ 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: ***@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)
Post by Remko Popma (JIRA)
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.
BaseConfiguration, NullConfiguration, and DefaultConfiguration.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
Remko Popma (JIRA)
2014-09-25 04:03:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-589:
-------------------------------
Summary: Allow filtering on custom levels in configuration (was: Allow the use of custom levels in configuration)
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)
Remko Popma (JIRA)
2014-09-25 04:05:33 UTC
Permalink
[ 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)
Remko Popma (JIRA)
2014-09-25 05:02:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-589:
-------------------------------
Fix Version/s: (was: 2.2)
2.1
Assignee: Remko Popma

This is turning out to be much, much easier than I thought. The plugin code is tiny and existing code doesn't need to change.

What remains to be done is some documentation in the [Custom Log Levels|http://logging.apache.org/log4j/2.x/manual/customloglevels.html] manual page, and update /log4j-core/src/main/resources/Log4j-config.xsd.

Am I missing something?

I'll push a branch for this when I get home. If no objections I'll include this in 2.1.
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Remko Popma (JIRA)
2014-09-25 17:26:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14148009#comment-14148009 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

Pushed branch LOG4J2-589 with documentation and schema update.
Feedback welcome.
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Remko Popma (JIRA)
2014-09-26 01:03:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14148572#comment-14148572 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

I have a question: currently I am putting the <CustomLevel> elements directly in the <Configuration> element. Would it be better to enclose them in a <CustomLevels> element?

That would look something like this:
{code}
<Configuration status="trace">
<!-- enclose custom level elements in a CustomLevels element? -->
<CustomLevels>
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
<CustomLevel name="VERBOSE" intLevel="550" />
</CustomLevels>
...
{code}

Looking at the plugin code for <Loggers> and <Appenders> the function of the "outer element plugins" seems to be to pass the LoggerConfig and Appender objects to the Configuration object. In the case of Levels, we just make sure the custom level is _defined_ by calling Level.forName(), but we can discard the result.

Is there any other good argument to nest them in a "holder" element?
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Gary Gregory (JIRA)
2014-09-26 01:29:33 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14148594#comment-14148594 ]

Gary Gregory commented on LOG4J2-589:
-------------------------------------

I like the CustomLevels element, it's neater to have it all in once place.

Gary
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Ralph Goers (JIRA)
2014-09-26 02:11:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14148624#comment-14148624 ]

Ralph Goers commented on LOG4J2-589:
------------------------------------

I would also argue that it is a good practice to place a list of items in a container and not have them just arbitrarily appear in some unrelated element. Another way of looking at it is that with the CustomLevels element I would expect to see a List<CustomLevel> (or similar) declared in the Configuration object. With CustomLevels just appearing there I don't have any idea where or how they are stored. Furthermore, I think something like Jackson would have a hard time with it.
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Remko Popma (JIRA)
2014-09-26 03:03:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14148662#comment-14148662 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

My intention was to just instantiate/define the Levels during configuration processing. The CustomLevelPlugin objects are currently transient and are discarded when config processing completes. I did not consider the need for storing this information in the Configuration object, because users can use {{Level.values()}} to obtain both the standard and the custom levels.

Should I add a getCustomLevels() method to Configuration? If so, please confirm the semantics:
This would only return the custom levels defined in the current configuration. So this does _not_ include custom levels that are defined in code with direct calls to {{Level.forName}}. If custom levels are removed from the config file, reconfiguration will result in a Configuration with _only_ the levels in the current configuration - even though the previously defined Level objects are _not_ removed. Finally, if we provide a setCustomLevels() method, should that method call {{Level.forName}}?

I did try a JSON configuration, both with and without a container element, and for some reason only the last "CustomLevel" element results in the {{CustomLevelPlugin.createLevel()}} factory method being invoked... No idea why yet.
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Remko Popma (JIRA)
2014-09-26 15:37:35 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14149306#comment-14149306 ]

Remko Popma commented on LOG4J2-589:
------------------------------------

Thanks for your feedback! I pushed an update on the LOG4J2-589 branch that incorporates your suggestions.
CustomLevel elements now must be configured in a containing CustomLevels element.

* Renamed CustomLevelPlugin to CustomLevelConfig
* Added method Configuration#getCustomLevels - returns List<CustomLevelConfig>
* Added plugin CustomLevels to act as the CustomLevel container
* Updated manual page
* Updated schema

Please take a look.
If no objections I'll merge this into master tomorrow afternoon JST (late Friday night EDT).
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)
Remko Popma (JIRA)
2014-09-27 11:59:34 UTC
Permalink
[ https://issues.apache.org/jira/browse/LOG4J2-589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma resolved LOG4J2-589.
--------------------------------
Resolution: Fixed

Merged branch LOG4J2-589 into master.
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
Assignee: Remko Popma
Labels: configuration, custom, level
Fix For: 2.1
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)

Loading...