This project has retired. For details please refer to its Attic page.
Source code
001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.reef.util.logging;
020
021import java.io.IOException;
022import java.io.PrintWriter;
023import java.io.StringWriter;
024import java.util.ArrayList;
025import java.util.Date;
026import java.util.List;
027import java.util.logging.Formatter;
028import java.util.logging.LogManager;
029import java.util.logging.LogRecord;
030
031/**
032 * A denser logging format for REEF that is similar to the standard SimpleFormatter.
033 * <p>
034 * The following config properties are available:
035 * <p>
036 * * `org.apache.reef.util.logging.ThreadLogFormatter.format`
037 * is a format string for String.format() that takes same arguments and in
038 * the same order as the standard SimpleFormatter, plus the thread name:
039 * 1. date
040 * 2. class and method name
041 * 3. logger name
042 * 4. logging level
043 * 5. message
044 * 6. stack trace
045 * 7. thread name
046 * <p>
047 * * `org.apache.reef.util.logging.ThreadLogFormatter.dropPrefix`
048 * contains a comma-separated list of package name prefixes that should be
049 * removed from the class name for logging. e.g. value `com.microsoft.,org.apache.`
050 * will have the formatter write class `org.apache.reef.util.logging.Config` as
051 * `reef.util.logging.Config`. (Note the dot at the end of the prefix).
052 */
053public final class ThreadLogFormatter extends Formatter {
054
055  private static final String DEFAULT_FORMAT = "%1$tF %1$tT,%1$tL %4$s %2$s %7$s | %5$s%6$s%n";
056
057  private final List<String> dropPrefix = new ArrayList<>();
058  private final Date date = new Date();
059  private final String logFormat;
060
061  public ThreadLogFormatter() {
062
063    super();
064    final LogManager logManager = LogManager.getLogManager();
065    final String className = this.getClass().getName();
066
067    final String format = logManager.getProperty(className + ".format");
068    this.logFormat = format != null ? format : DEFAULT_FORMAT;
069
070    final String rawDropStr = logManager.getProperty(className + ".dropPrefix");
071    if (rawDropStr != null) {
072      for (String prefix : rawDropStr.trim().split(",")) {
073        prefix = prefix.trim();
074        if (!prefix.isEmpty()) {
075          this.dropPrefix.add(prefix);
076        }
077      }
078    }
079  }
080
081  /**
082   * Format the log string. Internally, it uses `String.format()` that takes same
083   * arguments and in the same order as the standard SimpleFormatter, plus the thread name:
084   * 1. date
085   * 2. class and method name
086   * 3. logger name
087   * 4. logging level
088   * 5. message
089   * 6. stack trace
090   * 7. thread name
091   *
092   * @return string to be written to the log.
093   */
094  @Override
095  public String format(final LogRecord logRecord) {
096    this.date.setTime(System.currentTimeMillis());
097    return String.format(
098        this.logFormat,
099        this.date,
100        this.trimPrefix(logRecord.getSourceClassName()) + "." + logRecord.getSourceMethodName(),
101        logRecord.getLoggerName(),
102        logRecord.getLevel().getLocalizedName(),
103        formatMessage(logRecord),
104        this.getStackTrace(logRecord.getThrown()),
105        Thread.currentThread().getName());
106  }
107
108  /**
109   * Check if the class name starts with one of the prefixes specified in `dropPrefix`,
110   * and remove it. e.g. for class name `org.apache.reef.util.logging.Config` and
111   * prefix `com.microsoft.` (note the trailing dot), the result will be
112   * `reef.util.logging.Config`
113   */
114  private String trimPrefix(final String className) {
115    for (final String prefix : this.dropPrefix) {
116      if (className.startsWith(prefix)) {
117        return className.substring(prefix.length());
118      }
119    }
120    return className;
121  }
122
123  /**
124   * @return a string that contains stack trace of a given exception.
125   * if `error` is null, return an empty string.
126   */
127  private String getStackTrace(final Throwable error) {
128    if (error != null) {
129      try (final StringWriter sw = new StringWriter();
130           final PrintWriter pw = new PrintWriter(sw)) {
131        pw.println();
132        error.printStackTrace(pw);
133        return sw.toString();
134      } catch (final IOException ex) {
135        // should never happen
136        throw new RuntimeException("Unexpected error while logging stack trace", ex);
137      }
138    }
139    return "";
140  }
141}