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

de.unkrig.commons.util.logging.SimpleLogging 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;

import java.io.File;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.unkrig.commons.lang.ExceptionUtil;
import de.unkrig.commons.nullanalysis.Nullable;
import de.unkrig.commons.text.expression.EvaluationException;
import de.unkrig.commons.text.expression.ExpressionEvaluator;
import de.unkrig.commons.text.expression.Parser;
import de.unkrig.commons.text.parser.ParseException;
import de.unkrig.commons.util.logging.formatter.PrintfFormatter;
import de.unkrig.commons.util.logging.formatter.SelectiveFormatter;
import de.unkrig.commons.util.logging.handler.ProxyHandler;
import de.unkrig.commons.util.logging.handler.StderrHandler;
import de.unkrig.commons.util.logging.handler.StdoutHandler;

/**
 * A utility class that simplifies the usage of Java™'s {@code java.util.logging} facility.
 * 

* Typically, you simply call *

*
{@code SimpleLogging.init();}
*

* as soon as possible when your Java program starts (perhaps from a static initializer of your main class). *

*

* Later, at any possible point of time, you'd optionally call one of the following: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
{@link #setQuiet()}Suppress {@code LOGGER.info()} messages
{@link #setNoWarn()}Suppress {@code LOGGER.info()} and {@code LOGGER.warning()} messages
{@link #setNormal()}(Resets SimpleLogging to ist default behavior.)
{@link #setVerbose()}Also print {@code LOGGER.config()} messages to STDOUT
{@link #setDebug()}Also print {@code LOGGER.fine()} messages to STDERR*
{@link #setDebug()}
{@link #setDebug()}
Also print {@code LOGGER.finer()} messages to STDERR*
{@link #setDebug()}
{@link #setDebug()}
{@link #setDebug()}
Also print {@code LOGGER.finest()} messages to STDERR*
*

* * These messages are printed with source class name, source method name, exception (if any) and stack * trace. *

*

* Alternatively, you can call {@link #setLevel(Level)} with a respective level parameter. *

*

* Alternatively, you may want to call {@link #configureLoggers(String)} to configure log levels, handlers and * formats for individual loggers. For example, *

*
* {@code SimpleLogging.configureLoggers("FINEST:com.acme:FileHandler(\"foo.txt\"):java.util.logging.XMLFormatter");} *
*

* activates full debugging for logger "com.acme" and its children (e.g. "com.acme.foo"), and writes to the file * "foo.txt" in XML format. *

*/ public final class SimpleLogging { private SimpleLogging() {} /** * Logs the messages of all levels below {@link Level#CONFIG CONFIG} (FINE, FINER, FINEST, ...) to STDERR. */ private static final Handler DEBUG_HANDLER; /** * Logs the messages of levels {@link Level#CONFIG CONFIG} (inclusive) through {@link Level#WARNING WARNING} * (exclusive) to STDOUT or a custom {@link Handler} (see {@link #setOut(Handler)}. */ private static final ProxyHandler OUT_HANDLER; /** * Logs the messages of levels {@link Level#WARNING WARNING} and higher to STDERR. */ private static final Handler STDERR_HANDLER; // Default formatters for the handlers. private static final Formatter DEFAULT_DEBUG_FORMATTER = new PrintfFormatter("%10$27s::%7$-15s %8$s%9$s%n"); private static final Formatter DEFAULT_OUT_FORMATTER = SelectiveFormatter.loggerLevelGreaterThan( Level.FINE, PrintfFormatter.MESSAGE_AND_EXCEPTION, PrintfFormatter.MESSAGE_AND_STACK_TRACE ); private static final Formatter DEFAULT_STDERR_FORMATTER = SimpleLogging.DEFAULT_OUT_FORMATTER; static { DEBUG_HANDLER = new StderrHandler(); SimpleLogging.DEBUG_HANDLER.setFilter(LogUtil.LESS_THAN_CONFIG); SimpleLogging.DEBUG_HANDLER.setLevel(Level.ALL); SimpleLogging.DEBUG_HANDLER.setFormatter(SimpleLogging.DEFAULT_DEBUG_FORMATTER); LogUtil.ROOT_LOGGER.addHandler(SimpleLogging.DEBUG_HANDLER); OUT_HANDLER = new ProxyHandler(new StdoutHandler()); SimpleLogging.OUT_HANDLER.setFilter(LogUtil.LESS_THAN_WARNING); SimpleLogging.OUT_HANDLER.setLevel(Level.CONFIG); SimpleLogging.OUT_HANDLER.setFormatter(SimpleLogging.DEFAULT_OUT_FORMATTER); LogUtil.ROOT_LOGGER.addHandler(SimpleLogging.OUT_HANDLER); STDERR_HANDLER = new StderrHandler(); SimpleLogging.STDERR_HANDLER.setLevel(Level.WARNING); SimpleLogging.STDERR_HANDLER.setFormatter(SimpleLogging.DEFAULT_STDERR_FORMATTER); LogUtil.ROOT_LOGGER.addHandler(SimpleLogging.STDERR_HANDLER); } /** * Sets up the default configuration. *
    *
  • * Messages with levels >= {@link Level#WARNING WARNING} are printed to STDERR. *
  • *
  • * Messages with levels {@link Level#INFO INFO} are printed to STDOUT. *
  • *
  • * Messages with levels <= {@link Level#CONFIG CONFIG} are not printed. *
  • *
*/ public static void init() { // Everything is already done in static initializers. ; } /** * Configures the logging of a command-line utility as usual. * * * * * * * * * * * * * * * * * *
Typical command line optionlevelLevels logged to STDERRLevels logged to STDOUT
-nowarnSEVERESEVERE-
-quietWARNINGSEVERE, WARNING-
(none)INFOSEVERE, WARNINGINFO
-verboseCONFIGSEVERE, WARNINGINFO, CONFIG
-debugFINESTSEVERE, WARNING
FINE, FINER, FINEST*
INFO, CONFIG
* *: FINE, FINER and FINEST log records are printed with class, method, source and line number */ public static synchronized void setLevel(Level level) { LogUtil.ROOT_LOGGER.setLevel(level); } /** * @return The currently configured level for the root logger * @see #setLevel(Level) */ public static Level getLevel() { return LogUtil.ROOT_LOGGER.getLevel(); } /** * Sets the formatter for all three handlers (debug, out and stderr). * * @param spec The expression to parse and to evaluate * @see Parser The expression syntax */ public static void setFormatter(String spec) throws ParseException, EvaluationException { Formatter formatter = SimpleLogging.FORMATTER_INSTANTIATOR.evaluateTo(spec, Formatter.class); if (formatter == null) { SimpleLogging.DEBUG_HANDLER.setFormatter(SimpleLogging.DEFAULT_DEBUG_FORMATTER); SimpleLogging.OUT_HANDLER.setFormatter(SimpleLogging.DEFAULT_OUT_FORMATTER); SimpleLogging.STDERR_HANDLER.setFormatter(SimpleLogging.DEFAULT_STDERR_FORMATTER); } else { SimpleLogging.setFormatter(formatter); } } /** * Sets the formatter for all three handlers (debug, out and stderr). */ public static void setFormatter(Formatter formatter) { SimpleLogging.DEBUG_HANDLER.setFormatter(formatter); SimpleLogging.OUT_HANDLER.setFormatter(formatter); SimpleLogging.STDERR_HANDLER.setFormatter(formatter); } /** * Shorthand for {@code setLevel(Level.SEVERE + 1)}: Messages of levels {@code INFO}, {@code WARNING} and {@code * SEVERE} are suppressed. */ public static void setNoError() { SimpleLogging.setLevel(LogUtil.SEVERE_PLUS_1); } /** * Shorthand for {@code setLevel(Level.WARNING + 1)}: Messages of levels {@code INFO} and {@code WARNING} are * suppressed. */ public static void setNoWarn() { SimpleLogging.setLevel(LogUtil.WARNING_PLUS_1); } /** * Shorthand for {@code setLevel(Level.INFO + 1)}: Messages of level {@link Level#INFO}, i.e. 'normal output' are * suppressed. */ public static void setQuiet() { SimpleLogging.setLevel(LogUtil.INFO_PLUS_1); } /** * Shorthand for {@code setLevel(Level.INFO)}: Messages of level {@link Level#INFO INFO}, i.e. 'normal output' and * above ({@link Level#WARNING WARNING} and {@link Level#SEVERE SEVERE}) are logged. */ public static void setNormal() { SimpleLogging.setLevel(Level.INFO); } /** * Shorthand for {@link #setLevel(Level) setLevel}{@code (Level.CONFIG)}: Messages of level {@link Level#CONFIG * CONFIG}, i.e. "verbose output" are logged. */ public static void setVerbose() { SimpleLogging.setLevel(Level.CONFIG); } /** * Shorthand for {@code setLevel(FINE)}. All messages down to level {@link Level#FINE FINE} are logged. *

* Calling this method multiply lowers the level to {@link Level#FINER} and then {@link Level#FINEST}. */ public static void setDebug() { Level l = SimpleLogging.getLevel(); SimpleLogging.setLevel( l == Level.FINER ? Level.FINEST : l == Level.FINE ? Level.FINER : Level.FINE ); } /** * Installs a {@link Handler} which writes messages of levels {@link Level#INFO INFO} (inclusive) through {@link * Level#WARNING WARNING} (exclusive) to STDOUT. */ public static void setStdout() { SimpleLogging.setOut(new StdoutHandler()); } /** * Sets a {@link Handler} which writes messages of levels {@link Level#INFO INFO} (inclusive) through {@link * Level#WARNING WARNING} (exclusive) to the given {@link File}. */ public static void setOut(File value) throws IOException { SimpleLogging.setOut(new FileHandler(value.getPath())); } /** * Sets the given {@link Handler} for messages of levels {@link Level#INFO INFO} (inclusive) through {@link * Level#WARNING WARNING} (exclusive). *

* Clients may want to use this method to redirect their "normal" output (not the errors, warnings and debug * output) elsewhere, e.g. into an "output file". * * @param handler {@code null} to reset to the 'normal' behavior (print to STDOUT) */ public static synchronized void setOut(@Nullable Handler handler) { // Close the old delegate. SimpleLogging.OUT_HANDLER.close(); // Set the new delegate. SimpleLogging.OUT_HANDLER.setDelegate(handler == null ? new StdoutHandler() : handler); // Configure the new delegate. SimpleLogging.OUT_HANDLER.setFilter(LogUtil.LESS_THAN_WARNING); SimpleLogging.OUT_HANDLER.setLevel(Level.INFO); SimpleLogging.OUT_HANDLER.setFormatter(SimpleLogging.DEFAULT_OUT_FORMATTER); } static { for (Handler handler : LogUtil.ROOT_LOGGER.getHandlers()) { if (handler instanceof ConsoleHandler) { handler.close(); LogUtil.ROOT_LOGGER.removeHandler(handler); } } SimpleLogging.setNormal(); } private static final ExpressionEvaluator HANDLER_INSTANTIATOR = new ExpressionEvaluator().addOnDemandImports(new String[] { "java.util.logging", "de.unkrig.commons.util.logging.handler", }); private static final ExpressionEvaluator FORMATTER_INSTANTIATOR = new ExpressionEvaluator().addOnDemandImports(new String[] { "java.util.logging", "de.unkrig.commons.util.logging.formatter", }); // SUPPRESS CHECKSTYLE LineLength:8 /** * Sets the level of the named loggers, adds the given handler on them and sets the given * formatter on the handler. *

* The spec is parsed as follows: *

*
     * spec := [ level ] [ ':' [ logger-names ] [ ':' [ handler ] [ ':' [ formatter ] [ ':' use-parent-handlers ] ] ] ]
     * logger-names := logger-name [ ',' logger-name ]...
     * 
*

* The level component determines the log level of the handler, or, iff no * handler is given, the level of the loggers. * It must be parsable by {@link Level#parse(String)}, i.e. it must be a decimal number, or one of {@code * SEVERE}, {@code WARNING}, {@code INFO}, {@code CONFIG}, {@code FINE}, {@code FINER}, {@code FINEST} or {@code * ALL}. *

*

* The handler and formatter components denote {@link Parser expressions}, with the * automatically imported packages "{@code java.util.logging}", "{@code de.unkrig.commons.util.logging.handler}" * and "{@code de.unkrig.commons.util.logging.formatter}". Notice that neither expression may contain colons. *

*

* Example spec: *

*
     * FINE:de.unkrig:ConsoleHandler:FormatFormatter("%5$tF %5$tT.%5$tL %10$-20s %3$2d %8$s%9$s%n")
     * 
*

* If any of the components of the spec is missing or empty, a reasonable default value is assumed: *

*
*
level
*
* Logger: Level inherited from parent logger *
* Handler: Handler's default log level, typically {@code ALL} *
* *
loggers
*
* The root logger *
* *
handler
*
* No handler; only the parent loggers' handlers will be called (iff {@code user-parent-handlers} is {@code * true}) *
* *
formatter
*
* {@link PrintfFormatter#MESSAGE_AND_EXCEPTION MESSAGE_AND_EXCEPTION} *
* *
use-parent-handlers
*
* {@code true} *
*
*

* It is recommended that command line tools call this method from their {@code main(String[])} method, on each * occurrence of a {@code "-log} spec{@code "} command line option. *

* * @see Parser The expression syntax of the handler and the formatter components */ public static void configureLoggers(String spec) { String[] args = new String[5]; { String[] sa = spec.split(":", 5); for (int i = 0; i < args.length && i < sa.length; i++) { String s = sa[i].trim(); if (!"".equals(s)) args[i] = s; } } // Argument 1 == level Level level; if (args[0] != null) { level = Level.parse(args[0]); } else { level = null; } // Argument 2 == loggers Logger[] loggers; if (args[1] != null) { String loggerNames = args[1]; String[] tmp = loggerNames.split(","); loggers = new Logger[tmp.length]; for (int i = 0; i < tmp.length; i++) { loggers[i] = Logger.getLogger(tmp[i]); } } else { loggers = new Logger[] { LogUtil.ROOT_LOGGER }; } // Argument 3 == handler Handler handler; if (args[2] != null) { String handlerSpec = args[2]; try { handler = SimpleLogging.HANDLER_INSTANTIATOR.evaluateTo(handlerSpec, Handler.class); } catch (Exception e) { throw ExceptionUtil.wrap(handlerSpec + ": " + e.getMessage(), e, RuntimeException.class); } } else { handler = null; } // Argument 4 == formatter Formatter formatter; if (args[3] != null) { String formatterSpec = args[3]; try { formatter = SimpleLogging.FORMATTER_INSTANTIATOR.evaluateTo(formatterSpec, Formatter.class); } catch (Exception e) { throw ExceptionUtil.wrap( "Evaluating formatter spec \"" + formatterSpec + "\": " + e.getMessage(), e, RuntimeException.class ); } } else { formatter = PrintfFormatter.MESSAGE_AND_EXCEPTION; } // Argument 5 == useParentHandlers boolean useParentHandlers = true; if (args[4] != null) { useParentHandlers = Boolean.parseBoolean(args[4]); } // Configure the handler. if (handler != null) { if (level != null) handler.setLevel(level); handler.setFormatter(formatter); } // Configure the loggers. for (Logger logger : loggers) { if (handler != null) logger.addHandler(handler); if (level != null && !logger.isLoggable(level)) logger.setLevel(level); logger.setUseParentHandlers(useParentHandlers); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy