8010309 : PlatformLogger: isLoggable performance / waste due to HashMap<Integer, Level> leads to Integer allocations (boxing)

Laurent Bourgès bourges.laurent at gmail.com
Thu Mar 28 09:19:02 UTC 2013


Dear Mandy & Peter,

I will test your patch asap (+ benchmark)

Few comments below:


> Webrev at:
>    http://cr.openjdk.java.net/~mchung/jdk8/webrevs/8010309/webrev.00/
>
> Please let me know if you don't agree with these changes or you find any
> performance issue.
>
> I have retained the part in webrev.08 that only defines loggerProxy and
> javaLoggerProxy.  I don't think the new defaultLoggerProxy variable is
> needed.  I prefer to use Class.forName to force load JavaLoggerProxy class
> to make very explicit that we don't want the class be initialized.
> JavaLoggerProxy.<clinit> calls LoggingProxy.parseLevel unconditionally that
> will throw an AssertionError if java.util.logging is not available. If
> JavaLoggerProxy class is initialized, j.u.logging should be present and I
> want to catch error if otherwise.  Also Class.forName will load its
> superclass LoggerProxy.
>

I think it leads to early initialization of JUL Levels (when JUL is maybe
not available or initialized) from PlatformLogger static initialization:

        // initialize javaLevel fields for mapping from Level enum ->
j.u.l.Level object
        static {
            for (Level level : Level.values()) {
                level.javaLevel = LoggingSupport.parseLevel(level.name());
            }
        }

I thought Level.javaLevel should only be defined ONCE JUL is available i.e.
once PlatformLogger.redirectPlatformLoggers() is called.
I propose to move the javaLevel initialization in this method:

    public static synchronized void redirectPlatformLoggers() {
        if (loggingEnabled || !LoggingSupport.isAvailable()) return;

            // Resolve JUL Levels ONLY when JUL is initialized by
application code:
            for (Level level : Level.values()) {
                level.javaLevel = LoggingSupport.parseLevel(level.name());
            }

...
}



>
> As for the PlatformLogger.Level enums, having the members to be defined in
> ascending order is fragile.  It'd be more reliable to specify the value in
> the constructor so that it can be used as the isLoggable comparsion in the
> same way as in the Level implementation.
>

Good idea.


>
> On 3/27/2013 9:44 AM, Peter Levart wrote:
>
> Hi Mandy, Laurent,
>
> It turns out that the API change (change of type for level parameter int
> -> enum Level) is entirely source-compatible. The tip of JDK8 (tl repo)
> builds without a problem. So no-one is currently using
> PlatformLogger.getLevel() method and assigning it to a variable or such...
>
>
> Great thanks.  That is what I expect too.
>

If getLevel() is useless, why keep it in this proprietary API ? (only for
test purposes) ?


>
>
>  Here's the webrev for this change:
>
>
> http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.enumapi.01/index.html
>
> Besides doing the replacement of type and renaming and removing of the
> unneeded stuff, I also did some re-arrangements:
>
> - introduced a common abstract superclass for both types of LoggerProxys
> (DefaultLoggerProxy, JavaLoggerProxy), since JavaLoggerProxy does not need
> the fields of DefaultLoggerProxy and now both concrete subclasses can be
> declared final (makes JIT even more happy). Also the abstract LoggerProxy
> could host some common logic (see below about formatting)...
>
>
> That's a good idea.
>
>
>  - DefaultLoggerProxy's levelValue/effectiveLevel were given names just
> the opposite of their meaning. I aligned them with terminology used in
> j.u.l.Logger and renamed levelValue to plain level.
>
>
> Swapping these 2 variables makes sense.
>
>
>  - introduced private method DefaultLoggerProxy.deriveEffectiveLevel(level)
> that currently just returns defaultLevel (INFO) when given null.
>
>
> Ok.
>
>
>  I think with a little more effort, it could be possible to emulate the
> behaviour of j.u.l.Logger which inherits from 1st parent logger that has
> non-null level assigned. Of course with all the caching and invalidation...
>
>
> The only way to change the level to a non-default one is via the logging
> configuration and enable logging.  That's why there is no need for
> DefaultLoggerProxy to inherit level from its parent logger.  Also there is
> no parent logger hierarchy maintained in DefaultLoggerProxy.
>
>
>  - instead of static final DefaultLoggerProxy.defaultStream I created a
> private static method outputStream() that returns System.err. To accomodate
> for the situation when System.err is changed dynamically.
>
>
> Ok.
>
>
>  - fixed the JavaLoggerProxy.isEnabled() method. Original code was:
>
>  532         boolean isEnabled() { 533             Object level = LoggingSupport.getLevel(javaLogger); 534             return level == null || level.equals(levelObjects.get(OFF)) == false;
>  535         }
>
>
> If 'level' is null then it can be that 1st parent that has a non-nul level
> is "OFF". I think that in such situation all the children that don't
> override the level should be disabled too. The following does exactly that:
>
>  597         boolean isEnabled() { 598             return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel);
>  599         }
>
>
>
> That is right.  Good catch.
>
>
>  That's all for 1st rev. Besides the effective level inheritance, the
> following method in JavaLoggerProxy also caught my eye:
>
>         void doLog(Level level, String msg, Object... params) {
>             if (!isLoggable(level)) {
>                 return;
>             }
>             // only pass String objects to the j.u.l.Logger which may
>             // be created by untrusted code
>             int len = (params != null) ? params.length : 0;
>             Object[] sparams = new String[len];
>             for (int i = 0; i < len; i++) {
>                 sparams [i] = String.valueOf(params[i]);
>             }
>             LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams);
>         }
>
> I think this could be improved if the DefaultLoggerProxy.formatMessage()
> is used instead of turning each parameter into a String. The method could
> be moved up to abstract LoggerProxy and used in both implementations so
> that common formatting is applied regardless of back-end used.
>
>
> Let's do this in a separate clean up as it's better to keep 8010309 focus
> on performance improvement (although we have mixed this bag with some
> renaming).
>

I disagree Peter: JUL has its own formatting code: patterns ... and more
efficient than DefaultLoggerProxy.formatMessage().

The good question relies in the comment: why convert object args into
String early as JUL can do formatting / conversion?
What does mean:
            // only pass String objects to the j.u.l.Logger which may
            // be created by untrusted code
?
to avoid security issues ?

Laurent



More information about the core-libs-dev mailing list