Package org.waveprotocol.wave.util.logging

Source Code of org.waveprotocol.wave.util.logging.Log$MyLogRecord

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.waveprotocol.wave.util.logging;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
* Standard logging class, wraps a java.util.logging.Logger,
* augmenting it with convenience methods. Also provides a mapped diagnostic
* context for each thread. This context information is prepended to messages
* logged from that thread.
*
* The functionality to infer the caller of a logging method has been replicated
* from {@link LogRecord} because there was no easy way to use it correctly from
* this wrapper class.
*
*
*/
public class Log {

  /**
   * Version of LogRecord that can correctly infer its caller.
   */
  @VisibleForTesting
  static class MyLogRecord extends LogRecord {

    /**
     * Finds the caller of this class. Algorithm derived from code in
     * {@link java.util.logging.Logger}. This method necessary because it is not
     * possible to wrap logger in a way that allows the caller to be derived.
     *
     * @return information about the function that called into this class, or
     *         <code>null</code> if not found.
     */
    @VisibleForTesting
    static StackTraceElement findCaller() {

      // Get the stack trace.
      StackTraceElement stack[] = (new Throwable()).getStackTrace();
      String logClassName = Log.class.getName();

      // Scan down to a call to Log
      int i = 0;
      while (i < stack.length // \u2620
          && !logClassName.equals(stack[i].getClassName())) {
        i++;
      }

      // Scan down through further to first one to call Log
      while (i < stack.length // \u2620
          && logClassName.equals(stack[i].getClassName())) {
        i++;
      }
      return (i < stack.length ? stack[i] : null);
    }

    /** Mimics {@link LogRecord}'s needToInferCaller from superclass. */
    private boolean needToInferCaller = true;

    /** Constructor. Mimics signature of superclass. */
    public MyLogRecord(Level level, String msg, Throwable t) {
      super(level, msg);
      setThrown(t);
    }

    /** {@inheritDoc} */
    @Override
    public String getSourceClassName() {
      if (needToInferCaller) {
        inferCaller();
      }
      return super.getSourceClassName();
    }

    /** {@inheritDoc} */
    @Override
    public String getSourceMethodName() {
      if (needToInferCaller) {
        inferCaller();
      }
      return super.getSourceMethodName();
    }

    /** Infers the caller's source class and method name. */
    private void inferCaller() {
      needToInferCaller = false;

      StackTraceElement caller = findCaller();

      if (caller != null) {
        super.setSourceClassName(caller.getClassName());
        super.setSourceMethodName(caller.getMethodName());
      } else {
        super.setSourceClassName(null);
        super.setSourceMethodName(null);
      }
    }

    /** {@inheritDoc} */
    @Override
    public void setSourceClassName(String sourceClassName) {
      needToInferCaller = false;
      super.setSourceClassName(sourceClassName);
    }

    /** {@inheritDoc} */
    @Override
    public void setSourceMethodName(String sourceMethodName) {
      needToInferCaller = false;
      super.setSourceMethodName(sourceMethodName);
    }

  }

  /** Per-thread mapped diagnostic context. */
  private static final ThreadLocal<Map<String, String>> context =
      new ThreadLocal<Map<String, String>>() {
        /** {@inheritDoc} */
        @Override
        protected Map<String, String> initialValue() {
          // predictable iteration order gives nicer log messages
          return Maps.newLinkedHashMap();
        }
      };

  /**
   * Creates or lazily loads a logger. Currently just creates an instance but
   * allows us to do more clever stuff in the future.
   */
  public static Log get(Class<? extends Object> clazz) {
    return new Log(clazz);
  }

  /** The underlying logger to which this object logs messages. */
  private final Logger logger;

  /**
   * Creates a new logging object.
   *
   * @deprecated use {@link #get(Class)} instead.
   */
  @Deprecated
  private Log(Class<? extends Object> clazz) {
    this(Logger.getLogger(clazz.getName()));
  }

  /**
   * Creates a new logging object that will ultimately log records through the
   * given logger.
   */
  public Log(Logger logger) {
    this.logger = logger;
  }

  /**
   * Logs a message at level CONFIG.
   *
   * @param msg The message to log.
   */
  public void config(String msg) {
    log(Level.CONFIG, msg, null);
  }

