I would be happy with any solution that is generic enough to fulfill all users requirements and does not involve adding ad hoc levels.
On Thursday, January 23, 2014, Nick Williams <nicho...@nicholaswilliams.net> wrote: > Okay, I finally got a minute to read all of these emails, and... > > EVERYBODY FREEZE! > > What if I could get you an extensible enum that required no interface > changes and no binary-incompatible changes at all? Sound too good to be > true? I proposed this months ago (LOG4J2-41) and it got shot down multiple > times, but as of now I've heard THREE people say "extensible enum" in this > thread, so here it is, an extensible enum: > > 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); > } > } > > Extending it is easy: > > public final class ExtendedLevels { > public static final Level MY_LEVEL = new Level("MY_LEVEL", 250) {}; > } > > I still and have ALWAYS believed this was the best option. If we used this > option, I would be fine with not adding any new Levels because I could add > them myself. > > Nick > > On Jan 22, 2014, at 7:04 PM, Remko Popma wrote: > > > This is only a problem for webapps, right? > > Putting log4j jars in WEB-INF/lib avoids that problem (different class > loader). > > Apps that really want to share log4j jars with other apps would need to > play nice. Such apps would do well to use a naming convention like Gary > suggests. > > Otherwise, the last to register would overwrite any previous level with > the same name. (Should probably emit a StatusLogger warning.) > > > > Same intLevel for different names should not be a problem. > > > > > > On Thursday, January 23, 2014, Gary Gregory <garydgreg...@gmail.com> > wrote: > > Playing devils advocate: > > > > What happens when different apps register levels with the same name and > different intLevels? > > What happens when different apps register levels with the same intLevel > and different names? > > Should there be a convention that custom level names be FQNs? > > > > Gary > > > > > > On Wed, Jan 22, 2014 at 10:05 PM, Paul Benedict <pbened...@apache.org> > wrote: > > As Gary wanted, a new thread.... > > > > First, each enum needs an inherit strength. This would be part of the > interface. Forgive me if the word "strength" is wrong; but it's the 100, > 200, 300, etc. number that triggers the log level. So make sure the > interface contains the intLevel() method. > > > > Second, we need to know the name, right? The name probably requires a > new method since it can't be extracted from the enum anymore. > > > > public interface Level { > > int intLevel(); > > String name(); > > } > > > > PS: The intStrength() name seems hackish. What about strength() or > treshold()? > > > > Third, the registration can be done manually by providing a static > method (as your did Remko) that the client needs to invoke, or you could > have a class-path scanning mechanism. For the latter, you could introduce a > new annotation to be placed on the enum class. > > > > @CustomLevels > > public enum MyCustomEnums { > > } > > > > Paul > > > > On Wed, Jan 22, 2014 at 8:52 PM, Remko Popma <remko.po...@gmail.com> > wrote: > > Paul, can you give a bit more detail? > > > > I tried this: copy the current Level enum to a new enum called "Levels" > in the same package (other name would be fine too). Then change Level to an > interface (removing the constants and static methods, keeping only the > non-static methods). Finally make the Levels enum implement the Level > interface. > > > > After this, we need to do a find+replace for the references to > Level.CONSTANT to Levels.CONSTANT and Level.staticMethod() to > Levels.staticMethod(). > > > > Finally, the interesting part: how do users add or register their custom > levels and how do we enable the Levels.staticLookupMethod(String, Level) to > recognize these custom levels? > > > > > > > > On Thursday, January 23, 2014, Paul Benedict <pbened...@apache.org> > wrote: > > Agreed. This is not an engineering per se, but really more about if the > feature set makes sense. > > > > Well if you guys ever look into the interface idea, you'll give log4j > the feature of getting enums to represent custom levels. That's pretty > cool, IMO. I don't know if any other logging framework has that and that > would probably get some positive attention. It shouldn't be so hard to do a > find+replace on the code that accepts Level and replace it with another > name. Yes, there will be some minor refactoring that goes with it, but > hard? It shouldn't be. > > > > A name I propose for the interface is LevelDefinition. > > > > Paul > > > > > > On Wed, Jan 22, 2014 at 6:48 PM, Gary Gregory <garydgreg...@gmail.com> > wrote: > > Hi, I do not see this as an engineering problem but more a feature set > definition issue. So while there may be lots of more or less internally > complicated ways of solving this with interfaces, makers and whatnots, the > built in levels are the most user friendly. > > > > I have have lots of buttons, knobs and settings on my sound system that > I do not use, just like I do not use all the methods in all the classes in > the JRE... > > > > Gary > > > > > > > > > > -- > > E-Mail: garydgre > --------------------------------------------------------------------- > To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org<javascript:;> > For additional commands, e-mail: > log4j-dev-h...@logging.apache.org<javascript:;> > >