All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.unkrig.commons.util.logging.formatter.PrintfFormatter Maven / Gradle / Ivy

There is a newer version: 1.2.19
Show newest version

/*
 * de.unkrig.commons - A general-purpose Java class library
 *
 * Copyright (c) 2011, Arno Unkrig
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *       following disclaimer in the documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package de.unkrig.commons.util.logging.formatter;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import de.unkrig.commons.lang.StringUtil;
import de.unkrig.commons.nullanalysis.Nullable;
import de.unkrig.commons.util.logging.LogUtil;

/**
 * A {@link Formatter} that formats a {@link LogRecord} through a format string (as described for {@link
 * java.util.Formatter}) which is specified through the {@code de.unkrig.commons.util.logging.FormatFormatter.format}
 * logging property.
 * 

* Notice:
* Since Java 7, the {@link java.util.logging.SimpleFormatter} also has a '.format' property and thus implements the * key feature of this class. *

* This class is a COPY of {@code de.unkrig.ext.logging.formatter.PrintfFormatter}, and should be kept in sync * with that. */ public class PrintfFormatter extends Formatter { // Declare default values as PUBLIC constants so they can be used with the "{@value}" doc tag. // SUPPRESS CHECKSTYLE JavadocVariable:7 public static final String FORMAT_STRING_BENCHMARK = "%12$s %10$-20s %3$2d %8$s%n"; public static final String FORMAT_STRING_COMPACT = "%4$s %6$s::%7$s %8$s%9$s%n"; public static final String FORMAT_STRING_MESSAGE = "%8$s%n"; public static final String FORMAT_STRING_MESSAGE_AND_EXCEPTION = "%8$s%11$s%n"; public static final String FORMAT_STRING_MESSAGE_AND_STACK_TRACE = "%8$s%9$s%n"; public static final String FORMAT_STRING_SIMPLE = "%5$tF %5$tT.%5$tL %10$-20s %3$2d %8$s%9$s%n"; public static final String FORMAT_STRING_TIME_MESSAGE = "%5$tF %5$tT.%5$tL %8$s%n"; /** * Formats log records with the format string "{@value #FORMAT_STRING_BENCHMARK}", which is very suitable for * benchmarking. *

* Example: *

*
     *     3.000000;    2.000000;    1.000000 MyClass               0 Message
     * 
*/ public static final PrintfFormatter BENCHMARK = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_BENCHMARK); /** * Formats log records with the format string "{@value #FORMAT_STRING_COMPACT}", which produces a rather * terse logging format. *

* Examples: *

*
     * FINE pkg.MyClass::main Log message #1
     *
     * FINE pkg.MyClass::main Log message #2
     * java.io.IOException: Exception message
     *     at pkg.MyClass.main()
     * 
* * @see #format(LogRecord) */ public static final PrintfFormatter COMPACT = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_COMPACT); /** * Formats log records with the format string {@value #FORMAT_STRING_MESSAGE} (which expands to the log message). *

* Example: *

*
     * Log message
     * 
* * @see #format(LogRecord) */ public static final PrintfFormatter MESSAGE = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_MESSAGE); /** * Formats log records with the format string {@value #FORMAT_STRING_MESSAGE_AND_EXCEPTION} (which expands to the * log message plus the (optional) exception name and message). *

* Examples: *

*
     * Log message
     * Log message: java.io.FileNotFoundException: Exception message
     * 
* * @see #format(LogRecord) */ public static final PrintfFormatter MESSAGE_AND_EXCEPTION = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_MESSAGE_AND_EXCEPTION); /** * Formats log records with the format string {@value #FORMAT_STRING_MESSAGE_AND_STACK_TRACE} (which expands to the * log message, plus the (optional) exception name, message and stack trace). *

* Examples: *

*
     * Log message #1
     *
     * Log message #2
     * java.io.IOException: Exception message
     *     at pkg.MyClass.main()
     * 
* * @see #format(LogRecord) */ public static final PrintfFormatter MESSAGE_AND_STACK_TRACE = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_MESSAGE_AND_STACK_TRACE); /** * Formats log records with the format string {@value #FORMAT_STRING_SIMPLE} (which produces a simple one-line * format with date, time, simple class name, thread id, log message and the (optional) stack trace). *

* Example: *

*
     * 2012-03-08 10:10:28.515 MyClass               0 Log message #1
     *
     * 2012-03-08 10:10:28.516 MyClass               0 Log message #2
     * java.io.IOException: Exception message
     *     at pkg.MyClass.main()
     * 
* * @see #format(LogRecord) */ public static final PrintfFormatter SIMPLE = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_SIMPLE); /** * Formats log records with the format string {@value #FORMAT_STRING_TIME_MESSAGE} (which produces a simple * one-line format with date, time and log message). *

* Example: *

*
     * 2012-12-31 23:59:59.999 Message
     * 
*/ public static final PrintfFormatter TIME_MESSAGE = new PrintfFormatter(PrintfFormatter.FORMAT_STRING_TIME_MESSAGE); /** * The "default formatter", which is {@link #SIMPLE}. */ public static final PrintfFormatter DEFAULT = PrintfFormatter.SIMPLE; private static final String FORMAT_STRING_DEFAULT = PrintfFormatter.FORMAT_STRING_SIMPLE; private String format; private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); private static class State { long realTime, cpuTime, userTime; } /** * Contains the previously recorded real time, cpu time and user time for each thread. */ private static final ThreadLocal STATE_TL = new ThreadLocal(); private static final Object THE_BENCHMARK = new Object() { @Override public String toString() { State state = PrintfFormatter.STATE_TL.get(); if (state == null) { state = new State(); PrintfFormatter.STATE_TL.set(state); state.realTime = System.nanoTime(); state.cpuTime = PrintfFormatter.THREAD_MX_BEAN.getCurrentThreadCpuTime(); state.userTime = PrintfFormatter.THREAD_MX_BEAN.getCurrentThreadUserTime(); return " -.------; -.------; -.------"; } final long realTime = System.nanoTime(); final long cpuTime = PrintfFormatter.THREAD_MX_BEAN.getCurrentThreadCpuTime(); final long userTime = PrintfFormatter.THREAD_MX_BEAN.getCurrentThreadUserTime(); state.realTime = realTime; state.cpuTime = cpuTime; state.userTime = userTime; return ( this.ns2ms(realTime - state.realTime) + ';' + this.ns2ms(cpuTime - state.cpuTime) + ';' + this.ns2ms(userTime - state.userTime) ); } private String ns2ms(long ns) { StringBuilder sb = new StringBuilder(); String s1 = Integer.toString((int) (ns / 1000000L)); if (s1.length() < 5) sb.append("x ".substring(s1.length())); sb.append(s1); sb.append('.'); String s2 = Integer.toString((int) (ns % 1000000L)); if (s2.length() < 6) sb.append("x00000".substring(s2.length())); sb.append(s2); return sb.toString(); } }; /** * The format string is retrieved from the {@code "de.unkrig.commons.util.logging.formatter.PrintfFormatter.format"} * logging property. *

* Logging properties are typically defined in the file "{@code $JAVA_HOME/jre/lib/logging.properties}". *

* * @see LogManager * @see Logger */ public PrintfFormatter() { this(0, null); } /** * Constructor for derived classes which wish to impose a different ".format" logging property than that used * by {@link #PrintfFormatter()}. * * @param dummy Only there to distinguish this constructor from {@link #PrintfFormatter(String)} * @param propertyNamePrefix The property name prefix, or {@code null} to use the qualified name of the * actual formatter class * @see #format(LogRecord) */ protected PrintfFormatter(int dummy, @Nullable String propertyNamePrefix) { if (propertyNamePrefix == null) propertyNamePrefix = this.getClass().getName(); this.format = PrintfFormatter.cookFormat(LogUtil.getLoggingProperty(propertyNamePrefix + ".format")); } /** * A format string with placeholders as described for {@link #format(LogRecord)}. *

* The following special format strings are recognized: *

    *
  • {@link #SIMPLE SIMPLE} *
  • {@link #MESSAGE_AND_EXCEPTION MESSAGE_AND_EXCEPTION} *
  • {@link #MESSAGE MESSAGE} *
  • {@link #MESSAGE_AND_STACK_TRACE MESSAGE_AND_STACK_TRACE} *
  • {@link #COMPACT COMPACT} *
  • {@link #DEFAULT null} *
* @see #format(LogRecord) */ public PrintfFormatter(String format) { this.format = PrintfFormatter.cookFormat(format); } /** * Sets the format string for this logger. *

* The following special format string values designate certain "predefined" formats: *

*
    *
  • {@link #BENCHMARK "BENCHMARK"}
  • *
  • {@link #COMPACT "COMPACT"}
  • *
  • {@link #MESSAGE "MESSAGE"}
  • *
  • {@link #MESSAGE_AND_EXCEPTION "MESSAGE_AND_EXCEPTION"}
  • *
  • {@link #MESSAGE_AND_STACK_TRACE "MESSAGE_AND_STACK_TRACE"}
  • *
  • {@link #SIMPLE "SIMPLE"}
  • *
  • {@code null} (means {@link #DEFAULT})
  • *
  • {@link #DEFAULT "DEFAULT"}
  • *
* * @see java.util.Formatter The syntax of the format string * @see #format(LogRecord) The arguments' meanings */ public final void setFormat(String format) { this.format = PrintfFormatter.cookFormat(format); } private static String cookFormat(@Nullable String format) { return ( format == null ? PrintfFormatter.FORMAT_STRING_DEFAULT : "BENCHMARK".equals(format) ? PrintfFormatter.FORMAT_STRING_BENCHMARK : "COMPACT".equals(format) ? PrintfFormatter.FORMAT_STRING_COMPACT : "DEFAULT".equals(format) ? PrintfFormatter.FORMAT_STRING_DEFAULT : "MESSAGE".equals(format) ? PrintfFormatter.FORMAT_STRING_MESSAGE : "MESSAGE_AND_EXCEPTION".equals(format) ? PrintfFormatter.FORMAT_STRING_MESSAGE_AND_EXCEPTION : "MESSAGE_AND_STACK_TRACE".equals(format) ? PrintfFormatter.FORMAT_STRING_MESSAGE_AND_STACK_TRACE : "SIMPLE".equals(format) ? PrintfFormatter.FORMAT_STRING_SIMPLE : format ); } /** * @return The currently configured format string. */ public String getFormat() { return this.format; } /** * Formats a {@link LogRecord} with a PRINTF format string. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
PlaceholderDescriptionExample
%1$dSequence number 
%2$sLogger name 
%3$dThread ID{@code 1}
%4$sLevel{@link java.util.logging.Level#FINE FINE}
%5$tF %5$tT.%5$tLDate/time{@code 2012-03-08 10:10:28.468}
%6$sSource class name{@code pkg.MyClass}
%7$sSource method name{@code main}
%8$sMessage 
%9$s * The empty string iff the log record contains no throwable, otherwise: *
    *
  • A colon
  • *
  • A line break
  • *
  • The throwable type name
  • *
  • Another colon
  • *
  • A space
  • *
  • The throwable's message
  • *
  • Another line break
  • *
  • The throwable's stack trace (without the trailing line break)
  • *
* * preceding-text:
* pkg.MyException: Exception message
*     at pkg.Class.method(File.java:123)
*     at Main.main(Main.java:20) *
*
%10$sSimple source class name{@code MyClass}
%11$sColon, space, throwable converted to string (typically class name, colon, space, localized message) * * preceding-text: pkg.MyEception * *
%12$sReal, CPU and user time in milliseconds since this thread last logged to this handler
3.000000;    2.000000;    1.000000
%nLine separator 
%%Percent character ('%') 
*/ @Override public String format(@Nullable LogRecord record) { if (record == null) return "null"; Throwable thrown = record.getThrown(); String thrownText; { if (thrown == null) { thrownText = ""; } else { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); pw.println(":"); thrown.printStackTrace(pw); // :[ ]\n at...\n at... thrownText = sw.toString(); thrownText = StringUtil.lessTrailingLineSeparators(thrownText); } } String sourceClassName = record.getSourceClassName(); String simpleSourceClassName = ( sourceClassName == null ? null : sourceClassName.substring(sourceClassName.lastIndexOf('.') + 1) ); return String.format( this.format, record.getSequenceNumber(), // %1$d record.getLoggerName(), // %2$s record.getThreadID(), // %3$d record.getLevel(), // %4$s new Date(record.getMillis()), // %5$tT sourceClassName, // %6$s record.getSourceMethodName(), // %7$s this.formatMessage(record), // %8$s thrownText, // %9$s simpleSourceClassName, // %10$s thrown == null ? "" : ": " + thrown.toString(), // %11$s PrintfFormatter.THE_BENCHMARK // %12$s ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy