Release of SLF4J version 1.6.0-RC0

Hello all, I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes. As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest. Please refer to the the news page for precise details. http://www.slf4j.org/news.html You can download SLF4J, including full source code, class files and documentation on our download page, shown below. http://www.slf4j.org/download.html You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL. http://qos.ch/mailman/listinfo/announce -- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch

Thanks for implementing the fix for ticket 70! Cheers, Joern. On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Sure. I'd appreciate if you could give 1.6.0-RC0 a try. On 22/04/2010 2:17 PM, Joern Huxhorn wrote:
Thanks for implementing the fix for ticket 70!
Cheers, Joern.
On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already? I didn't have the time, yet, to check this myself. Please give me a short notice. I could likely check this on our project some time next week. Cheers, Joern. On 22.04.2010, at 20:21, Ceki Gülcü wrote:
Sure. I'd appreciate if you could give 1.6.0-RC0 a try.
On 22/04/2010 2:17 PM, Joern Huxhorn wrote:
Thanks for implementing the fix for ticket 70!
Cheers, Joern.
On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor. A new release of logback compatible with 1.6.0-RC0 will follow shortly.
I didn't have the time, yet, to check this myself. Please give me a short notice.
No pressure here.
I could likely check this on our project some time next week.
OK.
Cheers, Joern.
On 22.04.2010, at 20:21, Ceki Gülcü wrote:
Sure. I'd appreciate if you could give 1.6.0-RC0 a try.
On 22/04/2010 2:17 PM, Joern Huxhorn wrote:
Thanks for implementing the fix for ticket 70!
Cheers, Joern.
On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Hi Ceki, On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization? This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose. I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway. This wouldn't mean that varargs need to be added at this point.
A new release of logback compatible with 1.6.0-RC0 will follow shortly.
I didn't have the time, yet, to check this myself. Please give me a short notice.
No pressure here.
I could likely check this on our project some time next week.
OK.
Cheers, Joern.
On 22.04.2010, at 20:21, Ceki Gülcü wrote:
Sure. I'd appreciate if you could give 1.6.0-RC0 a try.
On 22/04/2010 2:17 PM, Joern Huxhorn wrote:
Thanks for implementing the fix for ticket 70!
Cheers, Joern.
On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Hi Joern, On 24/04/2010 1:55 AM, Joern Huxhorn wrote:
Hi Ceki,
On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization?
Yes, but ParameterizedMessage has to be passed as a Message to a logger of type org.slf4j.n.Logger. More below.
This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose.
I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway.
Initially, I also thought that 1.6.0 was a good time to integrate your changes. Changing the Logger interface breaks compatibility with client code using SLF4J. Breaking compatibility at this level is different than breaking compatibility within SLF4J internals. For example, as long as the end-user places slf4j-api-1.6.0.jar and an appropriate 1.6.0 binding on the class path, things will work fine without needing to compile client code or dependencies. However, if the logger interface was changed, then *all* client code (including all dependencies using SLF4J) would need to be recompiled. There is no comparison in the impact of changing SLF4J internals and changing client-facing interfaces such as org.slf4j.Logger. -- Ceki

Hi Ceki, On 24.04.2010, at 13:44, Ceki Gülcü <ceki@qos.ch> wrote:
Hi Joern,
On 24/04/2010 1:55 AM, Joern Huxhorn wrote:
Hi Ceki,
On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization?
Yes, but ParameterizedMessage has to be passed as a Message to a logger of type org.slf4j.n.Logger. More below.
This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose.
I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway.
Initially, I also thought that 1.6.0 was a good time to integrate your changes.
Changing the Logger interface breaks compatibility with client code using SLF4J. Breaking compatibility at this level is different than breaking compatibility within SLF4J internals. For example, as long as the end-user places slf4j-api-1.6.0.jar and an appropriate 1.6.0 binding on the class path, things will work fine without needing to compile client code or dependencies. However, if the logger interface was changed, then *all* client code (including all dependencies using SLF4J) would need to be recompiled. There is no comparison in the impact of changing SLF4J internals and changing client-facing interfaces such as org.slf4j.Logger.
Yes, I understand. That's why I'd suggest to add Message-aware methods, leaving the existing ones untouched. One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported. Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods] The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;) The big advantage, on the other hand, would be that the interfaces would indeed stay compile-time compatible. I only dropped this requirement since I assumed that the original slf4j-api was absolutely frozen (and I still think that it would be a good idea to keep it that way...). slf4j-api and slf4j-n-api were meant to exist side by side, with the former ensuring backwards-compatibility with Java 1.4 and slf4j-n-api for explicit opt-in to new functionality by changing the imports of Logger and LoggerFactory. If the preconditions of a frozen slf4j-api and Java 1.4 compatibility have been dropped, there's no use for a separate slf4j-n-api and we can simply extend the current Logger interface with the previously mentioned methods. Cheers, Joern.

I believe(d) that adding methods to an interface impacts client code using interface beyond the actual implementations of the interface. This is not true in simple cases, for example when the implementations change in accordance with changes in the interface. Obviously, when client code uses a newly added method in an interface and the interface available on the class path is older, one is sure to run into problems. I wonder if there are other problematic cases. If my assumption about new methods impacting client code is wrong, then that opens new possibilities. On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
Hi Ceki,
On 24.04.2010, at 13:44, Ceki Gülcü <ceki@qos.ch> wrote:
Hi Joern,
On 24/04/2010 1:55 AM, Joern Huxhorn wrote:
Hi Ceki,
On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization?
Yes, but ParameterizedMessage has to be passed as a Message to a logger of type org.slf4j.n.Logger. More below.
This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose.
I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway.
Initially, I also thought that 1.6.0 was a good time to integrate your changes.
Changing the Logger interface breaks compatibility with client code using SLF4J. Breaking compatibility at this level is different than breaking compatibility within SLF4J internals. For example, as long as the end-user places slf4j-api-1.6.0.jar and an appropriate 1.6.0 binding on the class path, things will work fine without needing to compile client code or dependencies. However, if the logger interface was changed, then *all* client code (including all dependencies using SLF4J) would need to be recompiled. There is no comparison in the impact of changing SLF4J internals and changing client-facing interfaces such as org.slf4j.Logger.
Yes, I understand. That's why I'd suggest to add Message-aware methods, leaving the existing ones untouched.
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
The big advantage, on the other hand, would be that the interfaces would indeed stay compile-time compatible.
I only dropped this requirement since I assumed that the original slf4j-api was absolutely frozen (and I still think that it would be a good idea to keep it that way...).
slf4j-api and slf4j-n-api were meant to exist side by side, with the former ensuring backwards-compatibility with Java 1.4 and slf4j-n-api for explicit opt-in to new functionality by changing the imports of Logger and LoggerFactory.
If the preconditions of a frozen slf4j-api and Java 1.4 compatibility have been dropped, there's no use for a separate slf4j-n-api and we can simply extend the current Logger interface with the previously mentioned methods.
Cheers, Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Adding new methods to an interface only affects implementations of that interface, not clients of the interface. So backward compatibility is maintained. This is exactly why I chose to implement the Message support the way I did. Complete backward compatibility is maintained. I believe Joern's implementation is a little different and has a bigger impact as it provides a how new api layer instead of extending the existing interface. IIRC Joern didn't go so far as to modify all the logback internals where the Logback in my fork does. Ralph On Apr 24, 2010, at 7:50 AM, Ceki Gülcü wrote:
I believe(d) that adding methods to an interface impacts client code using interface beyond the actual implementations of the interface. This is not true in simple cases, for example when the implementations change in accordance with changes in the interface. Obviously, when client code uses a newly added method in an interface and the interface available on the class path is older, one is sure to run into problems. I wonder if there are other problematic cases.
If my assumption about new methods impacting client code is wrong, then that opens new possibilities.
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
Hi Ceki,
On 24.04.2010, at 13:44, Ceki Gülcü <ceki@qos.ch> wrote:
Hi Joern,
On 24/04/2010 1:55 AM, Joern Huxhorn wrote:
Hi Ceki,
On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization?
Yes, but ParameterizedMessage has to be passed as a Message to a logger of type org.slf4j.n.Logger. More below.
This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose.
I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway.
Initially, I also thought that 1.6.0 was a good time to integrate your changes.
Changing the Logger interface breaks compatibility with client code using SLF4J. Breaking compatibility at this level is different than breaking compatibility within SLF4J internals. For example, as long as the end-user places slf4j-api-1.6.0.jar and an appropriate 1.6.0 binding on the class path, things will work fine without needing to compile client code or dependencies. However, if the logger interface was changed, then *all* client code (including all dependencies using SLF4J) would need to be recompiled. There is no comparison in the impact of changing SLF4J internals and changing client-facing interfaces such as org.slf4j.Logger.
Yes, I understand. That's why I'd suggest to add Message-aware methods, leaving the existing ones untouched.
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
The big advantage, on the other hand, would be that the interfaces would indeed stay compile-time compatible.
I only dropped this requirement since I assumed that the original slf4j-api was absolutely frozen (and I still think that it would be a good idea to keep it that way...).
slf4j-api and slf4j-n-api were meant to exist side by side, with the former ensuring backwards-compatibility with Java 1.4 and slf4j-n-api for explicit opt-in to new functionality by changing the imports of Logger and LoggerFactory.
If the preconditions of a frozen slf4j-api and Java 1.4 compatibility have been dropped, there's no use for a separate slf4j-n-api and we can simply extend the current Logger interface with the previously mentioned methods.
Cheers, Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Hi guys, On 24.04.2010, at 18:00, Ralph Goers <rgoers@apache.org> wrote:
Adding new methods to an interface only affects implementations of that interface, not clients of the interface. So backward compatibility is maintained.
Yes, this is the case. Clients compiled against the original interface will work using the extended one.
This is exactly why I chose to implement the Message support the way I did. Complete backward compatibility is maintained. I believe Joern's implementation is a little different and has a bigger impact as it provides a how new api layer instead of extending the existing interface.
Indeed. I developed it with the assumption that the Logger interface must not be extended under any circumstance.
IIRC Joern didn't go so far as to modify all the logback internals where the Logback in my fork does.
Yes, I didn't touch Logback at all. slf4j-n-api was meant as a base of further discussion. Your findings concerning performance invalidated my reduction of the interface, though I have one remaining headache: I'd really, really like to have generic methods that log with a Level parameter. This would be an ideal place for an enum but we are still staying 1.4 for now, right? If we are not using an enum now, we won't be able to switch to an enum at a later time without breaking binary compatibility. Because of that, I'd suggest to keep those generic log methods out of the interface until we actually switch to 1.5 altogether - even though I have several places where I could use that functionality very well! Cheers & have a nice weekend, Joern.
Ralph
On Apr 24, 2010, at 7:50 AM, Ceki Gülcü wrote:
I believe(d) that adding methods to an interface impacts client code using interface beyond the actual implementations of the interface. This is not true in simple cases, for example when the implementations change in accordance with changes in the interface. Obviously, when client code uses a newly added method in an interface and the interface available on the class path is older, one is sure to run into problems. I wonder if there are other problematic cases.
If my assumption about new methods impacting client code is wrong, then that opens new possibilities.
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
Hi Ceki,
On 24.04.2010, at 13:44, Ceki Gülcü <ceki@qos.ch> wrote:
Hi Joern,
On 24/04/2010 1:55 AM, Joern Huxhorn wrote:
Hi Ceki,
On 23.04.2010, at 16:18, Ceki Gülcü <ceki@qos.ch> wrote:
On 23/04/2010 2:14 PM, Joern Huxhorn wrote: > Didn't you change LocationAwareLogger and doesn't that mean > that it's > not compatible with the current Logback anymore? > Or is this only relevant for wrappers like jcl-over-slf4j and > you > changed all of them already?
Right, LocationAwareLogger affects compatibility with logback but so does MessageFormatter changes, actually the latter in a deeper way as it is no longer possible to compute the formatted message lazily in LoggingEvent. I must be computed eagerly in LoggingEvent's constructor.
Are you aware that my ParameterizedMessage supports both #70 and lazy initialization?
Yes, but ParameterizedMessage has to be passed as a Message to a logger of type org.slf4j.n.Logger. More below.
This is possible since the placeholders are only counted during creation. The actual formatting/placeholder replacement is only performed when the formatted message is requested. The formatted message is kept so it won't be regenerated in case of further calls. It could also be enhanced to perform the toString of the arguments at a later time. The Message interface could be extended by an prepareForDeferredProcessing() method for that purpose.
I agree with Ralph that this would be a good time to extend the Logger interface with Message-aware methods since 1.6 will be incompatible anyway.
Initially, I also thought that 1.6.0 was a good time to integrate your changes.
Changing the Logger interface breaks compatibility with client code using SLF4J. Breaking compatibility at this level is different than breaking compatibility within SLF4J internals. For example, as long as the end-user places slf4j-api-1.6.0.jar and an appropriate 1.6.0 binding on the class path, things will work fine without needing to compile client code or dependencies. However, if the logger interface was changed, then *all* client code (including all dependencies using SLF4J) would need to be recompiled. There is no comparison in the impact of changing SLF4J internals and changing client-facing interfaces such as org.slf4j.Logger.
Yes, I understand. That's why I'd suggest to add Message-aware methods, leaving the existing ones untouched.
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
The big advantage, on the other hand, would be that the interfaces would indeed stay compile-time compatible.
I only dropped this requirement since I assumed that the original slf4j-api was absolutely frozen (and I still think that it would be a good idea to keep it that way...).
slf4j-api and slf4j-n-api were meant to exist side by side, with the former ensuring backwards-compatibility with Java 1.4 and slf4j-n- api for explicit opt-in to new functionality by changing the imports of Logger and LoggerFactory.
If the preconditions of a frozen slf4j-api and Java 1.4 compatibility have been dropped, there's no use for a separate slf4j-n-api and we can simply extend the current Logger interface with the previously mentioned methods.
Cheers, Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 24 Apr 2010, at 21:05, Joern Huxhorn wrote:
I have one remaining headache: I'd really, really like to have generic methods that log with a Level parameter. This would be an ideal place for an enum but we are still staying 1.4 for now, right? If we are not using an enum now, we won't be able to switch to an enum at a later time without breaking binary compatibility. Because of that, I'd suggest to keep those generic log methods out of the interface until we actually switch to 1.5 altogether - even though I have several places where I could use that functionality very well!
I needed exactly this for the sysout-over-slf4j module that I'm hoping will be included as an SLF4J bridging module. Ceki told me that it was acceptable for an extension module to require Java 5. I've written an implementation here: http://github.com/Mahoney/slf4j/blob/master/sysout-over-slf4j/src/main/java/... Nothing very brilliant, you've probably written it yourself already. And it only does the two methods I needed. Still, thought it might be worth mentioning. Rob

