Coverage Report - com.jcabi.log.Logger
 
Classes in this File Line Coverage Branch Coverage Complexity
Logger
0%
0/82
0%
0/52
2.409
Logger$1
0%
0/9
0%
0/6
2.409
 
 1  
 /**
 2  
  * Copyright (c) 2012-2015, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.log;
 31  
 
 32  
 import java.io.ByteArrayOutputStream;
 33  
 import java.io.IOException;
 34  
 import java.io.OutputStream;
 35  
 import java.lang.reflect.Field;
 36  
 import java.lang.reflect.Modifier;
 37  
 import java.util.logging.Level;
 38  
 import lombok.EqualsAndHashCode;
 39  
 import lombok.ToString;
 40  
 import org.slf4j.LoggerFactory;
 41  
 
 42  
 /**
 43  
  * Universal logger, and adapter between your app and SLF4J API.
 44  
  *
 45  
  * <p>Instead of relying
 46  
  * on some logging engine you can use this class, which transforms all
 47  
  * messages to SLF4J. This approach gives you a perfect decoupling of business
 48  
  * logic and logging mechanism. All methods in the class are called
 49  
  * statically, without the necessity to instantiate the class.
 50  
  *
 51  
  * <p>Use it like this in any class, and in any package:
 52  
  *
 53  
  * <pre> package com.example.XXX;
 54  
  * import com.jcabi.log.Logger;
 55  
  * public class MyClass {
 56  
  *   public void foo(Integer num) {
 57  
  *     Logger.info(this, "foo(%d) just called", num);
 58  
  *   }
 59  
  * }</pre>
 60  
  *
 61  
  * <p>Or statically (pay attention to {@code MyClass.class}):
 62  
  *
 63  
  * <pre> public class MyClass {
 64  
  *   public static void foo(Integer num) {
 65  
  *     Logger.info(MyClass.class, "foo(%d) just called", num);
 66  
  *   }
 67  
  * }</pre>
 68  
  *
 69  
  * <p>Exact binding between SLF4J and logging facility has to be
 70  
  * specified in {@code pom.xml} of your project (or in classpath directly).
 71  
  *
 72  
  * <p>For performance reasons in most cases before sending a
 73  
  * {@code TRACE} or {@code DEBUG} log message you may check whether this
 74  
  * logging level is enabled in the project, e.g.:
 75  
  *
 76  
  * <pre> //...
 77  
  * if (Logger.isTraceEnabled(this)) {
 78  
  *   Logger.trace(this, "#foo() called");
 79  
  * }
 80  
  * //...</pre>
 81  
  *
 82  
  * <p>There is only one reason to do so - if you want to save time spent on
 83  
  * preparing of the arguments. By default, such a call is made inside every
 84  
  * method of {@link Logger} class.
 85  
  *
 86  
  * @author Yegor Bugayenko (yegor@teamed.io)
 87  
  * @version $Id: 4a9b34cc5ff447859231496ef8a29e2e5d8520cf $
 88  
  * @since 0.1
 89  
  */
 90  0
 @ToString
 91  0
 @EqualsAndHashCode
 92  
 @SuppressWarnings({ "PMD.TooManyMethods", "PMD.GodClass" })
 93  
 public final class Logger {
 94  
 
 95  
     /**
 96  
      * Empty array of objects.
 97  
      */
 98  0
     private static final Object[] EMPTY = {};
 99  
 
 100  
     /**
 101  
      * UTF-8.
 102  
      */
 103  
     private static final String UTF_8 = "UTF-8";
 104  
 
 105  
     /**
 106  
      * This is utility class.
 107  
      */
 108  0
     private Logger() {
 109  
         // intentionally empty
 110  0
     }
 111  
 
 112  
     /**
 113  
      * Format one string.
 114  
      * @param fmt The format
 115  
      * @param args List of arbitrary arguments
 116  
      * @return Formatted string
 117  
      */
 118  
     public static String format(final String fmt, final Object... args) {
 119  
         final String result;
 120  0
         if (args.length == 0) {
 121  0
             result = fmt;
 122  
         } else {
 123  0
             final PreFormatter pre = new PreFormatter(fmt, args);
 124  0
             result = String.format(pre.getFormat(), pre.getArguments());
 125  
         }
 126  0
         return result;
 127  
     }
 128  
 
 129  
     /**
 130  
      * Protocol one message, with {@code TRACE} priority level.
 131  
      * @param source The source of the logging operation
 132  
      * @param msg The text message to be logged
 133  
      * @since 0.7.11
 134  
      */
 135  
     public static void trace(final Object source, final String msg) {
 136  0
         Logger.trace(source, msg, Logger.EMPTY);
 137  0
     }
 138  
 
 139  
     /**
 140  
      * Protocol one message, with {@code TRACE} priority level.
 141  
      * @param source The source of the logging operation
 142  
      * @param msg The text message to be logged, with meta-tags
 143  
      * @param args List of arguments
 144  
      */
 145  
     public static void trace(
 146  
         final Object source,
 147  
         final String msg, final Object... args
 148  
     ) {
 149  0
         if (Logger.isTraceEnabled(source)) {
 150  0
             Logger.logger(source).trace(Logger.format(msg, args));
 151  
         }
 152  0
     }
 153  
 
 154  
     /**
 155  
      * Protocol one message, with {@code DEBUG} priority level.
 156  
      * @param source The source of the logging operation
 157  
      * @param msg The text message to be logged, with meta-tags
 158  
      * @since 0.7.11
 159  
      */
 160  
     public static void debug(final Object source, final String msg) {
 161  0
         Logger.debug(source, msg, Logger.EMPTY);
 162  0
     }
 163  
 
 164  
     /**
 165  
      * Protocol one message, with {@code DEBUG} priority level.
 166  
      * @param source The source of the logging operation
 167  
      * @param msg The text message to be logged, with meta-tags
 168  
      * @param args List of arguments
 169  
      */
 170  
     public static void debug(
 171  
         final Object source,
 172  
         final String msg, final Object... args
 173  
     ) {
 174  0
         if (Logger.isDebugEnabled(source)) {
 175  0
             Logger.logger(source).debug(Logger.format(msg, args));
 176  
         }
 177  0
     }
 178  
 
 179  
     /**
 180  
      * Protocol one message, with {@code INFO} priority level.
 181  
      * @param source The source of the logging operation
 182  
      * @param msg The text message to be logged
 183  
      * @since 0.7.11
 184  
      */
 185  
     public static void info(final Object source, final String msg) {
 186  0
         Logger.info(source, msg, Logger.EMPTY);
 187  0
     }
 188  
 
 189  
     /**
 190  
      * Protocol one message, with {@code INFO} priority level.
 191  
      * @param source The source of the logging operation
 192  
      * @param msg The text message to be logged, with meta-tags
 193  
      * @param args List of arguments
 194  
      */
 195  
     public static void info(
 196  
         final Object source,
 197  
         final String msg, final Object... args
 198  
     ) {
 199  0
         if (Logger.isInfoEnabled(source)) {
 200  0
             Logger.logger(source).info(Logger.format(msg, args));
 201  
         }
 202  0
     }
 203  
 
 204  
     /**
 205  
      * Protocol one message, with {@code WARN} priority level.
 206  
      * @param source The source of the logging operation
 207  
      * @param msg The text message to be logged
 208  
      * @since 0.7.11
 209  
      */
 210  
     public static void warn(final Object source, final String msg) {
 211  0
         Logger.warn(source, msg, Logger.EMPTY);
 212  0
     }
 213  
 
 214  
     /**
 215  
      * Protocol one message, with {@code WARN} priority level.
 216  
      * @param source The source of the logging operation
 217  
      * @param msg The text message to be logged, with meta-tags
 218  
      * @param args List of arguments
 219  
      */
 220  
     public static void warn(
 221  
         final Object source,
 222  
         final String msg, final Object... args
 223  
     ) {
 224  0
         if (Logger.isWarnEnabled(source)) {
 225  0
             Logger.logger(source).warn(Logger.format(msg, args));
 226  
         }
 227  0
     }
 228  
 
 229  
     /**
 230  
      * Protocol one message, with {@code ERROR} priority level.
 231  
      * @param source The source of the logging operation
 232  
      * @param msg The text message to be logged
 233  
      * @since 0.7.11
 234  
      */
 235  
     public static void error(final Object source, final String msg) {
 236  0
         Logger.error(source, msg, Logger.EMPTY);
 237  0
     }
 238  
 
 239  
     /**
 240  
      * Protocol one message, with {@code ERROR} priority level.
 241  
      * @param source The source of the logging operation
 242  
      * @param msg The text message to be logged, with meta-tags
 243  
      * @param args List of arguments
 244  
      */
 245  
     public static void error(final Object source,
 246  
         final String msg, final Object... args) {
 247  0
         Logger.logger(source).error(Logger.format(msg, args));
 248  0
     }
 249  
 
 250  
     /**
 251  
      * Validates whether {@code TRACE} priority level is enabled for
 252  
      * this particular logger.
 253  
      * @param source The source of the logging operation
 254  
      * @return Is it enabled?
 255  
      */
 256  
     public static boolean isTraceEnabled(final Object source) {
 257  0
         return Logger.logger(source).isTraceEnabled();
 258  
     }
 259  
 
 260  
     /**
 261  
      * Validates whether {@code DEBUG} priority level is enabled for
 262  
      * this particular logger.
 263  
      * @param source The source of the logging operation
 264  
      * @return Is it enabled?
 265  
      */
 266  
     public static boolean isDebugEnabled(final Object source) {
 267  0
         return Logger.logger(source).isDebugEnabled();
 268  
     }
 269  
 
 270  
     /**
 271  
      * Validates whether {@code INFO} priority level is enabled for
 272  
      * this particular logger.
 273  
      * @param source The source of the logging operation
 274  
      * @return Is it enabled?
 275  
      * @since 0.5
 276  
      */
 277  
     public static boolean isInfoEnabled(final Object source) {
 278  0
         return Logger.logger(source).isInfoEnabled();
 279  
     }
 280  
 
 281  
     /**
 282  
      * Validates whether {@code INFO} priority level is enabled for
 283  
      * this particular logger.
 284  
      * @param source The source of the logging operation
 285  
      * @return Is it enabled?
 286  
      * @since 0.5
 287  
      */
 288  
     public static boolean isWarnEnabled(final Object source) {
 289  0
         return Logger.logger(source).isWarnEnabled();
 290  
     }
 291  
 
 292  
     /**
 293  
      * Is the given logging level enabled?
 294  
      * @param level The level of logging
 295  
      * @param source The source of the logging operation
 296  
      * @return Is it enabled?
 297  
      * @since 0.13
 298  
      */
 299  
     public static boolean isEnabled(final Level level, final Object source) {
 300  0
         boolean enabled = false;
 301  0
         if (level.equals(Level.SEVERE)) {
 302  0
             enabled = Logger.logger(source).isErrorEnabled();
 303  0
         } else if (level.equals(Level.WARNING)) {
 304  0
             enabled = Logger.logger(source).isWarnEnabled();
 305  0
         } else if (level.equals(Level.INFO) || level.equals(Level.CONFIG)) {
 306  0
             enabled = Logger.logger(source).isInfoEnabled();
 307  0
         } else if (level.equals(Level.FINE) || level.equals(Level.ALL)) {
 308  0
             enabled = Logger.logger(source).isDebugEnabled();
 309  0
         } else if (level.equals(Level.FINER) || level.equals(Level.FINEST)) {
 310  0
             enabled = Logger.logger(source).isTraceEnabled();
 311  
         }
 312  0
         return enabled;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Log one line using the logging level specified.
 317  
      * @param level The level of logging
 318  
      * @param source The source of the logging operation
 319  
      * @param msg The text message to be logged
 320  
      * @param args Optional arguments for string formatting
 321  
      * @since 0.8
 322  
      * @checkstyle ParameterNumber (4 lines)
 323  
      */
 324  
     public static void log(final Level level, final Object source,
 325  
         final String msg, final Object... args) {
 326  0
         if (level.equals(Level.SEVERE)) {
 327  0
             Logger.error(source, msg, args);
 328  0
         } else if (level.equals(Level.WARNING)) {
 329  0
             Logger.warn(source, msg, args);
 330  0
         } else if (level.equals(Level.INFO) || level.equals(Level.CONFIG)) {
 331  0
             Logger.info(source, msg, args);
 332  0
         } else if (level.equals(Level.FINE) || level.equals(Level.ALL)) {
 333  0
             Logger.debug(source, msg, args);
 334  0
         } else if (level.equals(Level.FINER) || level.equals(Level.FINEST)) {
 335  0
             Logger.trace(source, msg, args);
 336  
         }
 337  0
     }
 338  
 
 339  
     /**
 340  
      * Returns an {@link OutputStream}, which converts all incoming data
 341  
      * into logging lines (separated by {@code \x0A} in UTF-8).
 342  
      * @param level The level of logging
 343  
      * @param source The source of the logging operation
 344  
      * @return Output stream directly pointed to the logging facility
 345  
      * @since 0.8
 346  
      * @see <a href="http://stackoverflow.com/questions/17258325">some discussion</a>
 347  
      * @checkstyle MagicNumberCheck (20 lines)
 348  
      */
 349  
     public static OutputStream stream(final Level level, final Object source) {
 350  
         // @checkstyle AnonInnerLengthCheck (50 lines)
 351  0
         return new OutputStream() {
 352  0
             private final transient ByteArrayOutputStream buffer =
 353  
                 new ByteArrayOutputStream();
 354  
             @Override
 355  
             public void write(final int data) throws IOException {
 356  0
                 if (data == '\n') {
 357  0
                     Logger.log(
 358  
                         level, source,
 359  
                         this.buffer.toString(Logger.UTF_8)
 360  
                     );
 361  0
                     this.buffer.reset();
 362  0
                 } else if (data >= 0x20 && data <= 0x7f) {
 363  0
                     this.buffer.write(data);
 364  
                 } else {
 365  0
                     this.buffer.write(
 366  
                         String.format(
 367  
                             "\\x%02x", data & 0xff
 368  
                         ).getBytes(Logger.UTF_8)
 369  
                     );
 370  
                 }
 371  0
             }
 372  
         };
 373  
     }
 374  
 
 375  
     /**
 376  
      * Set final static field in order to fix the %L log4j parameter.
 377  
      * @param field Field
 378  
      * @param value New value
 379  
      * @throws NoSuchFieldException If some problem
 380  
      * @throws IllegalAccessException If some problem
 381  
      * @checkstyle ThrowsCountCheck (4 lines)
 382  
      */
 383  
     private static void setFinalStatic(final Field field, final Object value)
 384  
         throws NoSuchFieldException, IllegalAccessException {
 385  0
         field.setAccessible(true);
 386  0
         final Field modifiers = Field.class.getDeclaredField("modifiers");
 387  0
         modifiers.setAccessible(true);
 388  0
         modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
 389  0
         field.set(null, value);
 390  0
     }
 391  
 
 392  
     /**
 393  
      * Get the instance of the logger for this particular caller.
 394  
      * @param source Source of the logging operation
 395  
      * @return The instance of {@code Logger} class
 396  
      */
 397  
     private static org.slf4j.Logger logger(final Object source) {
 398  
         final org.slf4j.Logger logger;
 399  0
         if (source instanceof Class) {
 400  0
             logger = LoggerFactory.getLogger((Class<?>) source);
 401  0
         } else if (source instanceof String) {
 402  0
             logger = LoggerFactory.getLogger(String.class.cast(source));
 403  
         } else {
 404  0
             logger = LoggerFactory.getLogger(source.getClass());
 405  
         }
 406  0
         if ("org.slf4j.impl.Log4jLoggerAdapter"
 407  
             .equals(logger.getClass().getName())) {
 408  
             try {
 409  0
                 final Field fqcn = logger.getClass()
 410  
                     .getDeclaredField("FQCN");
 411  0
                 setFinalStatic(fqcn, Logger.class.getName());
 412  0
             } catch (final NoSuchFieldException ex) {
 413  0
                 throw new IllegalStateException(ex);
 414  0
             } catch (final IllegalAccessException ex) {
 415  0
                 throw new IllegalStateException(ex);
 416  0
             }
 417  
         }
 418  0
         return logger;
 419  
     }
 420  
 
 421  
 }