[
https://issues.apache.org/jira/browse/LOG4J2-41?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13612306#comment-13612306
]
Nick Williams commented on LOG4J2-41:
-------------------------------------
Since there was discussion on the user list about this today, I decided I'd add
something here. I agree with NOT changing Level to an int. However, we CAN
still make it the functional equivalent of an enum but ALSO allow it to be
extensible. This change would be almost completely API-compatible: it could be
dropped in right now and, as long as the code isn't using an {{EnumMap}}s,
{{EnumSet}}s, etc, there should be no other changes needed. Here's the code:
{code}public abstract class Level implements Comparable<Level>, Serializable {
public static final Level OFF;
public static final Level FATAL;
public static final Level ERROR;
public static final Level WARN;
public static final Level INFO;
public static final Level DEBUG;
public static final Level TRACE;
public static final Level ALL;
private static final long serialVersionUID = 0L;
private static final Hashtable<String, Level> map;
private static final TreeMap<Integer, Level> values;
private static final Object constructorLock;
static {
// static variables must be constructed in certain order
constructorLock = new Object();
map = new Hashtable<String, Level>();
values = new TreeMap<Integer, Level>();
OFF = new Level("OFF", 0) {};
FATAL = new Level("FATAL", 100) {};
ERROR = new Level("ERROR", 200) {};
WARN = new Level("WARN", 300) {};
INFO = new Level("INFO", 400) {};
DEBUG = new Level("DEBUG", 500) {};
TRACE = new Level("TRACE", 600) {};
ALL = new Level("ALL", Integer.MAX_VALUE) {};
}
private static int ordinals;
private final String name;
private final int intLevel;
private final int ordinal;
protected Level(String name, int intLevel) {
if(name == null || name.length() == 0)
throw new IllegalArgumentException("Illegal null Level constant");
if(intLevel < 0)
throw new IllegalArgumentException("Illegal Level int less than
zero.");
synchronized (Level.constructorLock) {
if(Level.map.containsKey(name.toUpperCase()))
throw new IllegalArgumentException("Duplicate Level constant ["
+ name + "].");
if(Level.values.containsKey(intLevel))
throw new IllegalArgumentException("Duplicate Level int [" +
intLevel + "].");
this.name = name;
this.intLevel = intLevel;
this.ordinal = Level.ordinals++;
Level.map.put(name.toUpperCase(), this);
Level.values.put(intLevel, this);
}
}
public int intLevel() {
return this.intLevel;
}
public boolean isAtLeastAsSpecificAs(final Level level) {
return this.intLevel <= level.intLevel;
}
public boolean isAtLeastAsSpecificAs(final int level) {
return this.intLevel <= level;
}
public boolean lessOrEqual(final Level level) {
return this.intLevel <= level.intLevel;
}
public boolean lessOrEqual(final int level) {
return this.intLevel <= level;
}
@Override
@SuppressWarnings("CloneDoesntCallSuperClone")
public Level clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
@Override
public int compareTo(Level other) {
return intLevel < other.intLevel ? -1 : (intLevel > other.intLevel ? 1
: 0);
}
@Override
public boolean equals(Object other) {
return other instanceof Level && other == this;
}
public Class<Level> getDeclaringClass() {
return Level.class;
}
@Override
public int hashCode() {
return this.name.hashCode();
}
public String name() {
return this.name;
}
public int ordinal() {
return this.ordinal;
}
@Override
public String toString() {
return this.name;
}
public static Level toLevel(String name) {
return Level.toLevel(name, Level.DEBUG);
}
public static Level toLevel(String name, Level defaultLevel) {
if(name == null)
return defaultLevel;
name = name.toUpperCase();
if(Level.map.containsKey(name))
return Level.map.get(name);
return defaultLevel;
}
public static Level[] values() {
return Level.values.values().toArray(new Level[Level.values.size()]);
}
public static Level valueOf(String name) {
if(name == null)
throw new IllegalArgumentException("Unknown level constant [" +
name + "].");
name = name.toUpperCase();
if(Level.map.containsKey(name))
return Level.map.get(name);
throw new IllegalArgumentException("Unknown level constant [" + name +
"].");
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
{
return Enum.valueOf(enumType, name);
}
// for deserialization
protected final Object readResolve() throws ObjectStreamException {
return Level.valueOf(this.name);
}
}{code}
If a developer wanted to extend this, it could be done as simply as:
{code}public final class ExtendedLevels {
public static final Level MY_LEVEL = new Level("MY_LEVEL", 250) {};
}{code}
The stock levels' ints have been multiplied by 100 to allow room for extended
values falling between.
Just an idea. Like I said, I'm fine leaving it the way it is, but this could
achieve the functional equivalent and appease those who wish to see this
extensible.
> Extensible Log Level
> --------------------
>
> Key: LOG4J2-41
> URL: https://issues.apache.org/jira/browse/LOG4J2-41
> Project: Log4j 2
> Issue Type: Improvement
> Components: API
> Reporter: Ralph Goers
>
> It is desirable to have the Level be an enum. However, it is also desirable
> to let users add new log levels. These goals are in opposition to each other
> since enum classes are final. In addition, adding new levels implies adding
> new methods to the Logger interface (or some counterpart to it). This would
> be unworkable.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]