On 24.04.2010, at 23:09, Robert Elliot wrote:
On 24 Apr 2010, at 21:05, Joern Huxhorn wrote:
I have one remaining headache: I'd really, really like to have generic methods that log with a Level parameter. This would be an ideal place for an enum but we are still staying 1.4 for now, right? If we are not using an enum now, we won't be able to switch to an enum at a later time without breaking binary compatibility. Because of that, I'd suggest to keep those generic log methods out of the interface until we actually switch to 1.5 altogether - even though I have several places where I could use that functionality very well!
I needed exactly this for the sysout-over-slf4j module that I'm hoping will be included as an SLF4J bridging module. Ceki told me that it was acceptable for an extension module to require Java 5. I've written an implementation here: http://github.com/Mahoney/slf4j/blob/master/sysout-over-slf4j/src/main/java/...
Nothing very brilliant, you've probably written it yourself already. And it only does the two methods I needed. Still, thought it might be worth mentioning.
I've just taken a look at your code and I think it's quite neat. I've implemented it the other way around (i.e. a log-methods that receive a dumb Level enum) but I kind of like your idea of putting the log-methods into the enum instead. This leaves the Logger interface alone, which is nice. It should be extended by the various other methods, including isEnabled(Logger)/isEnabled(Logger, Marker), though - as you said. Thanks for letting me know, Joern.

I've just taken a look at your code and I think it's quite neat. I've implemented it the other way around (i.e. a log-methods that receive a dumb Level enum) but I kind of like your idea of putting the log-methods into the enum instead. This leaves the Logger interface alone, which is nice. It should be extended by the various other methods, including isEnabled(Logger)/isEnabled(Logger, Marker), though - as you said.
Thanks for letting me know, Joern.
Thanks - actually I'd prefer your way, it seems more natural, but I could do this way without needing a change. I've just tidied it up using reflection to make it much easier to add the missing methods, though probably at a small performance cost. Rob

On 25.04.2010, at 09:44, Robert Elliot wrote:
I've just taken a look at your code and I think it's quite neat. I've implemented it the other way around (i.e. a log-methods that receive a dumb Level enum) but I kind of like your idea of putting the log-methods into the enum instead. This leaves the Logger interface alone, which is nice. It should be extended by the various other methods, including isEnabled(Logger)/isEnabled(Logger, Marker), though - as you said.
Thanks for letting me know, Joern.
Thanks - actually I'd prefer your way, it seems more natural, but I could do this way without needing a change.
I'd also appreciate an extended Logger interface containing those log-methods, but only while switching to Java 1.5. This doesn't mean that the enum shouldn't be built like you suggested. Having both ways of logging would probably be nice, but I'm a bit torn here.
I've just tidied it up using reflection to make it much easier to add the missing methods, though probably at a small performance cost.
It would perform better if you performed the method lookups in the c'tor, keeping references to the Method objects. I wouldn't use reflection here, though, just because every bit of performance is relevant in case of logging. Cheers, Joern.

On 25 Apr 2010, at 13:24, Joern Huxhorn wrote:
I've just tidied it up using reflection to make it much easier to add the missing methods, though probably at a small performance cost.
It would perform better if you performed the method lookups in the c'tor, keeping references to the Method objects. I wouldn't use reflection here, though, just because every bit of performance is relevant in case of logging.
Just did some micro bench-marking, and was rather horrified - even with the method references cached in the constructor a call to is<Level>Enabled via reflection takes between 5 and 8 times longer than a direct call on the object in 64-bit Java 6 on a Mac, depending on how often the retrieved value is changed. So I'll revert that tidy-up - in general I prefer clean code to high performing code, but that's too high a price to pay. I thought reflection was meant to be pretty good now-a-days! Shame, as it's much less verbose and much less repetitive, and consequently it would be much easier to add the missing methods. Almost makes me interested in a code generation step.

Hi All, To emphasize the still experimental nature of 1.6.0, I made a new release changing suffix from RC to alpha. This will give us more wiggle room. More below. On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant? Here is an idea: try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); } This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
The big advantage, on the other hand, would be that the interfaces would indeed stay compile-time compatible.
I only dropped this requirement since I assumed that the original slf4j-api was absolutely frozen (and I still think that it would be a good idea to keep it that way...).
slf4j-api and slf4j-n-api were meant to exist side by side, with the former ensuring backwards-compatibility with Java 1.4 and slf4j-n-api for explicit opt-in to new functionality by changing the imports of Logger and LoggerFactory.
If the preconditions of a frozen slf4j-api and Java 1.4 compatibility have been dropped, there's no use for a separate slf4j-n-api and we can simply extend the current Logger interface with the previously mentioned methods.
If the Logger interface was frozen, then slf4j-n-api was the only possibility to evolve the API.
Cheers, Joern.

On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it. http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations) I see that interface as the main extension point of SLF4J & Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything. We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible. This would reduce the interface to debug(Message) and debug(Marker, Message), at least. How about interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); } in addition? My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods... Joern.