  /**
   * Logs a message at level CONFIG.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void config(String msg, Throwable t) {
    log(Level.CONFIG, msg, t);
  }

  private String contextualiseMessage(String message) {
    Map<String, String> contextData = context.get();
    if (contextData.isEmpty()) {
      return message;
    } else {
      StringBuilder result = new StringBuilder();
      result.append(message);
      result.append(" -- ");
      result.append(contextData);
      return result.toString();
    }
  }

  /**
   * Logs a message at level FINE.
   *
   * @param msg The message to log.
   */
  public void fine(String msg) {
    log(Level.FINE, msg, null);
  }

  /**
   * Logs a message at level FINE.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void fine(String msg, Throwable t) {
    log(Level.FINE, msg, t);
  }

  /**
   * Logs a message at level FINER.
   *
   * @param msg The message to log.
   */
  public void finer(String msg) {
    log(Level.FINER, msg, null);
  }

  /**
   * Logs a message at level FINER.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void finer(String msg, Throwable t) {
    log(Level.FINER, msg, t);
  }

  /**
   * Logs a message at level FINEST.
   *
   * @param msg The message to log.
   */
  public void finest(String msg) {
    log(Level.FINEST, msg, null);
  }

  /**
   * Logs a message at level FINEST.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void finest(String msg, Throwable t) {
    log(Level.FINEST, msg, t);
  }

  /**
   * Gets the underlying logger.
   */
  public Logger getLogger() {
    return logger;
  }

  /**
   * Logs a message at level INFO.
   *
   * @param msg The message to log.
   */
  public void info(String msg) {
    log(Level.INFO, msg, null);
  }

  /**
   * Logs a message at level INFO.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void info(String msg, Throwable t) {
    log(Level.INFO, msg, t);
  }

  /**
   * Whether CONFIG messages are being output.
   */
  public boolean isConfigLoggable() {
    return logger.isLoggable(Level.CONFIG);
  }

  /**
   * Whether FINE messages are being output.
   */
  public boolean isFineLoggable() {
    return logger.isLoggable(Level.FINE);
  }

  /**
   * Whether FINER messages are being output.
   */
  public boolean isFinerLoggable() {
    return logger.isLoggable(Level.FINER);
  }

  /**
   * Whether FINEST messages are being output.
   */
  public boolean isFinestLoggable() {
    return logger.isLoggable(Level.FINEST);
  }

  /**
   * Whether INFO messages are being output.
   */
  public boolean isInfoLoggable() {
    return logger.isLoggable(Level.INFO);
  }

  /**
   * Whether SEVERE messages are being output.
   */
  public boolean isSevereLoggable() {
    return logger.isLoggable(Level.SEVERE);
  }

  /**
   * Whether WARNING messages are being output.
   */
  public boolean isWarningLoggable() {
    return logger.isLoggable(Level.WARNING);
  }

  /**
   * Generic log a message, prepended with the current thread's log context, at
   * a given level.
   */
  public void log(Level level, String msg, Throwable t) {
    if (!logger.isLoggable(level)) {
      return;
    }

    LogRecord record = new MyLogRecord(level, contextualiseMessage(msg), t);
    logger.log(record);
  }

  /** Saves a key/value pair into this thread's log context. */
  public void putContext(String key, String value) {
    context.get().put(key, value);
  }

  /**
   * Removes a key from this thread's log context.
   *
   * @param key The key to remove.
   * @return true if there was a value previously associated with the key, false
   *         if this call resulted in no changes being made.
   */
  public boolean removeContext(String key) {
    return context.get().remove(key) != null;
  }

  /**
   * Logs a message at level SEVERE.
   *
   * @param msg The message to log.
   */
  public void severe(String msg) {
    log(Level.SEVERE, msg, null);
  }

  /**
   * Logs a message at level SEVERE along with an exception that can be
   * extracted from the logs and reported.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void severe(String msg, Throwable t) {
    log(Level.SEVERE, msg, t);
  }

  /**
   * Logs a message at level WARNING.
   *
   * @param msg The message to log.
   */
  public void warning(String msg) {
    log(Level.WARNING, msg, null);
  }

  /**
   * Logs a message at level WARNING along with an exception that can be
   * extracted from the logs and reported.
   *
   * @param msg The message to log.
   * @param t The throwable to log with the message.
   */
  public void warning(String msg, Throwable t) {
    log(Level.WARNING, msg, t);
  }
}
TOP

Related Classes of org.waveprotocol.wave.util.logging.Log$MyLogRecord

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.