Peter,

I am convinced it is better if the LevelEnum belongs to JavaLogger class
(encapsulated so defered initialization).

Here is a typical scenario:
1 - PlatformLogger initialized (not JUL) => LoggerProxy must work so
LevelEnum can not be used and the former getLevelName() (switch case) is
required.
2 - JUL initialized: calls redirectPlatformLogger() => JavaLoggers created
=> LevelEnum initialized (Level object can be retrieved from LoggingSupport)

For example, JUL can be started programmatically so the loggingEnabled flag
is not enough to initialize the LevelEnum.object at step 1 and it will be
null. Then at step 2, LevelEnum.object are null so the JavaLogger is broken.

Here is a test of this scenario:
public static void main(String[] args) {

    final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11"); //
1: LoggerProxy

    if (log.isLoggable(PlatformLogger.INFO)) {
        log.info("PlatformLogger.INFO: LoggerProxy");
    }

    final Logger logger = Logger.getLogger("test");
//   calls PlatformLogger.redirectPlatformLoggers(); // 2 : JavaLogger

    if (log.isLoggable(PlatformLogger.INFO)) {
        log.info("PlatformLogger.INFO: JavaLogger");
    }

    logger.info("done");
}

Laurent

2013/3/21 Peter Levart <peter.lev...@gmail.com>