In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable. Ralph On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J & Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data. Example 1) Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event); Example 2) Event event = new Event(structredData).add(throwable); logger.error(event); On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do: LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event); The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. For example, for auditing I can do: public class AuditMessage extends StructuredDataMessage { public AuditMessage(String msg, String type) { super("Audit@12345", msg); } } public class LogiinMessage extends AuditMessage { public LoginMessage(String userId) { super("Login Succeeded", "Login"); put("userid", userid); } } For an Object such as a HashMap I could do: public HashMapMessage<K,V> implements Message { private Map<K,V> map; public HashMapMessage(Map<K,V> map) { this.map = map; } public String getFormattedMessage() { StringBuilder sb = new StringBuidler(); for (Map.Entry<K,V> entry : map.entrySet()) { if (sb.size() > 0) { insertPad(sb); } formatEntry(Map.Entry<K,V> entry, sb); } } protected void insertPad(StringBuilder sb) { sb.append(" "); } protected void formatEntry(Map.Entry<K,V> entry, StringBuilder sb) { sb.append(entry.getKey()).append("="),append(entry.getValue()); } } which now allows me to easily override this if I just want to change how each entry is formatted. And so on. Ralph On Apr 26, 2010, at 1:08 PM, Ceki Gülcü wrote:
OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data.
Example 1)
Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event);
Example 2)
Event event = new Event(structredData).add(throwable); logger.error(event);
On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 27.04.2010, at 06:38, Ralph Goers wrote:
Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do:
LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event);
The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it.
Exactly. The actual Message implementation could be as complex as necessary to accomplish the required task but it would still be required to implement the minimal lazy-init-caching-toString defined in the Message contract. The big payoff will not show up in SLF4J but mainly in Logback where it would be possible to add explicit support for certain message implementations to custom appenders while out-of-the-box appenders would handle all Message instances similar to a String. I'd imagine to define a small set of "standard" message implementations, guaranteeing that they are available if slf4j-api is available on the classpath. In case of SocketAppender, those standard messages could be serialized as they are while others would need to be converted into String. Another benefit would be the ability to filter based on the actual class of the Message instance, e.g. only consume Messages of type AuditMessage or ignore all Messages of type TraceMessage (<= this is just an example but could be reasonable). Cheers, Joern.

Ralph, Thanks for sharing these different use cases. I agree with you that adding a throwable or a marker to a message does not necessarily make sense. And yes, we could do LogEvent event = new LogEvent(Level.ERROR,msg).addParam("p0").add(t); logger.log(event); I also understand that the point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. There are two distinct problems here. One is the encapsulation of various logging attributes (Marker, Level etc) and the other is the structure of messages. The encapsulation problem is important because I don't want to add 5x4 new methods to the Logger interface multiplied by the 6 implementations. We can't just keep adding methods to the Logger interface. Hence the idea of LoggingEvent. Given that a Message is data point in LoggingEvent, you can still write: LoggingEvent event = new LoggingEvent(new AuditMessage("msg0", "type0"))).add(t); logger.error(event); or LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event); On 27/04/2010 6:38 AM, Ralph Goers wrote:
Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do:
LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event);
The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. For example, for auditing I can do:
public class AuditMessage extends StructuredDataMessage { public AuditMessage(String msg, String type) { super("Audit@12345", msg); } }
public class LogiinMessage extends AuditMessage { public LoginMessage(String userId) { super("Login Succeeded", "Login"); put("userid", userid); } }
For an Object such as a HashMap I could do:
public HashMapMessage<K,V> implements Message { private Map<K,V> map;
public HashMapMessage(Map<K,V> map) { this.map = map; }
public String getFormattedMessage() { StringBuilder sb = new StringBuidler(); for (Map.Entry<K,V> entry : map.entrySet()) { if (sb.size()> 0) { insertPad(sb); } formatEntry(Map.Entry<K,V> entry, sb); } } protected void insertPad(StringBuilder sb) { sb.append(" "); } protected void formatEntry(Map.Entry<K,V> entry, StringBuilder sb) { sb.append(entry.getKey()).append("="),append(entry.getValue()); } }
which now allows me to easily override this if I just want to change how each entry is formatted.
And so on.
Ralph
On Apr 26, 2010, at 1:08 PM, Ceki Gülcü wrote:
OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data.
Example 1)
Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event);
Example 2)
Event event = new Event(structredData).add(throwable); logger.error(event);
On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
One of my goals in slf4j-n was to reduce the number of methods in the Logger interface. This was seemingly a bad idea since it would have a performance impact, in the case where a message isn't actually logged, as Ralph reported.
Because of this, it would be very wise to keep all the methods that are already present in the Logger interface and simply add debug(Message) debug(Message, Throwable) debug(Marker, Message) debug(Marker, Message, Throwable) [same for other levels plus generic log(Level, ...)-methods]
The designer in me doesn't like the "bloated" (in the sense that some methods could be dropped without losing functionality) interface, but the realist in me accepts that performance is more important than aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern.

In my previous message I forgot to ask whether there was interest in the LoggingEvent idea and if so, willingness to contibute code and/or to experiment with the results. On 27/04/2010 1:04 PM, Ceki Gülcü wrote:
Ralph,
Thanks for sharing these different use cases. I agree with you that adding a throwable or a marker to a message does not necessarily make sense. And yes, we could do
LogEvent event = new LogEvent(Level.ERROR,msg).addParam("p0").add(t); logger.log(event);
I also understand that the point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it.
There are two distinct problems here. One is the encapsulation of various logging attributes (Marker, Level etc) and the other is the structure of messages.
The encapsulation problem is important because I don't want to add 5x4 new methods to the Logger interface multiplied by the 6 implementations. We can't just keep adding methods to the Logger interface. Hence the idea of LoggingEvent. Given that a Message is data point in LoggingEvent, you can still write:
LoggingEvent event = new LoggingEvent(new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
or
LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
On 27/04/2010 6:38 AM, Ralph Goers wrote:
Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do:
LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event);
The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. For example, for auditing I can do:
public class AuditMessage extends StructuredDataMessage { public AuditMessage(String msg, String type) { super("Audit@12345", msg); } }
public class LogiinMessage extends AuditMessage { public LoginMessage(String userId) { super("Login Succeeded", "Login"); put("userid", userid); } }
For an Object such as a HashMap I could do:
public HashMapMessage<K,V> implements Message { private Map<K,V> map;
public HashMapMessage(Map<K,V> map) { this.map = map; }
public String getFormattedMessage() { StringBuilder sb = new StringBuidler(); for (Map.Entry<K,V> entry : map.entrySet()) { if (sb.size()> 0) { insertPad(sb); } formatEntry(Map.Entry<K,V> entry, sb); } } protected void insertPad(StringBuilder sb) { sb.append(" "); } protected void formatEntry(Map.Entry<K,V> entry, StringBuilder sb) { sb.append(entry.getKey()).append("="),append(entry.getValue()); } }
which now allows me to easily override this if I just want to change how each entry is formatted.
And so on.
Ralph
On Apr 26, 2010, at 1:08 PM, Ceki Gülcü wrote:
OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data.
Example 1)
Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event);
Example 2)
Event event = new Event(structredData).add(throwable); logger.error(event);
On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
> One of my goals in slf4j-n was to reduce the number of methods in > the > Logger interface. > This was seemingly a bad idea since it would have a performance > impact, > in the case where a message isn't actually logged, as Ralph > reported. > > Because of this, it would be very wise to keep all the methods > that are > already present in the Logger interface and simply add > debug(Message) > debug(Message, Throwable) > debug(Marker, Message) > debug(Marker, Message, Throwable) > [same for other levels plus generic log(Level, ...)-methods] > > The designer in me doesn't like the "bloated" (in the sense that > some > methods could be dropped without losing functionality) interface, > but > the realist in me accepts that performance is more important than > aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern.
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

Should the lack of response to my LoggingEvent proposal be interpreted as approval or as resignation? On 27/04/2010 1:04 PM, Ceki Gülcü wrote:
Ralph,
Thanks for sharing these different use cases. I agree with you that adding a throwable or a marker to a message does not necessarily make sense. And yes, we could do
LogEvent event = new LogEvent(Level.ERROR,msg).addParam("p0").add(t); logger.log(event);
I also understand that the point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it.
There are two distinct problems here. One is the encapsulation of various logging attributes (Marker, Level etc) and the other is the structure of messages.
The encapsulation problem is important because I don't want to add 5x4 new methods to the Logger interface multiplied by the 6 implementations. We can't just keep adding methods to the Logger interface. Hence the idea of LoggingEvent. Given that a Message is data point in LoggingEvent, you can still write:
LoggingEvent event = new LoggingEvent(new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
or
LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
On 27/04/2010 6:38 AM, Ralph Goers wrote:
Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do:
LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event);
The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. For example, for auditing I can do:
public class AuditMessage extends StructuredDataMessage { public AuditMessage(String msg, String type) { super("Audit@12345", msg); } }
public class LogiinMessage extends AuditMessage { public LoginMessage(String userId) { super("Login Succeeded", "Login"); put("userid", userid); } }
For an Object such as a HashMap I could do:
public HashMapMessage<K,V> implements Message { private Map<K,V> map;
public HashMapMessage(Map<K,V> map) { this.map = map; }
public String getFormattedMessage() { StringBuilder sb = new StringBuidler(); for (Map.Entry<K,V> entry : map.entrySet()) { if (sb.size()> 0) { insertPad(sb); } formatEntry(Map.Entry<K,V> entry, sb); } } protected void insertPad(StringBuilder sb) { sb.append(" "); } protected void formatEntry(Map.Entry<K,V> entry, StringBuilder sb) { sb.append(entry.getKey()).append("="),append(entry.getValue()); } }
which now allows me to easily override this if I just want to change how each entry is formatted.
And so on.
Ralph
On Apr 26, 2010, at 1:08 PM, Ceki Gülcü wrote:
OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data.
Example 1)
Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event);
Example 2)
Event event = new Event(structredData).add(throwable); logger.error(event);
On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
On 24/04/2010 3:55 PM, Joern Huxhorn wrote:
> One of my goals in slf4j-n was to reduce the number of methods in > the > Logger interface. > This was seemingly a bad idea since it would have a performance > impact, > in the case where a message isn't actually logged, as Ralph > reported. > > Because of this, it would be very wise to keep all the methods > that are > already present in the Logger interface and simply add > debug(Message) > debug(Message, Throwable) > debug(Marker, Message) > debug(Marker, Message, Throwable) > [same for other levels plus generic log(Level, ...)-methods] > > The designer in me doesn't like the "bloated" (in the sense that > some > methods could be dropped without losing functionality) interface, > but > the realist in me accepts that performance is more important than > aesthetics ;)
Can't we coalesce debug(Message), debug(Message, Throwable), debug(Marker, Message) and debug(Marker, Message, Throwable) into a single variant?
Here is an idea:
try { ... } catch(Throwable t) { Message m = new Message("hello{}").addParam("word").add(marker).add(t); logger.error(m); }
This approach incurs the cost of creating and building the Message object regardless of whether the request will be logged or not. I suspect that the bulk of the cost is due to the object creation incurred by new Message(...) and not due to the addition of extra data incurred in calling addParam() and the other add() methods. Thus, performance-wise we are in the same position as the original Message proposal but now we can get rid of all the overloaded variants dealing with Marker and throwable.
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern.

