On 17.09.2011, at 21:02, John Vasileff wrote:
>
> On Sep 17, 2011, at 2:40 PM, Joern Huxhorn wrote:
>
>>
>> On 17.09.2011, at 18:47, John Vasileff wrote:
>>
>>>
>>> On Sep 17, 2011, at 8:53 AM, Joern Huxhorn wrote:
>>>
>>>> Adding getThrowable() would also reintroduce the issue that such a Message
>>>> could not be serialized securely since deserialization would require the
>>>> exceptions class on the classpath. In my ParameterizedMessage over at
>>>> https://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/org/slf4j/n/messages/ParameterizedMessage.java
>>>> the Throwable is transient so it is deliberately lost while serializing,
>>>> i.e. it must be taken care of by the framework immediately after the
>>>> message is created.
>>>>
>>>> It is only contained in ParameterizedMessage at all simply because a
>>>> method can only return a single value. And the create-method is also
>>>> responsible for resolving the Throwable at the last position of arguments
>>>> if it is not used up by a corresponding {}.
>>>>
>>>> Yes, it would be "cleaner" to split that into a parseresult object that
>>>> contains both the message and an additional, optional Throwable. I just
>>>> didn't implement it that way to reduce the amount of garbage (that
>>>> parseresult object would get garbage-collected immediately after
>>>> evaluation).
>>>>
>>>> So adding Throwable to ParameterizedMessage was just a performance
>>>> optimization.
>>>>
>>>> The serialization issue is btw also the reason for *not* having Object[]
>>>> getParameters() but String[] getParameters() instead. The String
>>>> representation is the serialization-safe version of the parameters.
>>>>
>>>> But all of this is, in my books, an implementation detail of the Message
>>>> implementation with getFormattedMessage() being the only "public" API of
>>>> Message.
>>>>
>>>> Joern.
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: [email protected]
>>>> For additional commands, e-mail: [email protected]
>>>>
>>>
>>> Interesting point on serialization. When would you see serialization
>>> happening? Is this primarily for appenders?
>>>
>>
>> SocketAppender is using serialization. Since I'm the author of Lilith (
>> http://lilith.huxhorn.de/ ) I tend to focus on stuff like that.
>>
>> This is also the reason for the differentiation between the Message instance
>> and the (laziliy) formatted message string. A SocketAppender does not have
>> any need for a formatted message. It is perfectly valid to skip the
>> formatting entirely and simply transmit the message pattern and the message
>> parameters (as Strings) to safe some CPU in the logged application.
>>
>> Directly converting parameters into Strings as soon as we know that an event
>> really needs to be created is crucial to make sure that the logging
>> framework is not lying as is happening, for example, in this case:
>> http://techblog.appnexus.com/2011/webkit-chrome-safari-console-log-bug/
>> A lying logging framework is really, really bad...
>>
>> Joern.
>
> So, the two most significant points are:
>
> 1) ambiguity with info(Message msg, Throwable throwable) where for all
> practical purposes Message will at least sometimes also have a throwable.
> Minor additional point that having log methods with explicit Throwables adds
> several methods to the Logger interface.
>
> 2) issue with serializing appenders having to special case 'transient
> Throwable' in message objects (i.e. the appender would have to call
> getThrowable(), convert to String, and store separately from the message
> object.)
>
> My opinion is that Logger is by far the most important interface and design
> decisions should give preference to application developer facing interfaces.
> Thoughts?
The code simply looks like this:
public void log(Level level, String messagePattern, Object... args) {
if (!isEnabled(level)) return;
ParameterizedMessage message =
ParameterizedMessage.create(messagePattern, args);
performLogging(level, null, message, message.getThrowable());
}
public void log(Level level, Marker marker, String messagePattern, Object...
args) {
if (!isEnabled(level, marker)) return;
ParameterizedMessage message =
ParameterizedMessage.create(messagePattern, args);
performLogging(level, marker, message, message.getThrowable());
}
public void log(Level level, Message message) {
if (!isEnabled(level)) return;
if (message instanceof ParameterizedMessage) {
performLogging(level, null, message, ((ParameterizedMessage)
message).getThrowable());
} else {
performLogging(level, null, message, null);
}
}
public void log(Level level, Message message, Throwable throwable) {
if (!isEnabled(level)) return;
performLogging(level, null, message, throwable);
}
public void log(Level level, Marker marker, Message message) {
if (!isEnabled(level, marker)) return;
if (message instanceof ParameterizedMessage) {
performLogging(level, marker, message, ((ParameterizedMessage)
message).getThrowable());
} else {
performLogging(level, marker, message, null);
}
}
public void log(Level level, Marker marker, Message message, Throwable
throwable) {
if (!isEnabled(level, marker)) return;
performLogging(level, marker, message, throwable);
}
If a method with just a Message but no Throwable is called then this Message is
checked if it is a ParameterizedMessage. Otherwise null is used for the
Throwable. If, however, a method with an explicit Throwable is used then this
Throwable is used for logging.
The whole point of having the Throwable in ParameterizedMessage is simply
because create can only return a single value without creating additional
overhead caused by wrappers. The Throwable is not meant to be part of the
Message, it just happens to be like that directly after creation - but not
after deserialization! It's the job of the logging framework to handle the
Throwable in a sensible way.
Perhaps making getThrowable() package-private would make this more clear but
I'd have to move classes around in a non-logical way just to achieve this. Do
not want.
Joern.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]