Re: Using Custom Levels with a Custom/Wrapper Interface
On Wednesday, January 29, 2014, Nick Williams nicho...@nicholaswilliams.net wrote: On Jan 28, 2014, at 5:43 PM, Remko Popma wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) Personally, no. I don't like the code generation for IDE integration issues. I'd prefer an interface I wrote. I'm not sure what you mean by IDE integration issues, but let's not lose sight of why we are doing this in the first place: we want to provide convenience methods to make custom levels as easy to use as built-in levels. I'm beginning to feel that we are adding additional requirements on top of this that make it difficult to achieve our original goal... 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Also IMO, I would tend to steer away from only generating a concrete class. The interface and implementation should be separate. Imagine a Log4j fixing a bug in the generation code and releasing 2.0.1 or whatever. Now the user has a generated implementation, so they can't /just/ upgrade their Maven dependency. They /also/ have to re-generate their code. That's a pain. However, with the interface and implementation separate, the interface probably doesn't have to change, and the implementation will change as soon as they fire up their app with the latest Log4j. Also, if you separate the interface and implementation, you can make the generation of the interface optional. The user can /choose/ whether to generate the interface or create the interface themselves. Then the implementation generates at runtime. My original intuition was also to start with a separate interface and implementation but as I was thinking about what is the simplest thing that could possibly work? I ended up with this design. Please don't dismiss this solution just because it is based on a concrete class. Generating an implementation at runtime just seems so much more complex. It involves inspecting the signature of every method on the interface to see which Logger.log(Level, ...) method it should be mapped to. And what is there is no match? We would need to throw an IllegalArgumentException at runtime. This problem does not exist with the concrete class. I've taken a look at ASM (http://asm.ow2.org/) to generate byte code on-the-fly but I have kind of given up on that idea. (If anyone else wants to take this on please feel free but it seems the result may be fragile and difficult to maintain.) The alternative is probably annotation processing. This is interesting stuff and I look forward to taking a closer look into this in the next week or so. But just looking at the API, simple is not the word that comes to mind. :-) Also be aware that generating an implementation class at runtime would require users to have tools.jar in the classpath so we can compile the generated implementation class source code at runtime. The concrete class does not need additional jars. So there's a bunch of trade-offs here. I'd like to understand annotation processing a bit better first, but I still think the simplicity of the concrete class has a lot of appeal. Nick Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated
Re: Using Custom Levels with a Custom/Wrapper Interface
Annotation processing is beyond over-engineered, yet it still lacks some of the neat features from runtime annotation processing. I've been meaning to look more into it as it might be useful for injecting Loggers using annotations. On 29 January 2014 07:46, Remko Popma remko.po...@gmail.com wrote: On Wednesday, January 29, 2014, Nick Williams nicho...@nicholaswilliams.net wrote: On Jan 28, 2014, at 5:43 PM, Remko Popma wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) Personally, no. I don't like the code generation for IDE integration issues. I'd prefer an interface I wrote. I'm not sure what you mean by IDE integration issues, but let's not lose sight of why we are doing this in the first place: we want to provide convenience methods to make custom levels as easy to use as built-in levels. I'm beginning to feel that we are adding additional requirements on top of this that make it difficult to achieve our original goal... 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Also IMO, I would tend to steer away from only generating a concrete class. The interface and implementation should be separate. Imagine a Log4j fixing a bug in the generation code and releasing 2.0.1 or whatever. Now the user has a generated implementation, so they can't /just/ upgrade their Maven dependency. They /also/ have to re-generate their code. That's a pain. However, with the interface and implementation separate, the interface probably doesn't have to change, and the implementation will change as soon as they fire up their app with the latest Log4j. Also, if you separate the interface and implementation, you can make the generation of the interface optional. The user can /choose/ whether to generate the interface or create the interface themselves. Then the implementation generates at runtime. My original intuition was also to start with a separate interface and implementation but as I was thinking about what is the simplest thing that could possibly work? I ended up with this design. Please don't dismiss this solution just because it is based on a concrete class. Generating an implementation at runtime just seems so much more complex. It involves inspecting the signature of every method on the interface to see which Logger.log(Level, ...) method it should be mapped to. And what is there is no match? We would need to throw an IllegalArgumentException at runtime. This problem does not exist with the concrete class. I've taken a look at ASM (http://asm.ow2.org/) to generate byte code on-the-fly but I have kind of given up on that idea. (If anyone else wants to take this on please feel free but it seems the result may be fragile and difficult to maintain.) The alternative is probably annotation processing. This is interesting stuff and I look forward to taking a closer look into this in the next week or so. But just looking at the API, simple is not the word that comes to mind. :-) Also be aware that generating an implementation class at runtime would require users to have tools.jar in the classpath so we can compile the generated implementation class source code at runtime. The concrete class does not need additional jars. So there's a bunch of trade-offs here. I'd like to understand annotation processing a bit better first, but I still think the simplicity of the concrete class has a lot of appeal. Nick Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an
Re: Using Custom Levels with a Custom/Wrapper Interface
I would like to propose not doing anything on this subject until there is a request from the community. It's probably way too easy to over-engineer and devise solutions to problems that a small percentage (or zero?) of the users will need. I am all for ingenuity and passion, but after GA occurs and there's no experience with custom levels directly, we might get a better idea what the right answer is. So slate this for 2.x in JIRA and let's get to GA. On Wed, Jan 29, 2014 at 4:53 PM, Matt Sicker boa...@gmail.com wrote: Annotation processing is beyond over-engineered, yet it still lacks some of the neat features from runtime annotation processing. I've been meaning to look more into it as it might be useful for injecting Loggers using annotations. On 29 January 2014 07:46, Remko Popma remko.po...@gmail.com wrote: On Wednesday, January 29, 2014, Nick Williams nicho...@nicholaswilliams.net wrote: On Jan 28, 2014, at 5:43 PM, Remko Popma wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) Personally, no. I don't like the code generation for IDE integration issues. I'd prefer an interface I wrote. I'm not sure what you mean by IDE integration issues, but let's not lose sight of why we are doing this in the first place: we want to provide convenience methods to make custom levels as easy to use as built-in levels. I'm beginning to feel that we are adding additional requirements on top of this that make it difficult to achieve our original goal... 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Also IMO, I would tend to steer away from only generating a concrete class. The interface and implementation should be separate. Imagine a Log4j fixing a bug in the generation code and releasing 2.0.1 or whatever. Now the user has a generated implementation, so they can't /just/ upgrade their Maven dependency. They /also/ have to re-generate their code. That's a pain. However, with the interface and implementation separate, the interface probably doesn't have to change, and the implementation will change as soon as they fire up their app with the latest Log4j. Also, if you separate the interface and implementation, you can make the generation of the interface optional. The user can /choose/ whether to generate the interface or create the interface themselves. Then the implementation generates at runtime. My original intuition was also to start with a separate interface and implementation but as I was thinking about what is the simplest thing that could possibly work? I ended up with this design. Please don't dismiss this solution just because it is based on a concrete class. Generating an implementation at runtime just seems so much more complex. It involves inspecting the signature of every method on the interface to see which Logger.log(Level, ...) method it should be mapped to. And what is there is no match? We would need to throw an IllegalArgumentException at runtime. This problem does not exist with the concrete class. I've taken a look at ASM (http://asm.ow2.org/) to generate byte code on-the-fly but I have kind of given up on that idea. (If anyone else wants to take this on please feel free but it seems the result may be fragile and difficult to maintain.) The alternative is probably annotation processing. This is interesting stuff and I look forward to taking a closer look into this in the next week or so. But just looking at the API, simple is not the word that comes to mind. :-) Also be aware that generating an implementation class at runtime would require users to have tools.jar in the classpath so we can compile the generated implementation class source code at runtime. The concrete class does not need additional jars. So there's a bunch of trade-offs here. I'd like to understand annotation processing a bit better first, but I still think the simplicity of the concrete class has a lot of appeal. Nick Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with
Re: Using Custom Levels with a Custom/Wrapper Interface
I agree. Nick On Jan 29, 2014, at 5:09 PM, Paul Benedict wrote: I would like to propose not doing anything on this subject until there is a request from the community. It's probably way too easy to over-engineer and devise solutions to problems that a small percentage (or zero?) of the users will need. I am all for ingenuity and passion, but after GA occurs and there's no experience with custom levels directly, we might get a better idea what the right answer is. So slate this for 2.x in JIRA and let's get to GA. On Wed, Jan 29, 2014 at 4:53 PM, Matt Sicker boa...@gmail.com wrote: Annotation processing is beyond over-engineered, yet it still lacks some of the neat features from runtime annotation processing. I've been meaning to look more into it as it might be useful for injecting Loggers using annotations. On 29 January 2014 07:46, Remko Popma remko.po...@gmail.com wrote: On Wednesday, January 29, 2014, Nick Williams nicho...@nicholaswilliams.net wrote: On Jan 28, 2014, at 5:43 PM, Remko Popma wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) Personally, no. I don't like the code generation for IDE integration issues. I'd prefer an interface I wrote. I'm not sure what you mean by IDE integration issues, but let's not lose sight of why we are doing this in the first place: we want to provide convenience methods to make custom levels as easy to use as built-in levels. I'm beginning to feel that we are adding additional requirements on top of this that make it difficult to achieve our original goal... 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Also IMO, I would tend to steer away from only generating a concrete class. The interface and implementation should be separate. Imagine a Log4j fixing a bug in the generation code and releasing 2.0.1 or whatever. Now the user has a generated implementation, so they can't /just/ upgrade their Maven dependency. They /also/ have to re-generate their code. That's a pain. However, with the interface and implementation separate, the interface probably doesn't have to change, and the implementation will change as soon as they fire up their app with the latest Log4j. Also, if you separate the interface and implementation, you can make the generation of the interface optional. The user can /choose/ whether to generate the interface or create the interface themselves. Then the implementation generates at runtime. My original intuition was also to start with a separate interface and implementation but as I was thinking about what is the simplest thing that could possibly work? I ended up with this design. Please don't dismiss this solution just because it is based on a concrete class. Generating an implementation at runtime just seems so much more complex. It involves inspecting the signature of every method on the interface to see which Logger.log(Level, ...) method it should be mapped to. And what is there is no match? We would need to throw an IllegalArgumentException at runtime. This problem does not exist with the concrete class. I've taken a look at ASM (http://asm.ow2.org/) to generate byte code on-the-fly but I have kind of given up on that idea. (If anyone else wants to take this on please feel free but it seems the result may be fragile and difficult to maintain.) The alternative is probably annotation processing. This is interesting stuff and I look forward to taking a closer look into this in the next week or so. But just looking at the API, simple is not the word that comes to mind. :-) Also be aware that generating an implementation class at runtime would require users to have tools.jar in the classpath so we can compile the generated implementation class source code at runtime. The concrete class does not need additional jars. So there's a bunch of trade-offs here. I'd like to understand annotation processing a bit better first, but I still think the simplicity of the concrete class has a lot of appeal. Nick Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma
Re: Using Custom Levels with a Custom/Wrapper Interface
I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.comjavascript:_e({}, 'cvml', 'remko.po...@gmail.com'); wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class); Thoughts? On Tuesday, January 28, 2014, Gary Gregory garydgreg...@gmail.com wrote: On Mon, Jan 27, 2014 at 2:15 PM, Nick Williams nicho...@nicholaswilliams.net wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Of course, let's not get caught up in the names here and focus on the design first. What matters is that the current interface in split between a generic Level agnostic parent and a DSL sub-interface. I agree that 80/20 says that Logger should be the name of the interface that has info(), warn() and so on. The parent can be called LevelLogger for example since it must be called with a Level. I do not like SimpleLogger because it does not describe anything and 'simple' feels like a judgment call. Gary Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide
Re: Using Custom Levels with a Custom/Wrapper Interface
I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class);
Re: Using Custom Levels with a Custom/Wrapper Interface
I plan on catching up on all this tomorrow night. Gary On Tue, Jan 28, 2014 at 6:43 PM, Remko Popma remko.po...@gmail.com wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class); -- E-Mail: garydgreg...@gmail.com | ggreg...@apache.org Java Persistence with Hibernate, Second Editionhttp://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
Re: Using Custom Levels with a Custom/Wrapper Interface
On Jan 28, 2014, at 5:43 PM, Remko Popma wrote: I would really like everyone's feedback on this. I have two questions: 1. Does this fulfill everyone's requirements? Another way of asking this is: would you use this in your own projects? (If not, why not?) Personally, no. I don't like the code generation for IDE integration issues. I'd prefer an interface I wrote. 2. What do you think of this approach? Obviously code generation has drawbacks. For one thing, IDE refactoring tools don't work and API changes don't cause compile errors in the generator itself, only in the generated code. (I plan to add a JUnit test that generates a custom logger, compiles it and executes the custom logging methods so we can at least catch issues during the build.) The advantage is that it is open-ended and can deal with any custom level users can dream up. Is this trade-off acceptable? Are there other ways to solve this? Also IMO, I would tend to steer away from only generating a concrete class. The interface and implementation should be separate. Imagine a Log4j fixing a bug in the generation code and releasing 2.0.1 or whatever. Now the user has a generated implementation, so they can't /just/ upgrade their Maven dependency. They /also/ have to re-generate their code. That's a pain. However, with the interface and implementation separate, the interface probably doesn't have to change, and the implementation will change as soon as they fire up their app with the latest Log4j. Also, if you separate the interface and implementation, you can make the generation of the interface optional. The user can /choose/ whether to generate the interface or create the interface themselves. Then the implementation generates at runtime. Nick Remko On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I created https://issues.apache.org/jira/browse/LOG4J2-519 for this. Feedback welcome. On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class);
Re: Using Custom Levels with a Custom/Wrapper Interface
How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:49, Remko Popma remko.po...@gmail.com wrote: Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do
Re: Using Custom Levels with a Custom/Wrapper Interface
I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:49, Remko Popma remko.po...@gmail.com wrote: Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist
Re: Using Custom Levels with a Custom/Wrapper Interface
I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma remko.po...@gmail.com Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List log4j-dev@logging.apache.org Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:49, Remko Popma remko.po...@gmail.com wrote: Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config
Re: Using Custom Levels with a Custom/Wrapper Interface
I see... I thought the most common use case would be adding custom levels, but I see now that /taking away existing levels/ is also a use case. Perhaps that should be a separate code-gen tool, which generates an interface ( impl) that does not extend from Logger. One advantage of having the custom logger interface extend Logger is that the object can be passed to any method that accepts a Logger... I'm beginning to think these use cases may need to be supported separately. On Mon, Jan 27, 2014 at 11:51 PM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:49, Remko Popma remko.po...@gmail.com wrote: Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic
Re: Using Custom Levels with a Custom/Wrapper Interface
It's OK for a custom logger interface to extend Logger as long as the framework provides a stock implementation of the Logger interface. You want your customers to easily subclass existing code and not create the entire implementation from scratch. If you guys can make the Logger Implementation part of the supported client code, then it will be easy -- so easy that custom generation of code may just be overkill. On Mon, Jan 27, 2014 at 9:30 AM, Remko Popma remko.po...@gmail.com wrote: I see... I thought the most common use case would be adding custom levels, but I see now that /taking away existing levels/ is also a use case. Perhaps that should be a separate code-gen tool, which generates an interface ( impl) that does not extend from Logger. One advantage of having the custom logger interface extend Logger is that the object can be passed to any method that accepts a Logger... I'm beginning to think these use cases may need to be supported separately. On Mon, Jan 27, 2014 at 11:51 PM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please
Re: Using Custom Levels with a Custom/Wrapper Interface
On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de...@gmail.com wrote: I know we can't do what I would like via configuration. My point was to primarily to spark discussion on how we could make the api as simple as possible. I'm ok with where we are on the custom level support. I do think this brings us back around to adding built in levels, in a separate thread. I'm really pleased with how things are coming together. Good stuff. Scott On Jan 26, 2014 9:25 PM, Nicholas Williams nicho...@nicholaswilliams.net wrote: Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos
Re: Using Custom Levels with a Custom/Wrapper Interface
I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.org wrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.com wrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de
Re: Using Custom Levels with a Custom/Wrapper Interface
Yeah, I was thinking the same thing as you. Ralph On Jan 27, 2014, at 11:15 AM, Nick Williams nicho...@nicholaswilliams.net wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.org wrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.com wrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class
Re: Using Custom Levels with a Custom/Wrapper Interface
Nick, I don't understand your objection. I was not proposing replacing Logger. All I said was extract out a superinterface. Keep Logger usage how it is now, if you want -- and what I am recommending. On Mon, Jan 27, 2014 at 1:15 PM, Nick Williams nicho...@nicholaswilliams.net wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try { Constructor? constr = implClass.getConstructor(Logger.class); return (T) constr.newInstance(wrapped); } catch (Exception ex) { throw new IllegalStateException( Unable to construct instance of custom logger class + implClass.getName(), ex); } } On Monday, January 27, 2014, Scott Deboy scott.de
Re: Using Custom Levels with a Custom/Wrapper Interface
On Jan 27, 2014, at 11:15 AM, Nick Williams nicho...@nicholaswilliams.net wrote: However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. This is what I was recommending. Sorry for any confusion. Paul
Re: Using Custom Levels with a Custom/Wrapper Interface
Okay, I see now. I got confused by what Gary said: That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). Nick On Jan 27, 2014, at 1:25 PM, Paul Benedict wrote: On Jan 27, 2014, at 11:15 AM, Nick Williams nicho...@nicholaswilliams.net wrote: However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. This is what I was recommending. Sorry for any confusion. Paul
Re: Using Custom Levels with a Custom/Wrapper Interface
I would like to see SimpleLogger be where custom interfaces are extended. Now you can't forget that if we provide a default implementation class, that will already implement Logger. So it's very easy to cast directly to Logger if necessary to access your standard methods. On Mon, Jan 27, 2014 at 1:59 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Okay, I see now. I got confused by what Gary said: That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). Nick On Jan 27, 2014, at 1:25 PM, Paul Benedict wrote: On Jan 27, 2014, at 11:15 AM, Nick Williams nicho...@nicholaswilliams.net wrote: However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. This is what I was recommending. Sorry for any confusion. Paul -- Cheers, Paul
Re: Using Custom Levels with a Custom/Wrapper Interface
On Mon, Jan 27, 2014 at 2:15 PM, Nick Williams nicho...@nicholaswilliams.net wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Of course, let's not get caught up in the names here and focus on the design first. What matters is that the current interface in split between a generic Level agnostic parent and a DSL sub-interface. I agree that 80/20 says that Logger should be the name of the interface that has info(), warn() and so on. The parent can be called LevelLogger for example since it must be called with a Level. I do not like SimpleLogger because it does not describe anything and 'simple' feels like a judgment call. Gary Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate objects of the implementation class. Example tool invocation: java org.apache.logging.log4j.util.Generate com.mycomp.myproject.MyLogger DIAG=350 NOTICE=450 VERBOSE=550 Generated code: @CustomLoggerImplementation(MyLogger.Impl.class) public interface MyLogger extends Logger { void diag(Marker marker, Message msg); void diag(Marker marker, Message msg, Throwable t); // ... other methods public static final class Impl extends AbstractLoggerWrapper implements MyLogger { private final static Level DIAG = Level.getOrCreateLevel(DIAG, 350); private final static Level NOTICE = Level.getOrCreateLevel(NOTICE, 450); private final static Level VERBOSE = Level.getOrCreateLevel(VERBOSE, 550); public Impl(final AbstractLogger logger) { super(logger, logger.getName(), logger.getMessageFactory()); } public void diag(Marker marker, Message msg) { logger.log(DIAG, marker, msg); } public void diag(Marker marker, Message msg, Throwable t) { logger.log(DIAG, marker, msg, t); } // ... other methods } } LogManager: public static T extends Logger T getCustomLogger(ClassT cls, String name) { Logger wrapped = getLogger(name); return wrap(cls, wrapped); } private static T extends Logger T wrap(ClassT cls, Logger wrapped) { CustomLoggerImplementation annotation = cls.getAnnotation(CustomLoggerImplementation.class); Class? implClass = annotation.value(); try
Re: Using Custom Levels with a Custom/Wrapper Interface
More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class); Thoughts? On Tuesday, January 28, 2014, Gary Gregory garydgreg...@gmail.com wrote: On Mon, Jan 27, 2014 at 2:15 PM, Nick Williams nicho...@nicholaswilliams.net javascript:_e({}, 'cvml', 'nicho...@nicholaswilliams.net'); wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Of course, let's not get caught up in the names here and focus on the design first. What matters is that the current interface in split between a generic Level agnostic parent and a DSL sub-interface. I agree that 80/20 says that Logger should be the name of the interface that has info(), warn() and so on. The parent can be called LevelLogger for example since it must be called with a Level. I do not like SimpleLogger because it does not describe anything and 'simple' feels like a judgment call. Gary Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list of NAME=INTLEVEL custom log levels. The generated source code contains both the interface and an implementation. The implementation is an inner class of the interface (so users only need to manage one single file). The generated interface is annotated with the class name of the implementation class. At runtime, users call LogManager.getCustomLogger(Class, String) to get a custom logger instance. The LogManager then uses the annotation on the interface class to instantiate
Re: Using Custom Levels with a Custom/Wrapper Interface
I've started to work on implementing a source code generator along the lines described below. Does anyone disagree with this approach? I was thinking to name the tools Generate$ExtendedLogger and Generate$CustomLogger and put the Generate class in the log4j-api project under org.apache.logging.log4j.util or create a new package called org.apache.logging.log4j.experimental. Thoughts? On Tuesday, January 28, 2014, Remko Popma remko.po...@gmail.com wrote: More thoughts on CustomLogger/ExtendedLogger source code generation: Perhaps I was overcomplicating things... Why don't we generate source for a concrete class instead of an interface+implementation? If users want to /extend/ Logger, this class would extend AbstractLoggerWrapper (which has the standard debug(), info(), warn(), ... methods). If users want to hide the standard methods, the generated class would simply not extend AbstractLoggerWrapper, so the only public methods would be the generated methods for the custom log levels. This allows users to generate both extended loggers (with extra methods) and custom domain specific Loggers (like a logger that only has defcon1(), defcon2(), defcon3() methods). Note: this would also enable users to /take away/ existing log4j log levels. Suppose you don't like the FATAL or TRACE log levels? Simply generate a custom logger with levels ERROR=200 WARN=300 INFO=400 DEBUG=500. Since this is a wrapper, there is no need for API changes or additional methods on LogManager: the generated custom logger has factory methods that internally call the existing LogManager.getLogger() methods and wrap the resulting Logger. So in client code, users would obtain a custom logger this way: MyLogger logger = MyLogger.create(MyClass.class); Thoughts? On Tuesday, January 28, 2014, Gary Gregory garydgreg...@gmail.comjavascript:_e({}, 'cvml', 'garydgreg...@gmail.com'); wrote: On Mon, Jan 27, 2014 at 2:15 PM, Nick Williams nicho...@nicholaswilliams.net wrote: I would veto such a change, and here is my technical justification: You will break EVERYTHING currently using the Log4j 2 API. EVERYTHING that EVERY Log4j 2 user has currently written will have to be changed to use StandardLogger instead of Logger. That's not even considering the fact that Logger (or ILogger as the case may be) is *the* industry standard for logger interface names that provide info(), warn(), and other methods. I know that APIs can change when something's still beta, but this is a HUGE CHANGE. However, what I WOULD be okay with is creating a SimpleLogger interface for things like log(Level, ...), etc. and having Logger extend SimpleLogger to provide info(), warn(), and so on. This would be backwards compatible and abide by industry norms. Of course, let's not get caught up in the names here and focus on the design first. What matters is that the current interface in split between a generic Level agnostic parent and a DSL sub-interface. I agree that 80/20 says that Logger should be the name of the interface that has info(), warn() and so on. The parent can be called LevelLogger for example since it must be called with a Level. I do not like SimpleLogger because it does not describe anything and 'simple' feels like a judgment call. Gary Nick On Jan 27, 2014, at 12:46 PM, Gary Gregory wrote: On Mon, Jan 27, 2014 at 10:24 AM, Paul Benedict pbened...@apache.orgwrote: I propose this: 1) If you are willing to view standard levels as a type of DSL, then you should refactor the Logger interface to separate those concerns. Create a new superinterface that contains everything else. This is what custom loggers will extend. That's brilliant! The Logger interface contains methods like log(Level, ...) and StandardLogger extends Logger to provide info(), warn() and so on. This let's me create a custom Logger (DEFCON example) AND an extension to StandardLogger with refined levels (NOTICE, DIAG, VERBOSE). This makes it clear that a Custom Logger is different than an Extensible Logger to StandardLogger. Gary 2) The customer loggers not only register an interface but also the implementation that goes with it. 3) Retrieve a custom logger as follows: T extends LoggerSuperInterface T Logger.getCustomLogger(T t); Paul On Mon, Jan 27, 2014 at 8:51 AM, Gary Gregory garydgreg...@gmail.comwrote: I also want to avoid extending Logger for domain specific applications. For medical devices for example I could only have critical, warning, advisory. Original message From: Remko Popma Date:01/27/2014 09:15 (GMT-05:00) To: Log4J Developers List Subject: Re: Using Custom Levels with a Custom/Wrapper Interface How about starting with something very simple at first? We provide a tool that generates the source code for a custom logger interface. To invoke the tool the user passes it the fully qualified name of the interface, and a list
Re: Using Custom Levels with a Custom/Wrapper Interface
If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the getClassName method? I think you misunderstand the purpose of the ClassT argument. It has nothing to do with the logger name--it's the class of the Logger interface to automatically implement. Nick - To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org For additional commands, e-mail: log4j-dev-h...@logging.apache.org
Re: Using Custom Levels with a Custom/Wrapper Interface
It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the getClassName method? I think you misunderstand the purpose of the ClassT argument. It has nothing to do with the logger name--it's the class of the Logger interface to automatically implement. Nick - To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org For additional commands, e-mail: log4j-dev-h...@logging.apache.org
Re: Using Custom Levels with a Custom/Wrapper Interface
The JPA criteria API manages to generate a Foo_ class for the entity class Foo, and that seems to work out fine. On 26 January 2014 21:45, Nick Williams nicho...@nicholaswilliams.netwrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the getClassName method? I think you misunderstand the purpose of the ClassT argument. It has nothing to do with the logger name--it's the class of the Logger interface to automatically implement. Nick - 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
Re: Using Custom Levels with a Custom/Wrapper Interface
I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, “hello world”); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the getClassName method? I think you misunderstand the purpose of the ClassT argument. It has nothing to do with the logger name--it's the class of the Logger interface to automatically implement. Nick - To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org For additional commands, e-mail: log4j-dev-h...@logging.apache.org
Re: Using Custom Levels with a Custom/Wrapper Interface
I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, “hello world”); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.netjavascript:_e({}, 'cvml', 'nicho...@nicholaswilliams.net'); wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.netjavascript:_e({}, 'cvml', 'nicho...@nicholaswilliams.net'); wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the getClassName method? I think you misunderstand the purpose of the ClassT argument. It has nothing to do with the logger name--it's the class of the Logger interface to automatically implement. Nick - To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.orgjavascript:_e({}, 'cvml', 'log4j-dev-unsubscr...@logging.apache.org'); For additional commands, e-mail: log4j-dev-h...@logging.apache.orgjavascript:_e({}, 'cvml',
Re: Using Custom Levels with a Custom/Wrapper Interface
Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, “message); but even that requires “SomeClass” to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, “hello world”); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented again, and then instantiate/cache the logger instance like normal. Others have suggested deriving the level name from the method name instead of using an annotation. That's a viable alternative. Matt Sicker asked: And can't getCustomLogger also provide a default method that uses the
Re: Using Custom Levels with a Custom/Wrapper Interface
Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, String) T extends Logger T getCustomLogger(ClassT loggerClass, String, MessageFactory) The user can then obtain such a logger like so, etc.: MyLogger logger = LogManager.getCustomLogger(MyLogger.class); Log4j will generate an implementation of MyLogger that extends the default implementation, cache that implementation so that it doesn't have to be implemented
Re: Using Custom Levels with a Custom/Wrapper Interface
Sure, but what's wrong with the idea? The user provide their own interface, so that interface exists at compile time. The interface uses annotations, so it does not need to explicitly refer to a custom Level instance. Only the /implementation/ needs to know about the custom Level instances, and the implementation could be generated at runtime (I hope :-) ). On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, “message); but even that requires “SomeClass” to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, “hello world”); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [met
Re: Using Custom Levels with a Custom/Wrapper Interface
In addition to the above, we could provide a tool to generate a MyLogger interface with 14 method signatures for each custom Level name. This would be an offline tool that users would use only once. But this tool is optional... On Monday, January 27, 2014, Remko Popma remko.po...@gmail.com wrote: Sure, but what's wrong with the idea? The user provide their own interface, so that interface exists at compile time. The interface uses annotations, so it does not need to explicitly refer to a custom Level instance. Only the /implementation/ needs to know about the custom Level instances, and the implementation could be generated at runtime (I hope :-) ). On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, “message); but even that requires “SomeClass” to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, “hello world”); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypoth
Re: Using Custom Levels with a Custom/Wrapper Interface
Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams
Re: Using Custom Levels with a Custom/Wrapper Interface
Could we leverage Rhino? :) Scott On 1/26/14, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass, Object) T extends Logger T getCustomLogger(ClassT loggerClass, Object, MessageFactory) T extends Logger T
Re: Using Custom Levels with a Custom/Wrapper Interface
Of course, they'd have to use rhino, or something else...which doesn't help. Where's duck typing when you need it :) On 1/26/14, Scott Deboy scott.de...@gmail.com wrote: Could we leverage Rhino? :) Scott On 1/26/14, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...) void [methodName](String throwable) Each method /must/ be annotated with @LoggingLevel(name = levelName). Now LogManager has a few new methods: T extends Logger T getCustomLogger(ClassT loggerClass) T extends Logger T getCustomLogger(ClassT loggerClass, Class?) T extends Logger T getCustomLogger(ClassT loggerClass, Class?, MessageFactory) T extends Logger T getCustomLogger(ClassT loggerClass,
Re: Using Custom Levels with a Custom/Wrapper Interface
Yes, I was saying that. But, unless I'm misunderstanding, Scott doesn't want the user to even have to write the interface. He wants them to just configure it and the interface become available magically. I was pointing out that there's a disconnect between when the configuration is used (runtime) and when the user needs the interface (compile time). Unless we provide a code-generation tool for the user to run from the command line or from Ant/Maven/Gradle, they're going to have to write the interface themselves. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:49, Remko Popma remko.po...@gmail.com wrote: Nick, I thought that you meant that users would provide their own interface, like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } That way, this interface exists at compile time. On Monday, January 27, 2014, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams
Re: Using Custom Levels with a Custom/Wrapper Interface
If we go the run-once tool route, then you might as well use annotation processing. I think it would support everything necessary to generate the appropriate custom logger class. On 26 January 2014 23:00, Scott Deboy scott.de...@gmail.com wrote: Of course, they'd have to use rhino, or something else...which doesn't help. Where's duck typing when you need it :) On 1/26/14, Scott Deboy scott.de...@gmail.com wrote: Could we leverage Rhino? :) Scott On 1/26/14, Nicholas Williams nicho...@nicholaswilliams.net wrote: Scott, invokedynamic and javassist...those are all /runtime/ things. The user needs Logger#notice to be available at compile time. Those are not compatible. Nick Sent from my iPhone, so please forgive brief replies and frequent typos On Jan 26, 2014, at 22:37, Scott Deboy scott.de...@gmail.com wrote: Yes, I would like to declare in the config: Level: NOTICE, value: 232 And in Java code be able to use logger.notice(some message). But I think that'd require invokedynamic..which would probably require..javassist/ASM? I'd be ok with anything that's really close to that :) Scott On 1/26/14, Ralph Goers ralph.go...@dslextreme.com wrote: Scott would like users to add a level definition to the logging configuration and have everything else happen auto-magically. That would happen at run-time which is a bit late since the methods need to be available at compile time. I believe Scott said he would be fine if users had to do logger.log(SomeClass.SomeLevel, message); but even that requires SomeClass to be available at compile time. So what Scott says he would like and what Nick is proposing are two different things. Ralph On Jan 26, 2014, at 8:09 PM, Remko Popma remko.po...@gmail.com wrote: I actually thought that Nick's idea was the answer to that: users create a custom interface, something like this: public interface MyLogger extends Logger { @LoggingLevel(name=DIAG) void diag(String message); // optional other methods } They get an instance of this interface by calling: LogManager.getCustomLogger(MyLogger.class); LogManager has access to the processed configuration. The config has LevelsLevel name=DIAG intValue=450 elements. During configuration processing, the custom Level instances are created and registered, so on the first call to LogManager.getCustomLogger(MyLogger.class), the MyLogger instance is created and cached. Also, at this point the annotations are parsed to see what Level instance the MyLogger implementation will pass to the Logger.log(Level, String) method when the diag method is called. What is still open in this scenario is how the instance is created. Proxy? Or generate source compile? Or use a byte code library? On Monday, January 27, 2014, Ralph Goers ralph.go...@dslextreme.com wrote: I am going to have to echo what Nick said. If you can think of a way to make logger.log(SomeClass.SomeLevel, hello world); work without actually creating SomeClass then please share! Ralph On Jan 26, 2014, at 7:45 PM, Nick Williams nicho...@nicholaswilliams.net wrote: It would not be possible to do this strictly through configuration because the user needs a compiled interface to code against. Where is that compiled interface to come from? Nick On Jan 26, 2014, at 9:40 PM, Scott Deboy wrote: If there is a way to support this strictly through configuration that would be ideal. I'm trying to find a way to remove my request for additional built in levels but through configuration instead of adding them ourselves. Scott Scott On Jan 26, 2014 7:38 PM, Nick Williams nicho...@nicholaswilliams.net wrote: Here's a split-off thread for discussing how we can make using custom levels easier. Some on the team have expressed a desire to make it even easier. Given hypothetical custom levels DIAG and NOTE, the following would be nice to have: logger.note(message); logger.diag(message); etc. We're to discuss how best to approach this. My proposal (from previous email): Allow the user to define an interface that /must/ extend Logger. That interface may contain any methods that match the following signatures (the interface must have at least one method and there is no limit to the number of methods it may have): void [methodName](Marker, Message) void [methodName](Marker, Message, Throwable t) void [methodName](Marker, Object) void [methodName](Marker, Object, Throwable t) void [methodName](Marker, String) void [methodName](Marker, String, Object...) void [methodName](Marker, String throwable) void [methodName](Message) void [methodName](Message, Throwable t) void [methodName](Object) void [methodName](Object, Throwable t) void [methodName](String) void [methodName](String, Object...)