Generally when I don't reply it isn't because I love the idea. I can't think of any use cases where I'd want to construct a logging event that way. It also would seem that you would be taking what is currently a structure private to Logback and making it public as it would make no sense for SLF4J to have one LoggingEvent and Logback to have another. In short, it doesn't really solve what Joern and I have been looking for with support for the Message and doesn't provide much value that I can see. Ralph On Apr 30, 2010, at 6:15 AM, Ceki Gülcü wrote:
Should the lack of response to my LoggingEvent proposal be interpreted as approval or as resignation?
On 27/04/2010 1:04 PM, Ceki Gülcü wrote:
Ralph,
Thanks for sharing these different use cases. I agree with you that adding a throwable or a marker to a message does not necessarily make sense. And yes, we could do
LogEvent event = new LogEvent(Level.ERROR,msg).addParam("p0").add(t); logger.log(event);
I also understand that the point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it.
There are two distinct problems here. One is the encapsulation of various logging attributes (Marker, Level etc) and the other is the structure of messages.
The encapsulation problem is important because I don't want to add 5x4 new methods to the Logger interface multiplied by the 6 implementations. We can't just keep adding methods to the Logger interface. Hence the idea of LoggingEvent. Given that a Message is data point in LoggingEvent, you can still write:
LoggingEvent event = new LoggingEvent(new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
or
LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
On 27/04/2010 6:38 AM, Ralph Goers wrote:
Adding a throwable to an event with structured data makes no sense. The throwable, or components of it, would be part of the structured data. To do what you are suggesting you might as well do:
LogEvent event = new LogEvent(Level.ERROR, msg).addParam("parm1").addThrowable(t); logger.log(event);
The point of the Message interface is to provide a bit more structure around the message, not encapsulate all the logging attributes such as the Marker, Level, etc into it. For example, for auditing I can do:
public class AuditMessage extends StructuredDataMessage { public AuditMessage(String msg, String type) { super("Audit@12345", msg); } }
public class LogiinMessage extends AuditMessage { public LoginMessage(String userId) { super("Login Succeeded", "Login"); put("userid", userid); } }
For an Object such as a HashMap I could do:
public HashMapMessage<K,V> implements Message { private Map<K,V> map;
public HashMapMessage(Map<K,V> map) { this.map = map; }
public String getFormattedMessage() { StringBuilder sb = new StringBuidler(); for (Map.Entry<K,V> entry : map.entrySet()) { if (sb.size()> 0) { insertPad(sb); } formatEntry(Map.Entry<K,V> entry, sb); } } protected void insertPad(StringBuilder sb) { sb.append(" "); } protected void formatEntry(Map.Entry<K,V> entry, StringBuilder sb) { sb.append(entry.getKey()).append("="),append(entry.getValue()); } }
which now allows me to easily override this if I just want to change how each entry is formatted.
And so on.
Ralph
On Apr 26, 2010, at 1:08 PM, Ceki Gülcü wrote:
OK. Instead of passing a Message, one would pass an Event which would contain a Message as well as optionally other data.
Example 1)
Event event = new Event("hello {}").addParam("world").add(marker); logger.error(event);
Example 2)
Event event = new Event(structredData).add(throwable); logger.error(event);
On 26/04/2010 3:38 PM, Ralph Goers wrote:
In general, I agree with Joern. The point of the Message interface is to make it easy to create all different kinds of Messages. A StructuredData message really has no good way to implement addParam() or event addThrowable.
Ralph
On Apr 26, 2010, at 5:09 AM, Joern Huxhorn wrote:
On 26.04.2010, at 10:58, Ceki Gülcü wrote:
> On 24/04/2010 3:55 PM, Joern Huxhorn wrote: > >> One of my goals in slf4j-n was to reduce the number of methods in >> the >> Logger interface. >> This was seemingly a bad idea since it would have a performance >> impact, >> in the case where a message isn't actually logged, as Ralph >> reported. >> >> Because of this, it would be very wise to keep all the methods >> that are >> already present in the Logger interface and simply add >> debug(Message) >> debug(Message, Throwable) >> debug(Marker, Message) >> debug(Marker, Message, Throwable) >> [same for other levels plus generic log(Level, ...)-methods] >> >> The designer in me doesn't like the "bloated" (in the sense that >> some >> methods could be dropped without losing functionality) interface, >> but >> the realist in me accepts that performance is more important than >> aesthetics ;) > > Can't we coalesce debug(Message), debug(Message, Throwable), > debug(Marker, Message) and debug(Marker, Message, Throwable) into > a single variant? > > Here is an idea: > > try { > ... > } catch(Throwable t) { > Message m = new > Message("hello{}").addParam("word").add(marker).add(t); > logger.error(m); > } > > This approach incurs the cost of creating and building the Message > object regardless of whether the request will be logged or not. I > suspect that the bulk of the cost is due to the object creation > incurred by new Message(...) and not due to the addition of extra > data incurred in calling addParam() and the other add() methods. > Thus, performance-wise we are in the same position as the original > Message proposal but now we can get rid of all the overloaded > variants dealing with Marker and throwable. >
Well, the idea of the Message interface (!) was to enable lazy initialization (in contrast to a simply toString) without adding too many additional requirements to it.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
It would be much more work to implement a custom Message with the suggestion above. More work implies more chances of faulty implementation, for example in case of Marker support. The addParam() method would be quite a mistake, too, since a different implementation of Message might use key/value-pairs as parameters. (This is something that I'd really like to do since it would also enable easier translations)
I see that interface as the main extension point of SLF4J& Logback. The Message instance is supposed to end up in the appenders in case of Logback, so custom appenders could handle custom Message implementations in arbitrary ways without having to parse anything.
We could, however, add the Throwable to the Message interface. I left it out of the interface and added it only to the ParameterizedMessage implementation to keep the interface as clean as possible.
This would reduce the interface to debug(Message) and debug(Marker, Message), at least.
How about
interface Message { Message set(Throwable); // instead of setThrowable to be more concise? Throwable getThrowable(); }
in addition?
My problem is: I'm not really sure if I'll like this while using it ;) Regardless of the way we'll implement it in the Message, it will always be less concise (concerning both brevity and readability) than code using the four methods...
Joern.
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 30/04/2010 3:24 PM, Ralph Goers wrote:
Generally when I don't reply it isn't because I love the idea.
I gathered as much.
I can't think of any use cases where I'd want to construct a logging event that way. It also would seem that you would be taking what is currently a structure private to Logback and making it public as it would make no sense for SLF4J to have one LoggingEvent and Logback to have another.
Building a LoggingEvent prior to calling org.slf4j.Logger avoids adding new methods to the Logger interface in order to keep it sane.
In short, it doesn't really solve what Joern and I have been looking for with support for the Message and doesn't provide much value that I can see.
Given that it solves the method population explosion problem, LoggingEvent can be considered as a prerequisite to to the addition of the Message interface. -- Ceki

On 30.04.2010, at 16:04, Ceki Gülcü wrote:
On 30/04/2010 3:24 PM, Ralph Goers wrote:
Generally when I don't reply it isn't because I love the idea.
I gathered as much.
In my case, it was a mixture of not enough spare-time and not really knowing what I should reply. I was also a bit confused by your examples: LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event); I guess you mean logger.log instead of logger.error. Otherwise I'd be quite interested what the framework should do if the given event wasn't of Level.ERROR. Beside that, I'm very hesitant about adding Level at this point (read: I think it's a very bad idea), anyway, since the proper way of implementing it would be an enum but we need to stay 1.4-compatible for now, right? If we introduce it now then we won't be able to change it into an enum later on. That's why I would NOT add Level to SLF4J right now. I'd do this during the big 1.5 update sometime in the future.
I can't think of any use cases where I'd want to construct a logging event that way. It also would seem that you would be taking what is currently a structure private to Logback and making it public as it would make no sense for SLF4J to have one LoggingEvent and Logback to have another.
Building a LoggingEvent prior to calling org.slf4j.Logger avoids adding new methods to the Logger interface in order to keep it sane.
First of all, I agree with everything Ralph said above. Using the suggested methods would create illegible logging calls, at least in my opinion, since they differ quite much from the current signature. The current methods enforce a certain order which won't be the case anymore in case of LoggingEvent. Concerning the insanity, I'd rather be lazy while using the Logger than while implementing it. (Concerning the performance info at http://bugzilla.slf4j.org/show_bug.cgi?id=31#c82 I'd even consider adding methods with 4 and 5 arguments :p) This means I'd happily perform the monkey-work of implementing 4*5(LogLevels)*6(SLF4J-implementations) methods but I'm not really interested very much in Logger.log(LoggerEvent)
In short, it doesn't really solve what Joern and I have been looking for with support for the Message and doesn't provide much value that I can see.
Given that it solves the method population explosion problem, LoggingEvent can be considered as a prerequisite to to the addition of the Message interface.
I don't agree that this problem is a problem at all. If it's simply a matter of the amount of work that'll be required then I'd be happy to help out. If that's not the issue then please explain it to me. Joern.

Hi, On Fri, Apr 30, 2010 at 5:28 PM, Joern Huxhorn <jhuxhorn@googlemail.com> wrote:
On 30.04.2010, at 16:04, Ceki Gülcü wrote:
Given that it solves the method population explosion problem, [...]
I don't agree that this problem is a problem at all.
I'd prefer if the list of alternatives I get when typing "log.[ctrl+space]" remained bounded. BR, Jukka Zitting

On 30/04/2010 5:28 PM, Joern Huxhorn wrote:
In my case, it was a mixture of not enough spare-time and not really knowing what I should reply.
No worries.
I was also a bit confused by your examples: LoggingEvent event = new LoggingEvent(Level.ERROR, new AuditMessage("msg0", "type0"))).add(t); logger.error(event);
I guess you mean logger.log instead of logger.error. Otherwise I'd be quite interested what the framework should do if the given event wasn't of Level.ERROR.
Yes, I had meant to write logger.log instead of logger.error.
Beside that, I'm very hesitant about adding Level at this point (read: I think it's a very bad idea), anyway, since the proper way of implementing it would be an enum but we need to stay 1.4-compatible for now, right? If we introduce it now then we won't be able to change it into an enum later on. That's why I would NOT add Level to SLF4J right now. I'd do this during the big 1.5 update sometime in the future.
Indeed. Using enums for levels will require JDK 1.5 so we won't be able to use enums in SLF4J 1.6 which still targets JDK 1.4. Compared to typed level classes, e.g. java.util.logging.Level, ch.qos.logack.classic.Level and org.apache.log4j.Level, levels based on Java 5 enums offer little benefit. Yes, enums can be used as case labels, but how difficult is to write the following? int levelInt = level.toInt(); switch(levelInt) { case Level.TRACE_INT: ...; case Level.DEBUG_INT: ...; case Level.INFO_INT: ...; etc. } Do you see other benefits of a level based on enums over a typed level? If not, I think we can stick with typed levels even after SLF4J 1.6. If you wish to pursue this subtopic, I suggest creating a new thread named "level based on enums" or similar.
[cut]
Using the suggested methods would create illegible logging calls, at least in my opinion, since they differ quite much from the current signature. The current methods enforce a certain order which won't be the case anymore in case of LoggingEvent.
I agree that building the LoggingEvent prior to calling logger.log implies a paradigm change. However, I fail to understand your point about "current methods enforce a certain order...". Could you explain? More below.
Concerning the insanity, I'd rather be lazy while using the Logger than while implementing it.
Of course! I agree with that sentiment but it's also about striking a balance. Assuming that the vast majority of logger invocations are akin to logger.level(message), only in a small number of cases will the user write: LoggingEvent event = new LoggingEvent(Level.ERROR, new Message("msg0", "type0"))).add(t); logger.log(event); With this API change, simple things remain simple and more complex thing are made possible.
(Concerning the performance info at http://bugzilla.slf4j.org/show_bug.cgi?id=31#c82 I'd even consider adding methods with 4 and 5 arguments :p)
Considering that only a very small proportion of logger calls have 4 or 5 arguments, the expected performance gain obtained by the addition of these methods should be negligible, especially considering that although calling new Object[] is noticeably slower but not in way which offsets the diminishing probability of having 4 or 5 params.
This means I'd happily perform the monkey-work of implementing 4*5(LogLevels)*6(SLF4J-implementations) methods but I'm not really interested very much in Logger.log(LoggerEvent)
I actually agree with the way you brake down the problem. It's really a trade between monkey-work on side of SLF4J developers versus inconvenience to the users. The LoggingEvent paradigm is not that awful and much more extensible. (Add a new method to LoggingEvent instead of adding Nx5 new methods to logger, times its various implementations.)
I don't agree that this problem is a problem at all.
Given that the logger interface has to deal with 6 different types, namely Level, Marker, String (the message), Message (typed message), Throwable, Object[] (the arguments), combinatorial explosion in method signatures cannot be avoided. Bug 70, which you know well, is an example of possible interferences caused by just two different types (Object[] and Throwable). Around March of this year, Gunnar Wagenknecht observed that markers should viewed as tags and the current marker mechanism is over engineered. Unfortunately, I think he is right **. At this stage, it would be very inelegant to add Marker[] as a new parameter type to the logger interface. With the LoggerEvent paradigm, it the user wishes to add several markers to the event, she repeatedly calls event.add(marker). For example, new LoggingEvent("hello world").add(marker1).add(marker2); That's it. ** http://www.qos.ch/pipermail/slf4j-dev/2010-March/002875.html
If it's simply a matter of the amount of work that'll be required then I'd be happy to help out.
Thank you. I appreciate the offer.
If that's not the issue then please explain it to me.
I tried. See above. In a nutshell, I'd like to avoid painting ourselves to a corner. :-)
Joern.
-- Ceki

On Apr 30, 2010, at 10:16 AM, Ceki Gülcü wrote:
If that's not the issue then please explain it to me.
I tried. See above. In a nutshell, I'd like to avoid painting ourselves to a corner. :-)
I understand what you think is the issue. However, the unintended side effect that introducing LoggingEvent will have is this: LoggingEvent event = new LoggingEvent(Level.DEBUG).add(marker); event.setMessage(message1); logger.log(event); event.setMessage(message2); logger.log(event); For this to work the logging implementation has to make a new copy of the logging event. Otherwise, if an appender were to cache logging events, as they can safely do now (i.e. ListAppender), and then write them in batches events would get lost. Asynchronous appenders would also have to do this. The only way I've thought of to make this useful is to do something like: public class LogHeader { private Level level; private Marker marker; public void addMarker(Marker marker) { this.marker = marker; } public log(Message msg) { logger.log(this, msg); } } public class Debug extends LogHeader { public Debug() {; setLevel(Level.DEBUG); } } public class Error extends LogHeader { public Error() { setLevel(Level.ERROR); } } private static final Debug DEBUG = new Debug(); logger.log(DEBUG, msg); However, I don't think this does much to address what you think is a problem. Ralph

On 01/05/2010 3:45 AM, Ralph Goers wrote:
I understand what you think is the issue. However, the unintended side effect that introducing LoggingEvent will have is this:
LoggingEvent event = new LoggingEvent(Level.DEBUG).add(marker);
event.setMessage(message1); logger.log(event); event.setMessage(message2); logger.log(event);
For this to work the logging implementation has to make a new copy of the logging event. Otherwise, if an appender were to cache logging events, as they can safely do now (i.e. ListAppender), and then write them in batches events would get lost. Asynchronous appenders would also have to do this.
Logback's LoggingEvent is intended to be different than org.slf4j.spi.LoggingEvent. In all likelyhood, it (logback's LoggingEvent) will copy the data points from SLF4J's LoggingEvent so that the problem you mention will not arise. -- Ceki

