/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed 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 io.janusproject.util;
import java.io.PrintStream;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import io.janusproject.JanusConfig;
/**
* Helper for creating a logger.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public final class LoggerCreator {
private static final String FORMAT_PROPERTY_KEY = "java.util.logging.SimpleFormatter.format"; //$NON-NLS-1$
/**
* The parameters for the format string are:
* <ul>
* <li><code>%1</code>: the date,</li>
* <li><code>%2</code>: the name of the calling function,</li>
* <li><code>%3</code>: the name of the logger,</li>
* <li><code>%4</code>: the logging level,</li>
* <li><code>%5</code>: the message, and</li>
* <li><code>%6</code>: the throwable.</li>
* </ul>
*/
private static final String JANUS_FORMAT = "[%4$s, %1$tl:%1$tM:%1$tS%1$tp, %3$s] %5$s%6$s%n"; //$NON-NLS-1$
private static Level levelFromProperties;
private LoggerCreator() {
//
}
/**
* Change the configuration of the root logger for using the Janus format for the messages.
*/
public static void useJanusMessageFormat() {
final String format = System.getProperty(FORMAT_PROPERTY_KEY, null);
if (format == null || format.isEmpty()) {
System.setProperty(FORMAT_PROPERTY_KEY, JANUS_FORMAT);
}
}
/**
* Create a logger with the given name.
*
* <p>The level of logging is influence by {@link JanusConfig#VERBOSE_LEVEL_NAME}.
*
* @param name
* - the name of the new logger.
* @return the logger.
*/
public static Logger createLogger(String name) {
final Logger logger = Logger.getLogger(name);
final Handler stderr = new StandardErrorOutputConsoleHandler();
final Handler stdout = new StandardOutputConsoleHandler();
for (final Handler handler : logger.getHandlers()) {
logger.removeHandler(handler);
}
logger.addHandler(stderr);
logger.addHandler(stdout);
logger.setUseParentHandlers(false);
final Level level = getLoggingLevelFromProperties();
logger.setLevel(level);
return logger;
}
/**
* Create a logger with the given name.
*
* <p>The level of logging is influence by {@link JanusConfig#VERBOSE_LEVEL_NAME}.
*
* @param name
* - the name of the new logger.
* @param parent
* - the parent logger.
* @return the logger.
*/
public static Logger createLogger(String name, Logger parent) {
final Logger logger = Logger.getLogger(name);
if (parent != null) {
logger.setParent(parent);
}
logger.setUseParentHandlers(true);
if (parent != null) {
logger.setLevel(parent.getLevel());
}
return logger;
}
/**
* Extract the logging level from the system properties.
*
* @return the logging level.
*/
public static Level getLoggingLevelFromProperties() {
if (levelFromProperties == null) {
final String verboseLevel = JanusConfig.getSystemProperty(JanusConfig.VERBOSE_LEVEL_NAME, JanusConfig.VERBOSE_LEVEL_VALUE);
levelFromProperties = parseLoggingLevel(verboseLevel);
}
return levelFromProperties;
}
/**
* Extract the logging level from the given string.
*
* @param level
* - the string representation of the logging level.
* @return the logging level.
*/
@SuppressWarnings({ "checkstyle:returncount", "checkstyle:cyclomaticcomplexity" })
public static Level parseLoggingLevel(String level) {
if (level == null) {
return Level.INFO;
}
switch (level.toLowerCase()) {
case "none": //$NON-NLS-1$
case "false": //$NON-NLS-1$
case "0": //$NON-NLS-1$
return Level.OFF;
case "severe": //$NON-NLS-1$
case "error": //$NON-NLS-1$
case "1": //$NON-NLS-1$
return Level.SEVERE;
case "warn": //$NON-NLS-1$
case "warning": //$NON-NLS-1$
case "2": //$NON-NLS-1$
return Level.WARNING;
case "info": //$NON-NLS-1$
case "true": //$NON-NLS-1$
case "3": //$NON-NLS-1$
return Level.INFO;
case "fine": //$NON-NLS-1$
case "config": //$NON-NLS-1$
case "4": //$NON-NLS-1$
return Level.FINE;
case "finer": //$NON-NLS-1$
case "5": //$NON-NLS-1$
return Level.FINER;
case "finest": //$NON-NLS-1$
case "debug": //$NON-NLS-1$
case "6": //$NON-NLS-1$
return Level.FINEST;
case "all": //$NON-NLS-1$
case "7": //$NON-NLS-1$
return Level.ALL;
default:
try {
return fromInt(Integer.parseInt(level));
} catch (Throwable exception) {
//
}
return Level.INFO;
}
}
/**
* Convert a numerical representation of logging level to the logging level.
*
* @param num
* - the numerical index that corresponds to the given level.
* @return the logging level.
*/
@SuppressWarnings({ "checkstyle:magicnumber", "checkstyle:returncount" })
public static Level fromInt(int num) {
switch (num) {
case 0:
return Level.OFF;
case 1:
return Level.SEVERE;
case 2:
return Level.WARNING;
case 3:
return Level.INFO;
case 4:
return Level.FINE;
case 5:
return Level.FINER;
case 6:
return Level.FINEST;
case 7:
return Level.ALL;
default:
if (num < 0) {
return Level.OFF;
}
return Level.ALL;
}
}
/**
* Convert a logging level to its numerical equivalent.
*
* @param level
* - the logging level.
* @return the numerical index that corresponds to the given level.
*/
@SuppressWarnings({ "checkstyle:magicnumber", "checkstyle:returncount", "checkstyle:npathcomplexity" })
public static int toInt(Level level) {
if (level == Level.OFF) {
return 0;
}
if (level == Level.SEVERE) {
return 1;
}
if (level == Level.WARNING) {
return 2;
}
if (level == Level.INFO) {
return 3;
}
if (level == Level.CONFIG) {
return 4;
}
if (level == Level.FINE) {
return 4;
}
if (level == Level.FINER) {
return 5;
}
if (level == Level.FINEST) {
return 6;
}
if (level == Level.ALL) {
return 7;
}
return 3;
}
/**
* Convert a string representing a logging level into its numerical representation.
*
* <p>This is a convinient function that calls {@link #parseLoggingLevel(String)} and {@link #toInt(Level)}.
*
* @param level
* - the string representation of the logging level.
* @return the numerical index that corresponds to the given level.
*/
public static int toInt(String level) {
return toInt(parseLoggingLevel(level));
}
/**
* Replies the string representations for the logging levels.
*
* @return the string representations, indexed by the numerical index of the level.
*/
public static String[] getLevelStrings() {
return new String[] {"none", //$NON-NLS-1$
"error", //$NON-NLS-1$
"warning", //$NON-NLS-1$
"info", //$NON-NLS-1$
"fine", //$NON-NLS-1$
"finer", //$NON-NLS-1$
"finest", //$NON-NLS-1$
"all", //$NON-NLS-1$
};
}
/** A console handler that supports to be link to the standard output or the standard error output.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.6
*/
public abstract static class AbstractStandardConsoleHandler extends StreamHandler {
/**
* Constructor.
*
* @param stream the original stream.
*/
public AbstractStandardConsoleHandler(PrintStream stream) {
super(stream, new SimpleFormatter());
}
@Override
public synchronized void publish(LogRecord record) {
super.publish(record);
flush();
}
@Override
public synchronized void close() {
flush();
}
/** Replies if the given log level is loggable.
*
* @param recordLevel the level to test.
* @return {@code true} if loggable level.
*/
protected abstract boolean isLoggable(int recordLevel);
@Override
public boolean isLoggable(LogRecord record) {
if (record != null) {
final Level level = record.getLevel();
assert level != null;
if (isLoggable(level.intValue())) {
return super.isLoggable(record);
}
}
return false;
}
}
/** A console handler that supports to be link to the standard output.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.6
*/
public static class StandardOutputConsoleHandler extends AbstractStandardConsoleHandler {
/** Constructor.
*/
public StandardOutputConsoleHandler() {
super(System.out);
}
@Override
public boolean isLoggable(int level) {
return level < Level.WARNING.intValue();
}
}
/** A console handler that supports to be link to the standard error output.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.6
*/
public static class StandardErrorOutputConsoleHandler extends AbstractStandardConsoleHandler {
/** Constructor.
*/
public StandardErrorOutputConsoleHandler() {
super(System.err);
}
@Override
public boolean isLoggable(int level) {
return level >= Level.WARNING.intValue();
}
}
}