>  Hi Laurent, Mandy,
>
> Let me try one more time (it would be less optimal to have 2 switch
> statements instead of one). Here's the 3rd webrev:
>
>
> http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.03/index.html
>
> The changes:
> - added the comments
> - made LevelEnum private (to not be tempted to be used from outside the
> PlatformLogger)
> - used the loggingEnabled flag as a pre-check when trying to parse the
> level objects
>
>
> What do you think?
>
> Regards, Peter
>
> On 03/21/2013 04:06 PM, Laurent Bourgès wrote:
>
> Thanks Mandy for the clarifications !
>
> Peter, I propose to:
> - move the LevelEnum into the JavaLogger class in order to initialize it
> as later as possible i.e. after j.u.l calls redirectPlatformLoggers()
> - only use it in JavaLogger class (private enum) so revert changes to
> PlatformLogger and LoggerProxy classes
> - add few comments in the code to explain lazy initialization (see mandy's
> answer)
>
> Finally, could you keep my comment before the switch case (high occurences
> first) ?
>
> +        static LevelEnum forValue(int levelValue) {
> +            // higher occurences first (finest, fine, finer, info)+          
>   // based on isLoggable(level) calls (03/20/2013) +            // in jdk 
> project only (including generated sources)
> +            switch (levelValue) {+                case 
> PlatformLogger.FINEST:  return FINEST;  // 116 + 2257 matches in generated 
> files+                case PlatformLogger.FINE:    return FINE;    // 270
> +                case PlatformLogger.FINER:   return FINER;   // 157+         
>        case PlatformLogger.INFO:    return INFO;    // 39+                
> case PlatformLogger.WARNING: return WARNING; // 12
> +                case PlatformLogger.CONFIG:  return CONFIG;  // 6+           
>      case PlatformLogger.SEVERE:  return SEVERE;  // 1+                case 
> PlatformLogger.OFF:     return OFF;     // 0
> +                case PlatformLogger.ALL:     return ALL;     // 0+           
>      default:                     return UNKNOWN;+            }
> +        }
> cheers,
> Laurent
>
>
> 2013/3/21 Mandy Chung <mandy.ch...@oracle.com>
>
>>  Laurent, Peter,
>>
>> I haven't looked at the patch yet.  One thing worths mentioning is that
>> PlatformLogger was added for the platform code to use so as to avoid the
>> initialization of java.util.logging since logging is not turned on by
>> default and that to reduce the startup overhead.   In addition, it also
>> enables the elimination of the core classes dependency from
>> java.util.logging for modularization effort.  Therefore the PlatformLogger
>> only lazily looks up the Level object when java.util.logging is present and
>> also has been initialized by application code.
>>
>> Mandy
>>
>>
>> On 3/21/2013 7:45 AM, Peter Levart wrote:
>>
>> On 03/21/2013 03:30 PM, Laurent Bourgès wrote:
>>
>> Peter,
>>
>> your solution looks better; I wanted my patch to be simple, efficient and
>> only modify the JavaLogger class (localized changes).
>>
>> In your patch, I have doubts related to lazy and conditional
>> initialization in JavaLogger (static initialization):
>>
>> if (LoggingSupport.isAvailable()) {
>>     // initialize ...
>> }
>>
>>
>> In original code, if LoggingSupport.isAvailable() returned false, 
>> levelObjects
>> map remained empty and consequently null was used as the level object
>> passed to LoggingSupport methods. In LevelEnum I try to keep this logic.
>> When LevelEnum is first used, it's constants are initialized and level
>> objects with them. If
>> LoggingSupport.isAvailable() returns false, level objects are initialized
>> to null.
>>
>> I just noticed there's a bug in initialization of the LevelEnum.UNKNOWN
>> member constant. It should not try to parse level object. Here's an update:
>>
>>
>> http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.02/index.html
>>
>> But your concern might be correct. In my code LevelEnum is also used
>> from the LoggerProxy.format() method (in addition to all the places in
>> JavaLogger) to obtain the level name for formatting. If this method is
>> called the first time while LoggingSupport.isAvailable() returns false
>> and that happens before JavaLogger uses LevelEnum for the first time
>> (and at that time LoggingSupport.isAvailable() would return true), then
>> level objects will not be initialized correctly.
>>
>> Lazy initialization of level objects might help (for the price of added
>> complexity)...
>>
>> Regards, Peter
>>
>>  Does somebody have the knowledge about LoggingSupport.isAvailable()
>> and the lazy PlatformLogger initialization (JavaLogger are only used when 
>> j.u.l is initialized) ?
>>
>> What's happening if LoggingSupport.isAvailable() returns false in your patch 
>> ? - LevelEnum instances are incorrectly initialized: object field is null !
>> - PlatformLogger is then broken ... as Level object are required by j.u.l 
>> calls
>> To fix both problems, moving the LevelEnum into JavaLogger should help and 
>> check nulls on LevelEnum.object field.
>>
>> Thanks for your feedback,Laurent
>>
>>  2013/3/21 Peter Levart <peter.lev...@gmail.com>
>>
>>> On 03/21/2013 12:12 PM, Laurent Bourgès wrote:
>>>
>>>> Here is an improved patch tested on JDK7u13 and JDK8 internal build on
>>>> my
>>>>  machine linux x64:
>>>> http://jmmc.fr/~bourgesl/share/webrev-8010309/
>>>>
>>>> FYI, I removed completely the Map<Integer, Object> levelObjects and used
>>>> two arrays to perform the PlatformLogger's level (int) to j.u.l.Level
>>>> mapping:
>>>>
>>>> I decided to keep it simple as possible (no enum ...) and used a switch
>>>> case based on current level occurences:
>>>>
>>>
>>>  Hi Laurent,
>>>
>>> In my experience enums are just the right and most compact tool for
>>> coding such constant associations. Here's a quick try (ripping off your
>>> optimized switch ;-):
>>>
>>>
>>> http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.01/index.html
>>>
>>> ...it adds 12 LOC to the original PlatformLogger and is 43 LOC less tha
>>> your patch. In addition:
>>>
>>> - only one switch instead of two (to maintain)
>>> - no parallel IDX_ constants
>>>
>>> What do you think?
>>>
>>> Regards, Peter
>>>
>>>
>>>
>>>>   510         /**
>>>>   511          * Return the corresponding j.u.l.Level instance
>>>>   512          * @param level PlatformLogger level as integer
>>>>   513          * @return Object (j.u.l.Level instance) or null if no
>>>> matching level
>>>>   514          */
>>>>   515         private static Object getLevel(final int level) {
>>>>   516             if (levelObjects == null) {
>>>>   517                 return null;
>>>>   518             }
>>>>   519             // higher occurences first (finest, fine, finer, info)
>>>>   520             // based on isLoggable(level) calls (03/20/2013)
>>>>   521             // in jdk project only (including generated sources)
>>>>   522             switch (level) {
>>>>   523                 case FINEST  : return levelObjects[IDX_FINEST];
>>>> // 116 + 2257 matches in generated files
>>>>   524                 case FINE    : return levelObjects[IDX_FINE];
>>>>  // 270
>>>>   525                 case FINER   : return levelObjects[IDX_FINER];
>>>> // 157
>>>>   526                 case INFO    : return levelObjects[IDX_INFO];
>>>>  // 39
>>>>   527                 case WARNING : return levelObjects[IDX_WARNING];
>>>> // 12
>>>>   528                 case CONFIG  : return levelObjects[IDX_CONFIG];
>>>>  // 6
>>>>   529                 case SEVERE  : return levelObjects[IDX_SEVERE];
>>>>  // 1
>>>>   530                 case OFF     : return levelObjects[IDX_OFF];
>>>> // 0
>>>>   531                 case ALL     : return levelObjects[IDX_ALL];
>>>> // 0
>>>>   532                 default      : return null;
>>>>   533             }
>>>>   534         }
>>>>
>>>> I enhanced the PlatformLoggerTest class also and figured out that TLAB
>>>> optimized Integer allocations but I think the patch is still useful.
>>>>
>>>> Can you have a look to the patch ?
>>>> Should I write a jtreg test  (performance / GC issue) ?
>>>>
>>>> Cheers,
>>>> Laurent
>>>>
>>>>
>>>> 2013/3/20 Mandy Chung <mandy.ch...@oracle.com>
>>>>
>>>>    Hi Laurent,
>>>>>
>>>>> Thank you for signing the OCA.  Your contribution is very welcome.  You
>>>>> can submit a patch for this bug (see [1]) to Core libraries group which
>>>>> owns logging.  Jim Gish and I will sponsor it.
>>>>>
>>>>> Thanks
>>>>> Mandy
>>>>> [1] http://openjdk.java.net/contribute/
>>>>>
>>>>>
>>>>> On 3/20/2013 5:45 AM, Laurent Bourgès wrote:
>>>>>
>>>>> Hi mandy,
>>>>>
>>>>> Do you want me to propose an improved patch to remove the former Map
>>>>> and
>>>>> fix the getLevel() method ? or you prefer fix on your own ?
>>>>>
>>>>> Is it better to discuss the fix on the bug database (still not
>>>>> visible) ?
>>>>>
>>>>> Laurent
>>>>>
>>>>> 2013/3/19 Mandy Chung <mandy.ch...@oracle.com>
>>>>>
>>>>>    Hi Laurent,
>>>>>>
>>>>>> Thanks for the contribution.  I agree that the map can be replaced
>>>>>> with a
>>>>>> direct mapping from a int value to Level object and avoid the
>>>>>> autoboxing
>>>>>> conversion.
>>>>>>
>>>>>> I have filed a bug to track this and target this for JDK8:
>>>>>>    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id= 8010309
>>>>>>
>>>>>> Thanks
>>>>>> Mandy
>>>>>>
>>>>>>
>>>>>> On 3/19/13 5:19 AM, Laurent Bourgès wrote:
>>>>>>
>>>>>> Dear all,
>>>>>>
>>>>>> I run recently netbeans profiler on my swing application (Aspro2:
>>>>>> http://www.jmmc.fr/aspro
>>>>>> ) under linux x64 platform and figured out a
>>>>>> performance and waste issue related to PlatformLogger.
>>>>>>
>>>>>> Actually, the JavaLogger implementation uses a Map<Integer, Object>
>>>>>> levelObjects to store mapping between PlatformLogger's levels (int)
>>>>>> and JUL
>>>>>> Level instances.
>>>>>>
>>>>>> However, the isLoggable(int level) method is highly used by awt
>>>>>> project and
>>>>>> other JDK projects and it leads to many Integer allocations as
>>>>>> autoboxing
>>>>>> converts the level as int to an Integer instance used by the
>>>>>> Map.get() call.
>>>>>>
>>>>>>      /**
>>>>>>       * JavaLogger forwards all the calls to its corresponding
>>>>>>       * java.util.logging.Logger object.
>>>>>>       */
>>>>>>      static class JavaLogger extends LoggerProxy {
>>>>>>          private static final* Map<Integer, Object>* levelObjects =
>>>>>> new
>>>>>> HashMap<>();
>>>>>> ...
>>>>>>          public boolean isLoggable(*int level*) {
>>>>>>              return LoggingSupport.isLoggable(javaLogger, *
>>>>>> levelObjects.get(level)*);
>>>>>>          }
>>>>>>      }
>>>>>>
>>>>>> I wrote a simple test to illustrate that performance / waste problem:
>>>>>> PlatformLoggerTest that simply performs 1 million DISABLED log
>>>>>> statements:
>>>>>>              if (log.isLoggable(PlatformLogger.FINEST)) {
>>>>>>                  log.finest("test PlatformLogger.FINEST");
>>>>>>              }
>>>>>>
>>>>>> As you can see in screenshots:
>>>>>> - 5 million Integer instances are allocated
>>>>>> - Integer.valueOf(int) is called 5 million times (autoboxing)
>>>>>> - HashMap.get() represents 30% of the test time
>>>>>> - patched PlatformLogger is 3x times faster
>>>>>> [jvm options: -Xms8m -Xmx8m -verbose:gc]
>>>>>>
>>>>>> I suggest you to use an alternative way to map PlatformLogger's levels
>>>>>> (int) and JUL Level instances to fix that performance / memory issue:
>>>>>> I
>>>>>> added the getLevel(int level) method that performs a switch case to
>>>>>> return
>>>>>> the corresponding Level object (quick and dirty solution).
>>>>>>
>>>>>> I advocate this is not a very clean solution but I prefer efficiency
>>>>>> here:
>>>>>> any better solution may be appropriate to avoid at least Integer
>>>>>> allocation
>>>>>> and maybe enhance performance.
>>>>>>
>>>>>> Best regards,
>>>>>> Laurent Bourgès
>>>>>>
>>>>>> PS: here is the patch as text:
>>>>>>
>>>>>> # This patch file was generated by NetBeans IDE
>>>>>> # It uses platform neutral UTF-8 encoding and \n newlines.
>>>>>> --- PlatformLogger.java (6767)
>>>>>> +++ PlatformLogger.java (6768)
>>>>>> @@ -468,31 +468,13 @@
>>>>>>        * java.util.logging.Logger object.
>>>>>>        */
>>>>>>       static class JavaLogger extends LoggerProxy {
>>>>>> -        /** Note: using Integer keys leads to a lot of new Integer
>>>>>> instances !! */
>>>>>> -        private static final Map<Integer, Object> levelObjects = new
>>>>>> HashMap<>();
>>>>>> -        /** fastest mapping to Level instances from PlatformLogger
>>>>>> level
>>>>>> as integer */
>>>>>> -        private static final Object[] fastLevelObjects;
>>>>>> -
>>>>>> -
>>>>>> +        private static final Map<Integer, Object> levelObjects =
>>>>>> +            new HashMap<>();
>>>>>> +
>>>>>>           static {
>>>>>>               if (LoggingSupport.isAvailable()) {
>>>>>>                   // initialize the map to Level objects
>>>>>>                   getLevelObjects();
>>>>>> -
>>>>>> -                // initialize the fastLevelObjects:
>>>>>> -                fastLevelObjects = new Object[] {
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(OFF)),
>>>>>>     //
>>>>>> 0
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(SEVERE)),
>>>>>>    //
>>>>>> 1
>>>>>> -
>>>>>>  LoggingSupport.parseLevel(getLevelName(WARNING)),   //
>>>>>> 2
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(INFO)),
>>>>>>    //
>>>>>> 3
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(CONFIG)),
>>>>>>    //
>>>>>> 4
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(FINE)),
>>>>>>    //
>>>>>> 5
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(FINER)),
>>>>>>     //
>>>>>> 6
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(FINEST)),
>>>>>>    //
>>>>>> 7
>>>>>> -                    LoggingSupport.parseLevel(getLevelName(ALL))
>>>>>>    //
>>>>>> 8
>>>>>> -                };
>>>>>> -            } else {
>>>>>> -                fastLevelObjects = null; // check null
>>>>>>               }
>>>>>>           }
>>>>>>
>>>>>> @@ -515,7 +497,7 @@
>>>>>>               this.javaLogger = LoggingSupport.getLogger(name);
>>>>>>               if (level != 0) {
>>>>>>                   // level has been updated and so set the Logger's
>>>>>> level
>>>>>> -                LoggingSupport.setLevel(javaLogger, getLevel(level));
>>>>>> +                LoggingSupport.setLevel(javaLogger,
>>>>>> levelObjects.get(level));
>>>>>>               }
>>>>>>           }
>>>>>>
>>>>>> @@ -526,11 +508,11 @@
>>>>>>           * not be updated.
>>>>>>           */
>>>>>>           void doLog(int level, String msg) {
>>>>>> -            LoggingSupport.log(javaLogger, getLevel(level), msg);
>>>>>> +            LoggingSupport.log(javaLogger, levelObjects.get(level),
>>>>>> msg);
>>>>>>           }
>>>>>>
>>>>>>           void doLog(int level, String msg, Throwable t) {
>>>>>> -            LoggingSupport.log(javaLogger, getLevel(level), msg, t);
>>>>>> +            LoggingSupport.log(javaLogger, levelObjects.get(level),
>>>>>> msg,
>>>>>> t);
>>>>>>           }
>>>>>>
>>>>>>           void doLog(int level, String msg, Object... params) {
>>>>>> @@ -544,12 +526,12 @@
>>>>>>               for (int i = 0; i < len; i++) {
>>>>>>                   sparams [i] = String.valueOf(params[i]);
>>>>>>               }
>>>>>> -            LoggingSupport.log(javaLogger, getLevel(level), msg,
>>>>>> sparams);
>>>>>> +            LoggingSupport.log(javaLogger, levelObjects.get(level),
>>>>>> msg,
>>>>>> sparams);
>>>>>>           }
>>>>>>
>>>>>>           boolean isEnabled() {
>>>>>>               Object level = LoggingSupport.getLevel(javaLogger);
>>>>>> -            return level == null || level.equals(getLevel(OFF)) ==
>>>>>> false;
>>>>>> +            return level == null ||
>>>>>> level.equals(levelObjects.get(OFF)) ==
>>>>>> false;
>>>>>>           }
>>>>>>
>>>>>>           int getLevel() {
>>>>>> @@ -566,34 +548,12 @@
>>>>>>
>>>>>>           void setLevel(int newLevel) {
>>>>>>               levelValue = newLevel;
>>>>>> -            LoggingSupport.setLevel(javaLogger, getLevel(newLevel));
>>>>>> +            LoggingSupport.setLevel(javaLogger,
>>>>>> levelObjects.get(newLevel));
>>>>>>           }
>>>>>>
>>>>>>           public boolean isLoggable(int level) {
>>>>>> -            return LoggingSupport.isLoggable(javaLogger,
>>>>>> getLevel(level));
>>>>>> +            return LoggingSupport.isLoggable(javaLogger,
>>>>>> levelObjects.get(level));
>>>>>>           }
>>>>>> -
>>>>>> -        /**
>>>>>> -         * Return the corresponding level object (fastest
>>>>>> implementation)
>>>>>> -         * @param level PlatformLogger level as primitive integer
>>>>>> -         * @return Object (JUL Level instance)
>>>>>> -         */
>>>>>> -        private static Object getLevel(final int level) {
>>>>>> -            // higher occurences first (finest, fine, finer, info):
>>>>>> -            switch (level) {
>>>>>> -                case FINEST  : return fastLevelObjects[7];
>>>>>> -                case FINE    : return fastLevelObjects[5];
>>>>>> -                case FINER   : return fastLevelObjects[6];
>>>>>> -                case INFO    : return fastLevelObjects[3];
>>>>>> -                case CONFIG  : return fastLevelObjects[4];
>>>>>> -                case WARNING : return fastLevelObjects[2];
>>>>>> -                case SEVERE  : return fastLevelObjects[1];
>>>>>> -                case ALL     : return fastLevelObjects[8];
>>>>>> -                case OFF     : return fastLevelObjects[0];
>>>>>> -                default      : return null;
>>>>>> -            }
>>>>>> -        }
>>>>>> -
>>>>>>       }
>>>>>>
>>>>>>       private static String getLevelName(int level) {
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>
>>
>>
>>
>
>

Reply via email to