In that case, SLF4J's LoggingEvent becomes even more pointless. It adds more overhead/messiness to the caller to create these events purely for the sake of making SLF4J's api simpler. That is a very poor tradeoff IMO. Ralph On May 1, 2010, at 4:54 PM, Ceki Gülcü wrote:
On 01/05/2010 3:45 AM, Ralph Goers wrote:
I understand what you think is the issue. However, the unintended side effect that introducing LoggingEvent will have is this:
LoggingEvent event = new LoggingEvent(Level.DEBUG).add(marker);
event.setMessage(message1); logger.log(event); event.setMessage(message2); logger.log(event);
For this to work the logging implementation has to make a new copy of the logging event. Otherwise, if an appender were to cache logging events, as they can safely do now (i.e. ListAppender), and then write them in batches events would get lost. Asynchronous appenders would also have to do this.
Logback's LoggingEvent is intended to be different than org.slf4j.spi.LoggingEvent. In all likelyhood, it (logback's LoggingEvent) will copy the data points from SLF4J's LoggingEvent so that the problem you mention will not arise.
-- Ceki
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 30.04.2010, at 19:16, Ceki Gülcü wrote:
Beside that, I'm very hesitant about adding Level at this point (read: I think it's a very bad idea), anyway, since the proper way of implementing it would be an enum but we need to stay 1.4-compatible for now, right? If we introduce it now then we won't be able to change it into an enum later on. That's why I would NOT add Level to SLF4J right now. I'd do this during the big 1.5 update sometime in the future.
Indeed.
Using enums for levels will require JDK 1.5 so we won't be able to use enums in SLF4J 1.6 which still targets JDK 1.4. Compared to typed level classes, e.g. java.util.logging.Level, ch.qos.logack.classic.Level and org.apache.log4j.Level, levels based on Java 5 enums offer little benefit. Yes, enums can be used as case labels, but how difficult is to write the following?
int levelInt = level.toInt(); switch(levelInt) { case Level.TRACE_INT: ...; case Level.DEBUG_INT: ...; case Level.INFO_INT: ...; etc. }
Do you see other benefits of a level based on enums over a typed level? If not, I think we can stick with typed levels even after SLF4J 1.6. If you wish to pursue this subtopic, I suggest creating a new thread named "level based on enums" or similar.
Did that ;) It's already possible to implement this kind of functionality in current SLF4J so I don't think that adding logging-methods with a generic level are an important issue right now. We are not forced to add it ASAP since a "workaround" exists and it's not adding new functionality in the sense that one can do something that isn't already possible. Yes, it's not perfect that one has to implement such code but it's possible anyway. I'm not sure if you've seen the Level and Threshold enums in slf4j-n-api. They contain additional logic to check if a Level passes. http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... There's no magic in it, it's just neat to be able to add additional functionality to those enums, assuming that a Logger would have a retrievable Threshold. My main point is that Level is more or less a prime example of an enum use-case. A modern API would use an enum so we should, too, as soon as we require Java 1.5. It gives us the opportunity to add functionality, if necessary, and frees us from implementing immutability, cloning and deserialization ourselves. Joern.

Hello all, In light of your comments below and assuming that an overhaul of the SLF4J API cannot be done in a short amount of time, I think that my plan for 1.6 and 2.0 compatibility is looking less realistic. SLF4J version 2.0 which is planned to require Java 5 will provide us a good opportunity to modify SLF4J API and start to use Java 5 functionality in both SLF4J API and code. So instead of cramming changes into 1.6 that we might later regret, I think we should release 1.6 as soon as possible so that users can take advantage of nop-defaulting right now and take our time working on SLF4J 2.0. More formally, - Release 1.6.0-alpha2 as 1.6.0 - Require Java 5 for SLF4J 2.0 - Allow for breaking-changes in 2.0 so that we can incorporate new functionality and API additions for v2.0 Does that sound OK? On 01/05/2010 11:18 AM, Joern Huxhorn wrote:
On 30.04.2010, at 19:16, Ceki Gülcü wrote:
Beside that, I'm very hesitant about adding Level at this point (read: I think it's a very bad idea), anyway, since the proper way of implementing it would be an enum but we need to stay 1.4-compatible for now, right? If we introduce it now then we won't be able to change it into an enum later on. That's why I would NOT add Level to SLF4J right now. I'd do this during the big 1.5 update sometime in the future.
Indeed.
Using enums for levels will require JDK 1.5 so we won't be able to use enums in SLF4J 1.6 which still targets JDK 1.4. Compared to typed level classes, e.g. java.util.logging.Level, ch.qos.logack.classic.Level and org.apache.log4j.Level, levels based on Java 5 enums offer little benefit. Yes, enums can be used as case labels, but how difficult is to write the following?
int levelInt = level.toInt(); switch(levelInt) { case Level.TRACE_INT: ...; case Level.DEBUG_INT: ...; case Level.INFO_INT: ...; etc. }
Do you see other benefits of a level based on enums over a typed level? If not, I think we can stick with typed levels even after SLF4J 1.6. If you wish to pursue this subtopic, I suggest creating a new thread named "level based on enums" or similar.
Did that ;)
It's already possible to implement this kind of functionality in current SLF4J so I don't think that adding logging-methods with a generic level are an important issue right now. We are not forced to add it ASAP since a "workaround" exists and it's not adding new functionality in the sense that one can do something that isn't already possible.
Yes, it's not perfect that one has to implement such code but it's possible anyway.
I'm not sure if you've seen the Level and Threshold enums in slf4j-n-api. They contain additional logic to check if a Level passes.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
There's no magic in it, it's just neat to be able to add additional functionality to those enums, assuming that a Logger would have a retrievable Threshold.
My main point is that Level is more or less a prime example of an enum use-case. A modern API would use an enum so we should, too, as soon as we require Java 1.5. It gives us the opportunity to add functionality, if necessary, and frees us from implementing immutability, cloning and deserialization ourselves.
Joern.

