Love the idea of using an annotation to generate the custom logger. Still supporting the idea of making a Level interface with a default enum implementation. The annotation could generate a custom enum that either only has its specified levels, or it could be combined with the default enum. Since you can't extend an enum, annotation processing would be the next best thing. Plus, as I said before, generics _do_ support the idea of <T extends Enum<T> & Level> so that the Logger.log methods can use an arbitrary Level-derived enum. That's getting a bit into the implementation details, though.
On 23 January 2014 21:45, Remko Popma <[email protected]> wrote: > Gary, > > I think that's a very cool idea! > Much more flexible, powerful and elegant than pre-defined levels could > ever be. > It definitely makes sense to design the extensible enum with this > potential usage in mind. > > Remko > > > On Friday, January 24, 2014, Gary Gregory <[email protected]> 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 could allow hiding the fact that markers (or >> elves) are used so that the configuration looks like a normal >> configuration. Maybe a markerLevel attribute is used, who knows. The point >> is that this is now getting too complicated and too clever. Bottom line: >> real custom levels (not hidden through markers and definitively not elves) >> are needed to cleanly implement the custom level feature. >> >> >> Now, I am convinced that custom levels are needed and useful. >> >> >> Next up is how to implement them, a different story… but still based on >> the fact that I want a DefConLogger class, that’s my user story. First, the >> obvious, it would be nice to have: >> >> >> public enum DefConLevel { DEFCON1, DEFCON2, DEFCON3, DEFCON4, DEFCON5 } >> >> >> The order would determine the precedence from most to least important >> level. The enum might not be strictly needed (at first at least) since I >> 80/20 care about methods on DefConLogger, not how it works internally. >> >> >> So how do I write DefConLogger? >> >> >> Wouldn’t it be nice to be able to write: >> >> >> @CustomLogger(levels=DefConLevel.class) >> public class DefConLogger {} >> >> >> And have Log4J generate the boiler plate code (If you have the right >> magic hooked up in your IDE)? It might generate bytecodes directly, not >> sure. There are all sorts of code BC and generation possibilities with ASM, >> BCEL or a code generator. >> >> >> That still leaves the implementation TDB but then it really does not >> matter how we do it, as long as we do the dirty work for the user. >> >> >> Well, we care about the implementation on this ML of course. >> >> >> Gary >> >> >> On Wed, Jan 22, 2014 at 10:05 PM, Paul Benedict <[email protected]>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 <[email protected]>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 <[email protected]> >>>> 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 >>>>> <[email protected]>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: [email protected] | [email protected] >> Java Persistence with Hibernate, Second >> Edition<http://www.manning.com/bauer3/> >> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >> Spring Batch in Action <http://www.manning.com/templier/> >> Blog: http://garygregory.wordpress.com >> Home: http://garygregory.com/ >> Tweet! http://twitter.com/GaryGregory >> > -- Matt Sicker <[email protected]>
