1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.istack.internal.logging;
  27 
  28 import com.sun.istack.internal.NotNull;
  29 
  30 import java.util.StringTokenizer;
  31 import java.util.logging.Level;
  32 
  33 /**
  34  * This is a helper class that provides some convenience methods wrapped around the
  35  * standard {@link java.util.logging.Logger} interface.
  36  *
  37  * The class also makes sure that logger names of each Metro subsystem are consistent
  38  * with each other.
  39  *
  40  * @author Marek Potociar <marek.potociar at sun.com>
  41  * @author Fabian Ritzmann
  42  */
  43 public class Logger {
  44 
  45     private static final String WS_LOGGING_SUBSYSTEM_NAME_ROOT = "com.sun.metro";
  46     private static final String ROOT_WS_PACKAGE = "com.sun.xml.internal.ws.";
  47     //
  48     private static final Level METHOD_CALL_LEVEL_VALUE = Level.FINEST;
  49     //
  50     private final String componentClassName;
  51     private final java.util.logging.Logger logger;
  52 
  53     /**
  54      * Prevents creation of a new instance of this Logger unless used by a subclass.
  55      */
  56     protected Logger(final String systemLoggerName, final String componentName) {
  57         this.componentClassName = "[" + componentName + "] ";
  58         this.logger = java.util.logging.Logger.getLogger(systemLoggerName);
  59     }
  60 
  61     /**
  62      * <p>
  63      * The factory method returns preconfigured Logger wrapper for the class. Method calls
  64      * {@link #getSystemLoggerName(java.lang.Class)} to generate default logger name.
  65      * </p>
  66      * <p>
  67      * Since there is no caching implemented, it is advised that the method is called only once
  68      * per a class in order to initialize a final static logger variable, which is then used
  69      * through the class to perform actual logging tasks.
  70      * </p>
  71      *
  72      * @param componentClass class of the component that will use the logger instance. Must not be {@code null}.
  73      * @return logger instance preconfigured for use with the component
  74      * @throws NullPointerException if the componentClass parameter is {@code null}.
  75      */
  76     public static @NotNull Logger getLogger(final @NotNull Class<?> componentClass) {
  77         return new Logger(getSystemLoggerName(componentClass), componentClass.getName());
  78     }
  79 
  80     /**
  81      * The factory method returns preconfigured Logger wrapper for the class. Since there is no caching implemented,
  82      * it is advised that the method is called only once per a class in order to initialize a final static logger variable,
  83      * which is then used through the class to perform actual logging tasks.
  84      *
  85      * This method should be only used in a special cases when overriding of a default logger name derived from the
  86      * package of the component class is needed. For all common use cases please use {@link #getLogger(java.lang.Class)}
  87      * method.
  88      *
  89      * @param customLoggerName custom name of the logger.
  90      * @param componentClass class of the component that will use the logger instance. Must not be {@code null}.
  91      * @return logger instance preconfigured for use with the component
  92      * @throws NullPointerException if the componentClass parameter is {@code null}.
  93      *
  94      * @see #getLogger(java.lang.Class)
  95      */
  96     public static @NotNull Logger getLogger(final @NotNull String customLoggerName, final @NotNull Class<?> componentClass) {
  97         return new Logger(customLoggerName, componentClass.getName());
  98     }
  99 
 100     /**
 101      * Calculates the subsystem suffix based on the package of the component class
 102      * @param componentClass class of the component that will use the logger instance. Must not be {@code null}.
 103      * @return system logger name for the given {@code componentClass} instance
 104      */
 105     static final String getSystemLoggerName(@NotNull Class<?> componentClass) {
 106         StringBuilder sb = new StringBuilder(componentClass.getPackage().getName());
 107         final int lastIndexOfWsPackage = sb.lastIndexOf(ROOT_WS_PACKAGE);
 108         if (lastIndexOfWsPackage > -1) {
 109             sb.replace(0, lastIndexOfWsPackage + ROOT_WS_PACKAGE.length(), "");
 110 
 111             StringTokenizer st = new StringTokenizer(sb.toString(), ".");
 112             sb = new StringBuilder(WS_LOGGING_SUBSYSTEM_NAME_ROOT).append(".");
 113             if (st.hasMoreTokens()) {
 114                 String token = st.nextToken();
 115                 if ("api".equals(token)) {
 116                     token = st.nextToken();
 117                 }
 118                 sb.append(token);
 119             }
 120         }
 121 
 122         return sb.toString();
 123     }
 124 
 125     public void log(final Level level, final String message) {
 126         if (!this.logger.isLoggable(level)) {
 127             return;
 128         }
 129         logger.logp(level, componentClassName, getCallerMethodName(), message);
 130     }
 131 
 132     public void log(final Level level, final String message, Object param1) {
 133         if (!this.logger.isLoggable(level)) {
 134             return;
 135         }
 136         logger.logp(level, componentClassName, getCallerMethodName(), message, param1);
 137     }
 138 
 139     public void log(final Level level, final String message, Object[] params) {
 140         if (!this.logger.isLoggable(level)) {
 141             return;
 142         }
 143         logger.logp(level, componentClassName, getCallerMethodName(), message, params);
 144     }
 145 
 146     public void log(final Level level, final String message, final Throwable thrown) {
 147         if (!this.logger.isLoggable(level)) {
 148             return;
 149         }
 150         logger.logp(level, componentClassName, getCallerMethodName(), message, thrown);
 151     }
 152 
 153     public void finest(final String message) {
 154         if (!this.logger.isLoggable(Level.FINEST)) {
 155             return;
 156         }
 157         logger.logp(Level.FINEST, componentClassName, getCallerMethodName(), message);
 158     }
 159 
 160     public void finest(final String message, Object[] params) {
 161         if (!this.logger.isLoggable(Level.FINEST)) {
 162             return;
 163         }
 164         logger.logp(Level.FINEST, componentClassName, getCallerMethodName(), message, params);
 165     }
 166 
 167     public void finest(final String message, final Throwable thrown) {
 168         if (!this.logger.isLoggable(Level.FINEST)) {
 169             return;
 170         }
 171         logger.logp(Level.FINEST, componentClassName, getCallerMethodName(), message, thrown);
 172     }
 173 
 174     public void finer(final String message) {
 175         if (!this.logger.isLoggable(Level.FINER)) {
 176             return;
 177         }
 178         logger.logp(Level.FINER, componentClassName, getCallerMethodName(), message);
 179     }
 180 
 181     public void finer(final String message, Object[] params) {
 182         if (!this.logger.isLoggable(Level.FINER)) {
 183             return;
 184         }
 185         logger.logp(Level.FINER, componentClassName, getCallerMethodName(), message, params);
 186     }
 187 
 188     public void finer(final String message, final Throwable thrown) {
 189         if (!this.logger.isLoggable(Level.FINER)) {
 190             return;
 191         }
 192         logger.logp(Level.FINER, componentClassName, getCallerMethodName(), message, thrown);
 193     }
 194 
 195     public void fine(final String message) {
 196         if (!this.logger.isLoggable(Level.FINE)) {
 197             return;
 198         }
 199         logger.logp(Level.FINE, componentClassName, getCallerMethodName(), message);
 200     }
 201 
 202     public void fine(final String message, final Throwable thrown) {
 203         if (!this.logger.isLoggable(Level.FINE)) {
 204             return;
 205         }
 206         logger.logp(Level.FINE, componentClassName, getCallerMethodName(), message, thrown);
 207     }
 208 
 209     public void info(final String message) {
 210         if (!this.logger.isLoggable(Level.INFO)) {
 211             return;
 212         }
 213         logger.logp(Level.INFO, componentClassName, getCallerMethodName(), message);
 214     }
 215 
 216     public void info(final String message, Object[] params) {
 217         if (!this.logger.isLoggable(Level.INFO)) {
 218             return;
 219         }
 220         logger.logp(Level.INFO, componentClassName, getCallerMethodName(), message, params);
 221     }
 222 
 223     public void info(final String message, final Throwable thrown) {
 224         if (!this.logger.isLoggable(Level.INFO)) {
 225             return;
 226         }
 227         logger.logp(Level.INFO, componentClassName, getCallerMethodName(), message, thrown);
 228     }
 229 
 230     public void config(final String message) {
 231         if (!this.logger.isLoggable(Level.CONFIG)) {
 232             return;
 233         }
 234         logger.logp(Level.CONFIG, componentClassName, getCallerMethodName(), message);
 235     }
 236 
 237     public void config(final String message, Object[] params) {
 238         if (!this.logger.isLoggable(Level.CONFIG)) {
 239             return;
 240         }
 241         logger.logp(Level.CONFIG, componentClassName, getCallerMethodName(), message, params);
 242     }
 243 
 244     public void config(final String message, final Throwable thrown) {
 245         if (!this.logger.isLoggable(Level.CONFIG)) {
 246             return;
 247         }
 248         logger.logp(Level.CONFIG, componentClassName, getCallerMethodName(), message, thrown);
 249     }
 250 
 251     public void warning(final String message) {
 252         if (!this.logger.isLoggable(Level.WARNING)) {
 253             return;
 254         }
 255         logger.logp(Level.WARNING, componentClassName, getCallerMethodName(), message);
 256     }
 257 
 258     public void warning(final String message, Object[] params) {
 259         if (!this.logger.isLoggable(Level.WARNING)) {
 260             return;
 261         }
 262         logger.logp(Level.WARNING, componentClassName, getCallerMethodName(), message, params);
 263     }
 264 
 265     public void warning(final String message, final Throwable thrown) {
 266         if (!this.logger.isLoggable(Level.WARNING)) {
 267             return;
 268         }
 269         logger.logp(Level.WARNING, componentClassName, getCallerMethodName(), message, thrown);
 270     }
 271 
 272     public void severe(final String message) {
 273         if (!this.logger.isLoggable(Level.SEVERE)) {
 274             return;
 275         }
 276         logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), message);
 277     }
 278 
 279     public void severe(final String message, Object[] params) {
 280         if (!this.logger.isLoggable(Level.SEVERE)) {
 281             return;
 282         }
 283         logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), message, params);
 284     }
 285 
 286     public void severe(final String message, final Throwable thrown) {
 287         if (!this.logger.isLoggable(Level.SEVERE)) {
 288             return;
 289         }
 290         logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), message, thrown);
 291     }
 292 
 293     public boolean isMethodCallLoggable() {
 294         return this.logger.isLoggable(METHOD_CALL_LEVEL_VALUE);
 295     }
 296 
 297     public boolean isLoggable(final Level level) {
 298         return this.logger.isLoggable(level);
 299     }
 300 
 301     public void setLevel(final Level level) {
 302         this.logger.setLevel(level);
 303     }
 304 
 305     public void entering() {
 306         if (!this.logger.isLoggable(METHOD_CALL_LEVEL_VALUE)) {
 307             return;
 308         }
 309 
 310         logger.entering(componentClassName, getCallerMethodName());
 311     }
 312 
 313     public void entering(final Object... parameters) {
 314         if (!this.logger.isLoggable(METHOD_CALL_LEVEL_VALUE)) {
 315             return;
 316         }
 317 
 318         logger.entering(componentClassName, getCallerMethodName(), parameters);
 319     }
 320 
 321     public void exiting() {
 322         if (!this.logger.isLoggable(METHOD_CALL_LEVEL_VALUE)) {
 323             return;
 324         }
 325         logger.exiting(componentClassName, getCallerMethodName());
 326     }
 327 
 328     public void exiting(final Object result) {
 329         if (!this.logger.isLoggable(METHOD_CALL_LEVEL_VALUE)) {
 330             return;
 331         }
 332         logger.exiting(componentClassName, getCallerMethodName(), result);
 333     }
 334 
 335     /**
 336      * Method logs {@code exception}'s message as a {@code SEVERE} logging level
 337      * message.
 338      * <p/>
 339      * If {@code cause} parameter is not {@code null}, it is logged as well and
 340      * {@code exception} original cause is initialized with instance referenced
 341      * by {@code cause} parameter.
 342      *
 343      * @param exception exception whose message should be logged. Must not be
 344      *        {@code null}.
 345      * @param cause initial cause of the exception that should be logged as well
 346      *        and set as {@code exception}'s original cause. May be {@code null}.
 347      * @return the same exception instance that was passed in as the {@code exception}
 348      *         parameter.
 349      */
 350     public <T extends Throwable> T logSevereException(final T exception, final Throwable cause) {
 351         if (this.logger.isLoggable(Level.SEVERE)) {
 352             if (cause == null) {
 353                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage());
 354             } else {
 355                 exception.initCause(cause);
 356                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage(), cause);
 357             }
 358         }
 359 
 360         return exception;
 361     }
 362 
 363     /**
 364      * Method logs {@code exception}'s message as a {@code SEVERE} logging level
 365      * message.
 366      * <p/>
 367      * If {@code logCause} parameter is {@code true}, {@code exception}'s original
 368      * cause is logged as well (if exists). This may be used in cases when
 369      * {@code exception}'s class provides constructor to initialize the original
 370      * cause. In such case you do not need to use
 371      * {@link #logSevereException(Throwable, Throwable)}
 372      * method version but you might still want to log the original cause as well.
 373      *
 374      * @param exception exception whose message should be logged. Must not be
 375      *        {@code null}.
 376      * @param logCause deterimnes whether initial cause of the exception should
 377      *        be logged as well
 378      * @return the same exception instance that was passed in as the {@code exception}
 379      *         parameter.
 380      */
 381     public <T extends Throwable> T logSevereException(final T exception, final boolean logCause) {
 382         if (this.logger.isLoggable(Level.SEVERE)) {
 383             if (logCause && exception.getCause() != null) {
 384                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage(), exception.getCause());
 385             } else {
 386                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage());
 387             }
 388         }
 389 
 390         return exception;
 391     }
 392 
 393     /**
 394      * Same as {@link #logSevereException(Throwable, boolean) logSevereException(exception, true)}.
 395      */
 396     public <T extends Throwable> T logSevereException(final T exception) {
 397         if (this.logger.isLoggable(Level.SEVERE)) {
 398             if (exception.getCause() == null) {
 399                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage());
 400             } else {
 401                 logger.logp(Level.SEVERE, componentClassName, getCallerMethodName(), exception.getMessage(), exception.getCause());
 402             }
 403         }
 404 
 405         return exception;
 406     }
 407 
 408     /**
 409      * Method logs {@code exception}'s message at the logging level specified by the
 410      * {@code level} argument.
 411      * <p/>
 412      * If {@code cause} parameter is not {@code null}, it is logged as well and
 413      * {@code exception} original cause is initialized with instance referenced
 414      * by {@code cause} parameter.
 415      *
 416      * @param exception exception whose message should be logged. Must not be
 417      *        {@code null}.
 418      * @param cause initial cause of the exception that should be logged as well
 419      *        and set as {@code exception}'s original cause. May be {@code null}.
 420      * @param level loging level which should be used for logging
 421      * @return the same exception instance that was passed in as the {@code exception}
 422      *         parameter.
 423      */
 424     public <T extends Throwable> T logException(final T exception, final Throwable cause, final Level level) {
 425         if (this.logger.isLoggable(level)) {
 426             if (cause == null) {
 427                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage());
 428             } else {
 429                 exception.initCause(cause);
 430                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage(), cause);
 431             }
 432         }
 433 
 434         return exception;
 435     }
 436 
 437     /**
 438      * Method logs {@code exception}'s message at the logging level specified by the
 439      * {@code level} argument.
 440      * <p/>
 441      * If {@code logCause} parameter is {@code true}, {@code exception}'s original
 442      * cause is logged as well (if exists). This may be used in cases when
 443      * {@code exception}'s class provides constructor to initialize the original
 444      * cause. In such case you do not need to use
 445      * {@link #logException(Throwable, Throwable, Level) logException(exception, cause, level)}
 446      * method version but you might still want to log the original cause as well.
 447      *
 448      * @param exception exception whose message should be logged. Must not be
 449      *        {@code null}.
 450      * @param logCause deterimnes whether initial cause of the exception should
 451      *        be logged as well
 452      * @param level loging level which should be used for logging
 453      * @return the same exception instance that was passed in as the {@code exception}
 454      *         parameter.
 455      */
 456     public <T extends Throwable> T logException(final T exception, final boolean logCause, final Level level) {
 457         if (this.logger.isLoggable(level)) {
 458             if (logCause && exception.getCause() != null) {
 459                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage(), exception.getCause());
 460             } else {
 461                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage());
 462             }
 463         }
 464 
 465         return exception;
 466     }
 467 
 468     /**
 469      * Same as {@link #logException(Throwable, Throwable, Level)
 470      * logException(exception, true, level)}.
 471      */
 472     public <T extends Throwable> T logException(final T exception, final Level level) {
 473         if (this.logger.isLoggable(level)) {
 474             if (exception.getCause() == null) {
 475                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage());
 476             } else {
 477                 logger.logp(level, componentClassName, getCallerMethodName(), exception.getMessage(), exception.getCause());
 478             }
 479         }
 480 
 481         return exception;
 482     }
 483 
 484     /**
 485      * Function returns the name of the caller method for the method executing this
 486      * function.
 487      *
 488      * @return caller method name from the call stack of the current {@link Thread}.
 489      */
 490     private static String getCallerMethodName() {
 491         return getStackMethodName(5);
 492     }
 493 
 494     /**
 495      * Method returns the name of the method that is on the {@code methodIndexInStack}
 496      * position in the call stack of the current {@link Thread}.
 497      *
 498      * @param methodIndexInStack index to the call stack to get the method name for.
 499      * @return the name of the method that is on the {@code methodIndexInStack}
 500      *         position in the call stack of the current {@link Thread}.
 501      */
 502     private static String getStackMethodName(final int methodIndexInStack) {
 503         final String methodName;
 504 
 505         final StackTraceElement[] stack = Thread.currentThread().getStackTrace();
 506         if (stack.length > methodIndexInStack + 1) {
 507             methodName = stack[methodIndexInStack].getMethodName();
 508         } else {
 509             methodName = "UNKNOWN METHOD";
 510         }
 511 
 512         return methodName;
 513     }
 514 
 515 }