Since adding Messages doesn't break the API I'd still like it added in 1.6. Ralph On May 3, 2010, at 3:38 AM, Ceki Gülcü wrote:
Hello all,
In light of your comments below and assuming that an overhaul of the SLF4J API cannot be done in a short amount of time, I think that my plan for 1.6 and 2.0 compatibility is looking less realistic. SLF4J version 2.0 which is planned to require Java 5 will provide us a good opportunity to modify SLF4J API and start to use Java 5 functionality in both SLF4J API and code.
So instead of cramming changes into 1.6 that we might later regret, I think we should release 1.6 as soon as possible so that users can take advantage of nop-defaulting right now and take our time working on SLF4J 2.0.
More formally,
- Release 1.6.0-alpha2 as 1.6.0 - Require Java 5 for SLF4J 2.0 - Allow for breaking-changes in 2.0 so that we can incorporate new functionality and API additions for v2.0
Does that sound OK?
On 01/05/2010 11:18 AM, Joern Huxhorn wrote:
On 30.04.2010, at 19:16, Ceki Gülcü wrote:
Beside that, I'm very hesitant about adding Level at this point (read: I think it's a very bad idea), anyway, since the proper way of implementing it would be an enum but we need to stay 1.4-compatible for now, right? If we introduce it now then we won't be able to change it into an enum later on. That's why I would NOT add Level to SLF4J right now. I'd do this during the big 1.5 update sometime in the future.
Indeed.
Using enums for levels will require JDK 1.5 so we won't be able to use enums in SLF4J 1.6 which still targets JDK 1.4. Compared to typed level classes, e.g. java.util.logging.Level, ch.qos.logack.classic.Level and org.apache.log4j.Level, levels based on Java 5 enums offer little benefit. Yes, enums can be used as case labels, but how difficult is to write the following?
int levelInt = level.toInt(); switch(levelInt) { case Level.TRACE_INT: ...; case Level.DEBUG_INT: ...; case Level.INFO_INT: ...; etc. }
Do you see other benefits of a level based on enums over a typed level? If not, I think we can stick with typed levels even after SLF4J 1.6. If you wish to pursue this subtopic, I suggest creating a new thread named "level based on enums" or similar.
Did that ;)
It's already possible to implement this kind of functionality in current SLF4J so I don't think that adding logging-methods with a generic level are an important issue right now. We are not forced to add it ASAP since a "workaround" exists and it's not adding new functionality in the sense that one can do something that isn't already possible.
Yes, it's not perfect that one has to implement such code but it's possible anyway.
I'm not sure if you've seen the Level and Threshold enums in slf4j-n-api. They contain additional logic to check if a Level passes.
http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o...
There's no magic in it, it's just neat to be able to add additional functionality to those enums, assuming that a Logger would have a retrievable Threshold.
My main point is that Level is more or less a prime example of an enum use-case. A modern API would use an enum so we should, too, as soon as we require Java 1.5. It gives us the opportunity to add functionality, if necessary, and frees us from implementing immutability, cloning and deserialization ourselves.
Joern.
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On 03/05/2010 1:02 PM, Ralph Goers wrote:
Since adding Messages doesn't break the API I'd still like it added in 1.6.
In order to support Message the o.s.Logger interface has to be modified which was the main issue discussed thus far. Moreover, any addition to the o.s.Logger interface has to be supported indefinitely since Logger is a client-facing interface. Changes to Logger cannot be taken lightly. Frankly, I am quite puzzled by the "Message doesn't break the API" statement which I attribute to a yet unidentified misunderstanding. -- Ceki

On 03.05.2010, at 13:44, Ceki Gülcü wrote:
On 03/05/2010 1:02 PM, Ralph Goers wrote:
Since adding Messages doesn't break the API I'd still like it added in 1.6.
In order to support Message the o.s.Logger interface has to be modified which was the main issue discussed thus far. Moreover, any addition to the o.s.Logger interface has to be supported indefinitely since Logger is a client-facing interface. Changes to Logger cannot be taken lightly. Frankly, I am quite puzzled by the "Message doesn't break the API" statement which I attribute to a yet unidentified misunderstanding.
I think he was referring to the fact that adding to an interface does not break client-side API compatibility, "only" compatibility of implementing classes. The reason why we (re)started the Message discussion at this point was the addition of a new method in LocationAwareLogger that rendered current SLF4J implementations incompatible with the new SLF4J. The idea was that breaking this compatibility only once would be better than multiple times - so adding Message support in the same release would have made sense. I agree that we mustn't extend the Logger interface carelessly - and since it doesn't look like we'll find a common position anytime soon, it might indeed be the best to postpone this issue... Anyway, please consider fixing http://jira.qos.ch/browse/LBCLASSIC-156 before releasing a new SLF4J 1.6.0 compatible Logback. It's a rather small and simple issue after all. Cheers, Joern.

On 30.04.2010, at 19:16, Ceki Gülcü wrote:
Using the suggested methods would create illegible logging calls, at least in my opinion, since they differ quite much from the current signature. The current methods enforce a certain order which won't be the case anymore in case of LoggingEvent.
I agree that building the LoggingEvent prior to calling logger.log implies a paradigm change. However, I fail to understand your point about "current methods enforce a certain order...". Could you explain?
In case of logger.log(new LoggerEvent(Level.INFO, new ParameterizedMessage("Foo Bar {}").addParam("FooBar")).set(throwable).add(marker1).add(marker2)) the order is completely different compared to logger.info(marker1, "Foo Bar {}", "FooBar", throwable) It's downright unreadable, at least if taking just a glance. Level is too far on the right. The Throwable is somewhere in the middle. It's the same reasoning as in Scalas decision to define variables like this: var x: Integer The important part, the variable name, is at the first position. The less important part (while just scanning the code), the probably very noisy type, is further to the right. In case of logging, the most important part while scanning source code is the level. Yes, my reasoning is mostly based on aesthetics and the ability to read other peoples code quickly. Our brain is a pattern-matching-engine. ;)
Concerning the insanity, I'd rather be lazy while using the Logger than while implementing it.
Of course! I agree with that sentiment but it's also about striking a balance. Assuming that the vast majority of logger invocations are akin to logger.level(message), only in a small number of cases will the user write:
LoggingEvent event = new LoggingEvent(Level.ERROR, new Message("msg0", "type0"))).add(t); logger.log(event);
With this API change, simple things remain simple and more complex thing are made possible.
Comments below.
(Concerning the performance info at http://bugzilla.slf4j.org/show_bug.cgi?id=31#c82 I'd even consider adding methods with 4 and 5 arguments :p)
Considering that only a very small proportion of logger calls have 4 or 5 arguments, the expected performance gain obtained by the addition of these methods should be negligible, especially considering that although calling new Object[] is noticeably slower but not in way which offsets the diminishing probability of having 4 or 5 params.
I wasn't trying to suggest we should add these. I think we shouldn't. If performance was THE most important factor then, yes, we should, but you are right that these are corner-cases that won't happen too often. You shouldn't forget, however, that the addition of Throwable support essentially reduces the really available amount of fixed arguments to 2, not 3, in that case. Again, I think that this is acceptable anyway, even though most of my log-messages have >2 parameters.
This means I'd happily perform the monkey-work of implementing 4*5(LogLevels)*6(SLF4J-implementations) methods but I'm not really interested very much in Logger.log(LoggerEvent)
I actually agree with the way you brake down the problem. It's really a trade between monkey-work on side of SLF4J developers versus inconvenience to the users. The LoggingEvent paradigm is not that awful and much more extensible. (Add a new method to LoggingEvent instead of adding Nx5 new methods to logger, times its various implementations.)
I really think that LoggingEvent is a very private property of the backend-implementation that shouldn't "leak" into SLF4J. I also share Ralphs concern about reusing LoggingEvent instances multiple times. Code like that will happen and the probability increases with the complexity of it's creation - no matter what the documentation will say about this. People won't read it anyway until it's too late (i.e. until they already have some inexplicable behavior). ;) The main idea of the Message interface was to prevent such a tight coupling while offering a maximum of flexibility.
I don't agree that this problem is a problem at all.
Given that the logger interface has to deal with 6 different types, namely Level, Marker, String (the message), Message (typed message), Throwable, Object[] (the arguments), combinatorial explosion in method signatures cannot be avoided. Bug 70, which you know well, is an example of possible interferences caused by just two different types (Object[] and Throwable).
Around March of this year, Gunnar Wagenknecht observed that markers should viewed as tags and the current marker mechanism is over engineered. Unfortunately, I think he is right **. At this stage, it would be very inelegant to add Marker[] as a new parameter type to the logger interface. With the LoggerEvent paradigm, it the user wishes to add several markers to the event, she repeatedly calls event.add(marker). For example,
new LoggingEvent("hello world").add(marker1).add(marker2);
That's it.
** http://www.qos.ch/pipermail/slf4j-dev/2010-March/002875.html
How about only adding logger.debug(Message) logger.debug(Message, Throwable) leaving out the Marker? That way the Message implementation might opt-in to add Marker-support as seen fit. I'd like to point out, though, that I haven't thought this through, yet. I think it would likely create rather big problems, including TurboFilters that won't properly match on Markers anymore... which would create a large amount of confusion for the user. The main-point of Markers, for me, is the ability to use them for efficient filtering in Logback (and Lilith). I never liked the hierarchy-aspect of them, especially since they are supposed to be global. In my opinion, the marker-hierarchy shouldn't be created in the software but in the configuration instead. Under no circumstance should other modules be able to change my marker-hierarchy. I once reported a bug about recursive marker-hierarchies that you fixed by preventing recursion at all. This has the effect that the module that creates the Marker first will be able to decide on the hierarchy while a module loaded later-on in the application will be stuck on the previously created hierarchy - even if it's logging is expecting an entirely different one. This is a catastrophe waiting to happen - which is why I'm not using them in that way in my code. I'm also not sure about possible classloader-woes concerning MarkerFactory. [1][2] I haven't checked this but I suspect there could be problems with Markers comparable to those of custom java.util.logging.Level. Beside that, I think the Markers ENTERING, EXITING and THROWING should be used without being put into a TRACING marker. The grouping of those three markers into a TRACING group should be done in the Logback config and filters could then filter on that group. But this after-the-fact discussion isn't helping anymore. One way to fix it, though, would be not caching Markers in MarkerFactory at all. The use-case described by Gunnar could (and IMHO: should) be handled using the MDC, not Markers. He could call MDC.put("CONFIDENTIAL","true") and perform special handling that way. Both ways assume that the Logging system is evaluating either Marker or MDC which isn't the wisest thing to do. Confidential info should NEVER be logged, IMHO, regardless of the logging configuration.
If it's simply a matter of the amount of work that'll be required then I'd be happy to help out.
Thank you. I appreciate the offer.
You're welcome. Cheers & have a nice weekend, Joern. [1] http://blogs.sun.com/fkieviet/entry/classloader%5Fleaks%5Fthe%5Fdreaded%5Fja... [2] http://blogs.sun.com/fkieviet/entry/how%5Fto%5Ffix%5Fthe%5Fdreaded

