By the way, there may be a small concurrency bug in the Level.values()
static method. This may be better:

    public static Level[] values() {
        Collection<Level> values = Level.levels.values();
        return values.toArray(new Level[values.size()]); // use value
collection size, not map size
    }



On Mon, Jan 27, 2014 at 7:43 AM, Matt Sicker <boa...@gmail.com> wrote:

> +1 to everything Nick said.
>
>
> On 26 January 2014 16:40, Nick Williams <nicho...@nicholaswilliams.net>wrote:
>
>> I would be OK with getting rid of the ordinal. It makes it less
>> enum-like, but I agree that the ordinal really has little purpose now. The
>> intLevel is more important.
>>
>> Here may be the best approach I can think of for calculating the
>> StandardLevel-equivalent on instantiation:
>>
>>     public static Level OFF = new Level("OFF", 0, StandardLevel.OFF) {};
>>     ...
>>     public static Level ALL = new Level("ALL", Integer.MAX_VALUE,
>> StandardLevel.ALL);
>>
>>     ...
>>
>>     private Level(String name, int intLevel, StandardLevel standardLevel)
>> { // this is the only c-tor standard levels use
>>         // same logic as current constructor
>>         this.standardLevel = standardLevel;
>>     }
>>
>>     protected Level(String name, int intLevel) { // this is the only
>> c-tor custom levels use
>>         this(name, intLevel, Level.calculateStandardLevel(intLevel));
>>     }
>>
>>     public enum StandardLevel {
>>         OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL
>>     }
>>
>> Thoughts?
>>
>> N
>>
>> On Jan 26, 2014, at 4:02 PM, Ralph Goers wrote:
>>
>> > I do have one other comment. You mention that the ordinal value isn’t
>> guaranteed because the levels might be instantiated in a different order
>> each time.  An alternative wold be to just get rid of the ordinal.  It
>> isn’t used anywhere by anything and when custom values are added they will
>> be added after the standard levels, which is correct but might not be what
>> you would expect.  Eliminating that would allow the static initialization
>> to stay as it is and get rid of the need for synchronization in the
>> constructor.
>> >
>> > Ralph
>> >
>> > On Jan 26, 2014, at 12:41 PM, Nick Williams <
>> nicho...@nicholaswilliams.net> wrote:
>> >
>> >> Some (ok, a lot of) feedback:
>> >>
>> >> - `private static ConcurrentMap<String, Level> levels` should be final.
>> >>
>> >> - `private static Object constructorLock` should be final. In fact,
>> code inspection flags this as a warning since code synchronizes on it.
>> >>
>> >> - The standard Level constants should be instantiated in a static
>> initializer like in my original code. Otherwise the order they are
>> instantiated in is unpredictable, and DEBUG (for example) may even have a
>> different ordinal each time the JVM starts.
>> >>
>> >> - Level isn't abstract. However, you use `new Level("xxx", n) {}`
>> (brackets) for Level.OFF, ExtendedLevels.NOTE, and ExtendedLevels.DETAIL,
>> but you use `new Level("xxx", n)` (no brackets) for other levels. IMO,
>> Level should be abstract (so that there's always exactly one instance of
>> every class that extends Level, just like a real enum), and the levels
>> should all use {} to construct. However, if we don't make Level abstract,
>> then we should remove {} from OFF, NOTE, and DETAIL because it's
>> unnecessary.
>> >>
>> >> - The way StdLevel appears in the middle of Level separates its static
>> fields from its instance fields, constructor, and methods. It makes it
>> difficult to read. I have to scroll pass StdLevel to see the rest of Level.
>> Can we please move StdLevel to the bottom of Level?
>> >>
>> >> - This is more personal preference, but I don't like abbreviating
>> things in class names. What is Std? We may know, but does everyone? To
>> someone unfamiliar with English, Standard is easy to translate, but they
>> have to first figure out that Std is short for Std. Can be please
>> un-abbreviate this to StandardLevel?
>> >>
>> >> - I disagree with this approach:
>> >>
>> >>           if (levels.containsKey(name)) {
>> >>               Level level = levels.get(name);
>> >>               if (level.intLevel() != intLevel) {
>> >>                   throw new IllegalArgumentException("Level " + name +
>> " has already been defined.");
>> >>               }
>> >>               ordinal = level.ordinal;
>> >>           }
>> >>
>> >> This allows multiple Levels with the same name/intLevel to be
>> instantiated, which prevents equality testing (level == Level.OFF) from
>> being 100% deterministic. It should really be this:
>> >>
>> >>           if (levels.containsKey(name)) {
>> >>               throw new IllegalArgumentException("Level " + name + "
>> has already been defined.");
>> >>           }
>> >>
>> >> - I think we should make all of the methods of Level final. Custom
>> levels shouldn't be able to change their behavior, IMO. Alternatively, if
>> we don't make Level abstract, we should make it final.
>> >>
>> >> - Level has no JavaDoc. The Level constants in Level have no JavaDoc.
>> That needs to be fixed.
>> >>
>> >> - I'm still not convinced StdLevel/StandardLevel is necessary. It
>> seems like an anti-pattern to me. From what I can tell (please let me know
>> if I'm missing something), StdLevel's entire purpose is to allow us to
>> still switch on the standard levels. I think that's a bad reason to create
>> an enum whose constants mirror Level. The primary problem with the StdLevel
>> is that the conversion from a Level to a StdLevel in many cases happens
>> __on every logging event__. That's going to be hugely inefficient. Really,
>> when a custom Level is created, it should be created with an equivalent
>> standard Level (or StdLevel). There are several ways to accomplish this
>> that are open to discussion, but I don't think the current
>> StdLevel.getStdLevel is the right approach. One alternative:
>> >>
>> >>   private final Level standardLevel;
>> >>
>> >>   protected Level(String name, int level, Level
>> mapToOtherFrameworksAs) {
>> >>       this(name, level);
>> >>       this.standardLevel = mapToOtherFrameworksAs;
>> >>   }
>> >>
>> >>   private Level(String name, int level) {
>> >>       // same as now
>> >>       this.standardLevel = this;
>> >>   }
>> >>
>> >>   public final Level getStandardLevel() {
>> >>       return this.standardLevel;
>> >>   }
>> >>
>> >> This does, admittedly, have some problems. Another alternative that I
>> could also be happy with that keeps the StandardLevel enum:
>> >>
>> >>   private final StandardLevel standardLevel;
>> >>
>> >>   protected Level(String name, int level, StandardLevel
>> mapToOtherFrameworksAs) {
>> >>       this(name, level);
>> >>       this.standardLevel = mapToOtherFrameworksAs;
>> >>   }
>> >>
>> >>   private Level(String name, int level) {
>> >>       // same as now
>> >>       this.standardLevel = this;
>> >>   }
>> >>
>> >>   public final StandardLevel getStandardLevel() {
>> >>       return this.standardLevel;
>> >>   }
>> >>
>> >> Nick
>> >>
>> >> On Jan 26, 2014, at 1:38 PM, Ralph Goers wrote:
>> >>
>> >>> I’ve committed the changes.  Take a look at ExtendedLevels.java,
>> ExtendedLevelTest.java and log4j-customLevel.xml in the log4j-core test
>> directories to see how it works.
>> >>>
>> >>> Ralph
>> >>>
>> >>> On Jan 26, 2014, at 1:19 AM, Remko Popma <remko.po...@gmail.com>
>> wrote:
>> >>>
>> >>>> I'm very curious! Can't wait to see it. Go for it!
>> >>>>
>> >>>> On Sunday, January 26, 2014, Ralph Goers <ralph.go...@dslextreme.com>
>> wrote:
>> >>>> I have completed the work on custom levels.  It uses a variation of
>> Nick’s “extensible enum” class.  The major difference with what he proposed
>> is that the custom enums must be declared in a class annotated with
>> @Plugin(name=“xxxx” category=“Level”) for them to be usable during
>> configuration.
>> >>>>
>> >>>> Are their any objections to me checking this in?  I’ll be doing the
>> commit at around noon Pacific Daylight Time if I don’t hear any.
>> >>>>
>> >>>> Ralph
>> >>>>
>> >>>>
>> >>>>
>> >>>> On Jan 25, 2014, at 7:08 AM, Ralph Goers <ralph.go...@dslextreme.com>
>> wrote:
>> >>>>
>> >>>>> I am working on the implementation of custom levels now.  I should
>> have it done today.
>> >>>>>
>> >>>>> Ralph
>> >>>>>
>> >>>>> On Jan 24, 2014, at 7:07 PM, Remko Popma <remko.po...@gmail.com>
>> wrote:
>> >>>>>
>> >>>>>> What is the best way to make progress on the custom levels
>> implementation?
>> >>>>>>
>> >>>>>> Do we re-open LOG4J-41 or start a fresh Jira ticket? For
>> implementation ideas, do we attach files to Jira, or create a branch?
>> >>>>>>
>> >>>>>> Remko
>> >>>>>>
>> >>>>>> On Saturday, January 25, 2014, Gary Gregory <
>> garydgreg...@gmail.com> wrote:
>> >>>>>> On Fri, Jan 24, 2014 at 11:48 AM, Remko Popma <
>> remko.po...@gmail.com> wrote:
>> >>>>>> Gary,
>> >>>>>>
>> >>>>>> The hard-coded levels were proposed because it seemed that the
>> extensible enum idea raised by Nick was not going to be accepted.
>> >>>>>> My original position was that Markers could fulfill the
>> requirement but Nick and yourself made it clear that this was not
>> satisfactory.
>> >>>>>>
>> >>>>>> With extensible enums and markers off the table it seemed that the
>> hard-coded levels was the only alternative, and discussion ensued about
>> what these levels should be called and what strength they should have.
>> >>>>>>
>> >>>>>> During this discussion, several people, including me, repeatedly
>> expressed strong reservations about adding pre-defined levels, but by this
>> time I think people were thinking there was no alternative.
>> >>>>>>
>> >>>>>> It looked like we were getting stuck, with half the group moving
>> in one direction ("add pre-defined levels!") and the other half wanting to
>> move in another direction ("don't add pre-defined levels!"). I asked that
>> we re-reviewed our assumptions and try to reach a solution that would
>> satisfy all users.
>> >>>>>>
>> >>>>>> We then decided to explore the option of using extensible enums
>> again. This is still ongoing, but I haven't seen anyone arguing against
>> this idea since we started this thread.
>> >>>>>>
>> >>>>>> Hard-coded levels and the extensible enum are different solutions
>> to the same problem.
>> >>>>>>
>> >>>>>> Hello All:
>> >>>>>>
>> >>>>>> Absolutely not. See my DEFCON example.
>> >>>>>> Talking about an "extensible enum" is mixing design and
>> implementation, we are talking about 'custom' and/or 'extensible' levels.
>> >>>>>> Custom/Extensible levels can be designed to serve one or all of:
>> >>>>>>
>> >>>>>> - Allow inserting custom levels between built-in levels.
>> >>>>>> - Allow for domain specific levels outside of the concept of
>> built-in levels, the DEFCON example.
>> >>>>>> - Should the custom levels themselves be extensible?
>> >>>>>>
>> >>>>>> Gary
>> >>>>>>
>> >>>>>> The extensible enum solution satisfies all of us who are opposed
>> to adding pre-defined levels, while also satisfying the original
>> requirement raised by Nick and yourself. Frankly I don't understand why you
>> would still want the pre-defined levels.
>> >>>>>>
>> >>>>>> Remko
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> On Sat, Jan 25, 2014 at 12:53 AM, Gary Gregory <
>> garydgreg...@gmail.com> wrote:
>> >>>>>> On Thu, Jan 23, 2014 at 10:45 PM, Remko Popma <
>> remko.po...@gmail.com> wrote:
>> >>>>>> Gary,
>> >>>>>>
>> >>>>>> I think that's a very cool idea!
>> >>>>>> Much more flexible, powerful and elegant than pre-defined levels
>> could ever be.
>> >>>>>>
>> >>>>>> As I wrote: "I am discussing custom levels here with the
>> understanding that this is a separate topic from what the built-in levels
>> are."
>> >>>>>>
>> >>>>>> I'm not sure why you want to make the features mutually exclusive.
>> (Some) others agree that these are different features.
>> >>>>>>
>> >>>>>> I see two topics:
>> >>>>>>
>> >>>>>> - What are the default levels for a 21st century logging
>> framework. Do we simply blindly copy Log4j 1? Or do we look at frameworks
>> from different languages and platforms for inspiration?
>> >>>>>> - How (not if, I think we all agree) should we allow for custom
>> levels.
>> >>>>>>
>> >>>>>> Gary
>> >>>>>>
>> >>>>>> It definitely makes sense to design the extensible enum with this
>> potential usage in mind.
>> >>>>>>
>> >>>>>> Remko
>> >>>>>>
>> >>>>>>
>> >>>>>> On Friday, January 24, 2014, Gary Gregory <garydgreg...@gmail.com>
>> wrote:
>> >>>>>> I am discussing custom levels here with the understanding that
>> this is a separate topic from what the built-in levels are. Here is how I
>> convinced myself that custom levels are a “good thing”.
>> >>>>>>
>> >>>>>> No matter which built-in levels exits, I may want custom levels.
>> For example, I want my app to use the following levels DEFCON1, DEFCON2,
>> DEFCON3, DEFCON4, and DEFCON5. This might be for one part of my app or a
>> whole subsystem, no matter, I want to use the built-in levels in addition
>> to the DEFCON levels. It is worth mentioning that if I want that feature
>> only as a user, I can “skin” levels in a layout and assign any label to the
>> built-in levels. If I am also a developer, I want to use DEFCON levels in
>> the source code.
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> At first, my code might look like:
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> logger.log(DefconLevels.DEFCON5, “All is quiet”);
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> Let’s put aside for now the type of DefconLevels.DEFCON* objects.
>> I am a user, and I care about my call sites.
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> What I really want of course is to write:
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> defconLogger.defcon5(“All is quiet”)
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> Therefore, I argue that for any “serious” use of a custom level, I
>> will wrap a Logger in a custom logger class providing call-site friendly
>> methods like defcon5(String).
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> So now, as a developer, all I care about is DefConLogger. It might
>> wrap (or subclass) the Log4J Logger, who knows. The implementation of
>> DefConLogger is not important to the developer (all I care is that the
>> class has ‘defconN’ method) but it is important to the configuration
>> author. This tells me that as a developer I do not care how DefConLogger is
>> implemented, with custom levels, markers, or elves. However, as
>> configuration author, I also want to use DEFCON level just like the
>> built-in levels.
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> The configuration code co
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> --
>> >>>>>> E-Mail: garydgreg...@gmail.com | ggreg...@apache.org
>> >>>>>> Java Persistence with Hibernate, Second Edition
>> >>>>>> JUnit in Action, Second Edition
>> >>>>>> Spring Batch in Action
>> >>>>>> Blog: http://garygregory.wordpress.com
>> >>>>>> Home: http://garygregory.com/
>> >>>>>> Tweet! http://twitter.com/GaryGregory
>> >>>>>
>> >>>>
>> >>>
>> >>
>> >>
>> >> ---------------------------------------------------------------------
>> >> To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org
>> >> For additional commands, e-mail: log4j-dev-h...@logging.apache.org
>> >>
>> >
>> >
>> > ---------------------------------------------------------------------
>> > To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org
>> > For additional commands, e-mail: log4j-dev-h...@logging.apache.org
>> >
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org
>> For additional commands, e-mail: log4j-dev-h...@logging.apache.org
>>
>>
>
>
> --
> Matt Sicker <boa...@gmail.com>
>

Reply via email to