On May 1, 2010, at 3:37 AM, Joern Huxhorn wrote:
How about only adding
logger.debug(Message) logger.debug(Message, Throwable)
leaving out the Marker?
That way the Message implementation might opt-in to add Marker-support as seen fit.
I'd like to point out, though, that I haven't thought this through, yet.
I think it would likely create rather big problems, including TurboFilters that won't properly match on Markers anymore... which would create a large amount of confusion for the user.
The main-point of Markers, for me, is the ability to use them for efficient filtering in Logback (and Lilith).
I never liked the hierarchy-aspect of them, especially since they are supposed to be global.
I don't mind the hierarchy. I've just found them to be awkward because you have to call add. It would have been much more natural to have one marker class extend another if you want that kind of relationship.
In my opinion, the marker-hierarchy shouldn't be created in the software but in the configuration instead. Under no circumstance should other modules be able to change my marker-hierarchy. I once reported a bug about recursive marker-hierarchies that you fixed by preventing recursion at all. This has the effect that the module that creates the Marker first will be able to decide on the hierarchy while a module loaded later-on in the application will be stuck on the previously created hierarchy - even if it's logging is expecting an entirely different one. This is a catastrophe waiting to happen - which is why I'm not using them in that way in my code.
I'm not sure how you create a marker in the configuration when it is used in the code. What would debug(MY_MARKER, msg) do if MY_MARKER wasn't defined?
I'm also not sure about possible classloader-woes concerning MarkerFactory. [1][2] I haven't checked this but I suspect there could be problems with Markers comparable to those of custom java.util.logging.Level.
Beside that, I think the Markers ENTERING, EXITING and THROWING should be used without being put into a TRACING marker. The grouping of those three markers into a TRACING group should be done in the Logback config and filters could then filter on that group.
I disagree with this. First off, ENTERING and EXITING use a FLOW marker, not TRACING. THROWING and CATCHING extend EXCEPTION. This makes sense because it lets you filter on FLOW events as a whole or just entry events if you want.
But this after-the-fact discussion isn't helping anymore. One way to fix it, though, would be not caching Markers in MarkerFactory at all.
The use-case described by Gunnar could (and IMHO: should) be handled using the MDC, not Markers. He could call MDC.put("CONFIDENTIAL","true") and perform special handling that way.
I would only recommend the MDC be used for data that could possibly span log events. Having to set it and remove it after each logging call would be a pain. Markers work better for this.
Both ways assume that the Logging system is evaluating either Marker or MDC which isn't the wisest thing to do. Confidential info should NEVER be logged, IMHO, regardless of the logging configuration.
Sometimes it has to be. Trying to diagnose problems often requires this kind of information. Also, audit logs will often contain confidential or sensitive information. We have different levels of classification of data. A user's bank account number might appear in a log. A user's PIN or password will never appear. Ralph

On 01.05.2010, at 19:01, Ralph Goers wrote:
On May 1, 2010, at 3:37 AM, Joern Huxhorn wrote:
How about only adding
logger.debug(Message) logger.debug(Message, Throwable)
leaving out the Marker?
That way the Message implementation might opt-in to add Marker-support as seen fit.
I'd like to point out, though, that I haven't thought this through, yet.
I think it would likely create rather big problems, including TurboFilters that won't properly match on Markers anymore... which would create a large amount of confusion for the user.
The main-point of Markers, for me, is the ability to use them for efficient filtering in Logback (and Lilith).
I never liked the hierarchy-aspect of them, especially since they are supposed to be global.
I don't mind the hierarchy. I've just found them to be awkward because you have to call add. It would have been much more natural to have one marker class extend another if you want that kind of relationship.
I definitely like hierarchical markers, I simply don't think that this hierarchy should be placed in the code. It's about marker-hierarchy at compile-time vs. marker-hierarchy at runtime.
In my opinion, the marker-hierarchy shouldn't be created in the software but in the configuration instead. Under no circumstance should other modules be able to change my marker-hierarchy. I once reported a bug about recursive marker-hierarchies that you fixed by preventing recursion at all. This has the effect that the module that creates the Marker first will be able to decide on the hierarchy while a module loaded later-on in the application will be stuck on the previously created hierarchy - even if it's logging is expecting an entirely different one. This is a catastrophe waiting to happen - which is why I'm not using them in that way in my code.
I'm not sure how you create a marker in the configuration when it is used in the code. What would debug(MY_MARKER, msg) do if MY_MARKER wasn't defined?
No, you misunderstood me. I don't want to put the markers into the logback config file, merely the marker-hierarchy. I'll explain below...
I'm also not sure about possible classloader-woes concerning MarkerFactory. [1][2] I haven't checked this but I suspect there could be problems with Markers comparable to those of custom java.util.logging.Level.
Beside that, I think the Markers ENTERING, EXITING and THROWING should be used without being put into a TRACING marker. The grouping of those three markers into a TRACING group should be done in the Logback config and filters could then filter on that group.
I disagree with this. First off, ENTERING and EXITING use a FLOW marker, not TRACING. THROWING and CATCHING extend EXCEPTION. This makes sense because it lets you filter on FLOW events as a whole or just entry events if you want.
I didn't actually refer to XLogger, I was just making an example. Sorry for the confusion, I entirely forgot that this is actually implemented in XLogger already. I'll take the XLogger-Markers as an example, then: The hierarchy looks like this: FLOW_MARKER ENTRY_MARKER EXIT_MARKER EXCEPTION_MARKER THROWING_MARKER CATCHING_MARKER I deliberately wrote it like that, i.e. FLOW contains ENTRY and EXIT instead of the other way around. Your code will only ever use ENTRY, EXIT, THROWING, CATCHING, never FLOW or EXCEPTION. FLOW and EXCEPTION are only used for the hierarchy so it's possible to enable/disable both contained markers. I'd put *that* hierarchy into the logback/logging -config. I'd like to be able to define such a grouping at configuration/runtime For example, I, personally, would put ENTRY, EXIT, THROWING and CATCHING into a single group or a might only be interested in ENTRY, EXIT and THROWING and put those into another arbitrarily named group. If this was (or is?) supported then the marker hierarchy based on Marker.add() would be needless. Another problem is that different modules might use markers of the same name entirely differently. This isn't likely in the above case but there are other. This is the reason why markers should NOT be statically defined across different modules/packages, IMHO. With configuration of the hierarchy in the configuration one could work around such issues, I think. Personally, I'd deprecate MarkerFactory.getMarker() (pointing to getDetachedMarker() instead) and maybe even Marker.add(Marker), Marker.remove(Marker) etc., (documenting why using this feature is probably bad).
But this after-the-fact discussion isn't helping anymore. One way to fix it, though, would be not caching Markers in MarkerFactory at all.
The use-case described by Gunnar could (and IMHO: should) be handled using the MDC, not Markers. He could call MDC.put("CONFIDENTIAL","true") and perform special handling that way.
I would only recommend the MDC be used for data that could possibly span log events. Having to set it and remove it after each logging call would be a pain. Markers work better for this.
I wouldn't be a friend of this either. :p
Both ways assume that the Logging system is evaluating either Marker or MDC which isn't the wisest thing to do. Confidential info should NEVER be logged, IMHO, regardless of the logging configuration.
Sometimes it has to be. Trying to diagnose problems often requires this kind of information. Also, audit logs will often contain confidential or sensitive information. We have different levels of classification of data. A user's bank account number might appear in a log. A user's PIN or password will never appear.
I think it would be a good idea to use entirely separate markers in those cases, i.e. FOO_MARKER and CONFIDENTIAL_FOO_MARKER If the grouping/hierarchy-configuration would allow wildcards then it would be possible to group "*FOO_MARKER" and it would also be possible to group "CONFIDENTIAL_*" to catch all confidential calls. The used markers are essentially scoped for modules. I'd expect defining and documenting markers in an interface, e.g. public interface Slf4jExtMarkers { final Marker ENTRY=LoggerFactory.getDetachedMarker("ENTRY"); final Marker EXIT=LoggerFactory.getDetachedMarker("EXIT"); final Marker THROWING=LoggerFactory.getDetachedMarker("THROWING"); final Marker CATCHING=LoggerFactory.getDetachedMarker("CATCHING"); } Problem: Markers aren't immutable - so it would be possible the change those static instances :p We could, however, implement an ImmutableMarker instead for use-cases like that, which would throw an UnsupportedOperationException in case of add() and remove(). Cheers, Joern.

Hi everyone. I've added a tracing aspect to the Lilith modules. <dependency> <groupId>de.huxhorn.lilith</groupId> <artifactId>de.huxhorn.lilith.tracing</artifactId> </dependency> You can find a bit of info at http://sourceforge.net/apps/trac/lilith/wiki/TracingAspect The code is located at http://github.com/huxi/lilith/blob/master/tracing/src/main/java/de/huxhorn/l... with usage examples in http://github.com/huxi/lilith/blob/master/tracing/src/test/java/de/huxhorn/l... and http://github.com/huxi/lilith/tree/master/tracing/src/test/resources/ Please let me know what you think about it. Cheers, Joern. On 02.05.2010, at 06:38, Joern Huxhorn wrote:
On 01.05.2010, at 19:01, Ralph Goers wrote:
On May 1, 2010, at 3:37 AM, Joern Huxhorn wrote:
How about only adding
logger.debug(Message) logger.debug(Message, Throwable)
leaving out the Marker?
That way the Message implementation might opt-in to add Marker-support as seen fit.
I'd like to point out, though, that I haven't thought this through, yet.
I think it would likely create rather big problems, including TurboFilters that won't properly match on Markers anymore... which would create a large amount of confusion for the user.
The main-point of Markers, for me, is the ability to use them for efficient filtering in Logback (and Lilith).
I never liked the hierarchy-aspect of them, especially since they are supposed to be global.
I don't mind the hierarchy. I've just found them to be awkward because you have to call add. It would have been much more natural to have one marker class extend another if you want that kind of relationship.
I definitely like hierarchical markers, I simply don't think that this hierarchy should be placed in the code. It's about marker-hierarchy at compile-time vs. marker-hierarchy at runtime.
In my opinion, the marker-hierarchy shouldn't be created in the software but in the configuration instead. Under no circumstance should other modules be able to change my marker-hierarchy. I once reported a bug about recursive marker-hierarchies that you fixed by preventing recursion at all. This has the effect that the module that creates the Marker first will be able to decide on the hierarchy while a module loaded later-on in the application will be stuck on the previously created hierarchy - even if it's logging is expecting an entirely different one. This is a catastrophe waiting to happen - which is why I'm not using them in that way in my code.
I'm not sure how you create a marker in the configuration when it is used in the code. What would debug(MY_MARKER, msg) do if MY_MARKER wasn't defined?
No, you misunderstood me. I don't want to put the markers into the logback config file, merely the marker-hierarchy. I'll explain below...
I'm also not sure about possible classloader-woes concerning MarkerFactory. [1][2] I haven't checked this but I suspect there could be problems with Markers comparable to those of custom java.util.logging.Level.
Beside that, I think the Markers ENTERING, EXITING and THROWING should be used without being put into a TRACING marker. The grouping of those three markers into a TRACING group should be done in the Logback config and filters could then filter on that group.
I disagree with this. First off, ENTERING and EXITING use a FLOW marker, not TRACING. THROWING and CATCHING extend EXCEPTION. This makes sense because it lets you filter on FLOW events as a whole or just entry events if you want.
I didn't actually refer to XLogger, I was just making an example. Sorry for the confusion, I entirely forgot that this is actually implemented in XLogger already.
I'll take the XLogger-Markers as an example, then:
The hierarchy looks like this:
FLOW_MARKER ENTRY_MARKER EXIT_MARKER
EXCEPTION_MARKER THROWING_MARKER CATCHING_MARKER
I deliberately wrote it like that, i.e. FLOW contains ENTRY and EXIT instead of the other way around. Your code will only ever use ENTRY, EXIT, THROWING, CATCHING, never FLOW or EXCEPTION. FLOW and EXCEPTION are only used for the hierarchy so it's possible to enable/disable both contained markers.
I'd put *that* hierarchy into the logback/logging -config.
I'd like to be able to define such a grouping at configuration/runtime For example, I, personally, would put ENTRY, EXIT, THROWING and CATCHING into a single group or a might only be interested in ENTRY, EXIT and THROWING and put those into another arbitrarily named group.
If this was (or is?) supported then the marker hierarchy based on Marker.add() would be needless.
Another problem is that different modules might use markers of the same name entirely differently. This isn't likely in the above case but there are other.
This is the reason why markers should NOT be statically defined across different modules/packages, IMHO.
With configuration of the hierarchy in the configuration one could work around such issues, I think.
Personally, I'd deprecate MarkerFactory.getMarker() (pointing to getDetachedMarker() instead) and maybe even Marker.add(Marker), Marker.remove(Marker) etc., (documenting why using this feature is probably bad).
But this after-the-fact discussion isn't helping anymore. One way to fix it, though, would be not caching Markers in MarkerFactory at all.
The use-case described by Gunnar could (and IMHO: should) be handled using the MDC, not Markers. He could call MDC.put("CONFIDENTIAL","true") and perform special handling that way.
I would only recommend the MDC be used for data that could possibly span log events. Having to set it and remove it after each logging call would be a pain. Markers work better for this.
I wouldn't be a friend of this either. :p
Both ways assume that the Logging system is evaluating either Marker or MDC which isn't the wisest thing to do. Confidential info should NEVER be logged, IMHO, regardless of the logging configuration.
Sometimes it has to be. Trying to diagnose problems often requires this kind of information. Also, audit logs will often contain confidential or sensitive information. We have different levels of classification of data. A user's bank account number might appear in a log. A user's PIN or password will never appear.
I think it would be a good idea to use entirely separate markers in those cases, i.e. FOO_MARKER and CONFIDENTIAL_FOO_MARKER
If the grouping/hierarchy-configuration would allow wildcards then it would be possible to group "*FOO_MARKER" and it would also be possible to group "CONFIDENTIAL_*" to catch all confidential calls. The used markers are essentially scoped for modules.
I'd expect defining and documenting markers in an interface, e.g. public interface Slf4jExtMarkers { final Marker ENTRY=LoggerFactory.getDetachedMarker("ENTRY"); final Marker EXIT=LoggerFactory.getDetachedMarker("EXIT"); final Marker THROWING=LoggerFactory.getDetachedMarker("THROWING"); final Marker CATCHING=LoggerFactory.getDetachedMarker("CATCHING"); }
Problem: Markers aren't immutable - so it would be possible the change those static instances :p We could, however, implement an ImmutableMarker instead for use-cases like that, which would throw an UnsupportedOperationException in case of add() and remove().
Cheers, Joern. _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev

On Apr 30, 2010, at 7:04 AM, Ceki Gülcü wrote:
On 30/04/2010 3:24 PM, Ralph Goers wrote:
Generally when I don't reply it isn't because I love the idea.
I gathered as much.
I can't think of any use cases where I'd want to construct a logging event that way. It also would seem that you would be taking what is currently a structure private to Logback and making it public as it would make no sense for SLF4J to have one LoggingEvent and Logback to have another.
Building a LoggingEvent prior to calling org.slf4j.Logger avoids adding new methods to the Logger interface in order to keep it sane.
In short, it doesn't really solve what Joern and I have been looking for with support for the Message and doesn't provide much value that I can see.
Given that it solves the method population explosion problem, LoggingEvent can be considered as a prerequisite to to the addition of the Message interface.
So instead of adding new methods to Logger you will add them to LoggingEvent. That helps how? Ralph

On 01/05/2010 2:16 AM, Ralph Goers wrote:
On Apr 30, 2010, at 7:04 AM, Ceki Gülcü wrote:
On 30/04/2010 3:24 PM, Ralph Goers wrote:
Generally when I don't reply it isn't because I love the idea.
I gathered as much.
I can't think of any use cases where I'd want to construct a logging event that way. It also would seem that you would be taking what is currently a structure private to Logback and making it public as it would make no sense for SLF4J to have one LoggingEvent and Logback to have another.
Building a LoggingEvent prior to calling org.slf4j.Logger avoids adding new methods to the Logger interface in order to keep it sane.
In short, it doesn't really solve what Joern and I have been looking for with support for the Message and doesn't provide much value that I can see.
Given that it solves the method population explosion problem, LoggingEvent can be considered as a prerequisite to to the addition of the Message interface.
So instead of adding new methods to Logger you will add them to LoggingEvent. That helps how?
With the Logger interface, we overload the printing methods (debug(), info()...) so that they admit different types. This gets harder as the number of types increases. On the other hand, LoggingEvent is built piecemeal. For example, while it is practically impossible to add support for Marker arrays in Logger, in LoggingEvent this is trivial. The user invokes LoggingEvent's add(Marker) method multiple times or alternatively we can introduce a new method add(Marker[]) without any trouble. The approach is analogous to a constructor with 10 parameters versus an ObjectBuilder.
Ralph

Yes, he did. I also asked if that meant it would be an appropriate time to add the message concept. Ceki said, yes it would make sense, but I haven't seen any activity on that, which presumably means it will be even less likely to occur in the future. BTW - we just noticed that an Appender I wrote will no longer compile with the latest version of Logback due to the change to using an OutputStream instead of a Writer. Ralph On Apr 23, 2010, at 5:14 AM, Joern Huxhorn wrote:
Didn't you change LocationAwareLogger and doesn't that mean that it's not compatible with the current Logback anymore? Or is this only relevant for wrappers like jcl-over-slf4j and you changed all of them already?
I didn't have the time, yet, to check this myself. Please give me a short notice.
I could likely check this on our project some time next week.
Cheers, Joern.
On 22.04.2010, at 20:21, Ceki Gülcü wrote:
Sure. I'd appreciate if you could give 1.6.0-RC0 a try.
On 22/04/2010 2:17 PM, Joern Huxhorn wrote:
Thanks for implementing the fix for ticket 70!
Cheers, Joern.
On 21.04.2010, at 22:59, Ceki Gülcü wrote:
Hello all,
I am happy to announce the immediate availability of SLF4J version 1.6.0-RC0, which is still experimental, consisting of several important enhancements and bug fixes.
As of 1.6.0-RC0, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation discarding all log requests. Thus, instead of throwing an exception, SLF4J will emit a single warning message about the absence of a binding and proceed to discard all log requests without further protest.
Please refer to the the news page for precise details.
http://www.slf4j.org/news.html
You can download SLF4J, including full source code, class files and documentation on our download page, shown below.
http://www.slf4j.org/download.html
You can receive SLF4J related announcements by subscribing to the SLF4J announce mailing list. To subscribe to QOS.ch announce list, please visit the following URL.
http://qos.ch/mailman/listinfo/announce
-- Ceki Gülcü Logback: The reliable, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
_______________________________________________ slf4j-dev mailing list slf4j-dev@qos.ch http://qos.ch/mailman/listinfo/slf4j-dev
participants (5)
-
Ceki Gülcü
-
Joern Huxhorn
-
Jukka Zitting
-
Ralph Goers
-
Robert Elliot