Logging

In this post, I’m going to share my custom logging class. It can be seen as a scaled down version of log4j, which is a comprehensive logging application. While log4j is great, it is not easily added to ones program. I’ve used log4j before and found it very helpful in large applications, but when I’m writing something simple, all I need is a small, lightweight class to handle basic logging functions – hence the genesis of this logging class.

The Logger Class

The Logger class is quite simple. It outputs to the console and, if desired, to files. It contains four log levels (DEBUG, INFO, WARN, and ERROR) and an override feature. The system log level determines the messages are shown based on precedence as follows: DEBUG -> INFO -> WARN -> ERROR. The current system level and all levels to the right are output, when anything to the left is ignored. For example, if the system log level is set to WARN, then INFO and DEBUG messages will not be shown, while WARN and ERROR will. The override features forces all messages to be output regardless of precedence.

Logger.java
/*
 * Copyright (c) 2015 Ray Hylock
 * Department of Health Services and Information Management
 * College of Allied Health Sciences
 * East Carolina University
 * 
 * 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 edu.ecu.hsim.ray.logger;
 
import java.io.FileWriter;
import java.io.IOException;
import java.util.EnumMap;
import java.util.LinkedHashMap;
 
/**
 * A logging class similar to log4j. It allows for file output as well as
 * console. It also has an override feature in which we can force a write
 * regardless of the log level. Supports DEBUG, INFO, WARN, and ERROR.
 * Java 1.5+ compliant.
 * @author Ray Hylock
 */
public class Logger {
    /**************************************************************************
     *                        STATIC LOGGING VARIABLES                        *
     **************************************************************************/
    private static final EnumMap<PrintLevel, Integer> printLevels = 
            new EnumMap<PrintLevel, Integer>(PrintLevel.class);
    private static final LinkedHashMap<String, PrintLevel> nameToPrintLevels = 
            new LinkedHashMap<String, PrintLevel>();
    private static final LinkedHashMap<PrintLevel, String> printLevelToNames = 
            new LinkedHashMap<PrintLevel, String>();
    private static final int DEPTH = 3;
    public static enum Level {          // external levels
        DEBUG, INFO, ERROR, WARN
    }
    private static enum PrintLevel {    // internal levels
        DEBUG, INFO, ERROR, WARN, OVERRIDE
    }
    static {
        // setup print levels
        printLevels.put(PrintLevel.DEBUG, 0);
        printLevels.put(PrintLevel.INFO, 1);
        printLevels.put(PrintLevel.WARN, 2);
        printLevels.put(PrintLevel.ERROR, 3);
        printLevels.put(PrintLevel.OVERRIDE, 4);
        nameToPrintLevels.put("Debug", PrintLevel.DEBUG);
        nameToPrintLevels.put("Info", PrintLevel.INFO);
        nameToPrintLevels.put("Warn", PrintLevel.WARN);
        nameToPrintLevels.put("Error", PrintLevel.ERROR);
        nameToPrintLevels.put("Override", PrintLevel.OVERRIDE);
        printLevelToNames.put(PrintLevel.DEBUG, "Debug");
        printLevelToNames.put(PrintLevel.INFO, "Info");
        printLevelToNames.put(PrintLevel.WARN, "Warn");
        printLevelToNames.put(PrintLevel.ERROR, "Error");
        printLevelToNames.put(PrintLevel.OVERRIDE, "Override");
    }
     
    /**************************************************************************
     *                 DYNAMIC LOGGING VARIABLES AND METHODS                  *
     **************************************************************************/
    private Level LEVEL = Level.DEBUG;      // default print level
    private FileWriter LOG = null;
    private final String LOG_EXT = ".log";
    private FileWriter ERROR = null;
    private final String ERR_EXT = ".err";
    private boolean USE_SYSTEM_ERR = false; // default is false
     
    /**
     * Constructor for non-file output with default log level of DEBUG and
     * no use of {@link System#err} (i.e., everything prints to 
     * {@link System#out}).
     */
    public Logger(){}
     
    /**
     * Constructor for non-file output with default log level of DEBUG and
     * specified use of {@link System#err}. If {@code useSystemError = true},
     * then WARN and ERROR will print using {@link System#err}, otherwise
     * {@link System#out}. Since {@link System#out} and {@link System#err} use
     * different output streams, they are not synchronized.
     * @param useSystemError {@code true} to use {@link System#error},
     *                       {@code false} otherwise.
     */
    public Logger(boolean useSystemError){
        setUseSystemError(useSystemError);
    }
     
    /**
     * Constructor for non-file output with specified log level and no use of 
     * {@link System#err} (i.e., everything prints to {@link System#out}).
     * @param level the level
     */
    public Logger(Level level){
        setLevel(level);
    }
     
    /**
     * Constructor for non-file output with specified log level and use of
     * {@link System#err}. If {@code useSystemError = true}, then WARN and ERROR
     * will print using {@link System#err}, otherwise {@link System#out}. Since
     * {@link System#out} and {@link System#err} use different output streams,
     * they are not synchronized.
     * @param level           the level
     * @param useSystemError  {@code true} to use {@link System#error},
     *                        {@code false} otherwise.
     */
    public Logger(Level level, boolean useSystemError){
        setLevel(level);
        setUseSystemError(useSystemError);
    }
     
    /**
     * Constructor for file output. Will create a log and error file with the
     * same name, a default log level of DEBUG, and no use of {@link System#err}
     * (i.e., everything print to {@link System#out}).
     * @param file path and name of the file
     */
    public Logger(String file){
        createLogFile(file);
        createErrorFile(file);
    }
     
    /**
     * Constructor for file output with specified use of {@link System#err}.
     * Will create a log and error file with the same name and use a default log
     * level of DEBUG. If {@code useSystemError = true}, then WARN and ERROR
     * will print using {@link System#err}, otherwise {@link System#out}. Since
     * {@link System#out} and {@link System#err} use different output streams,
     * they are not synchronized.
     * @param file            path and name of the file
     * @param useSystemError  {@code true} to use {@link System#error},
     *                        {@code false} otherwise.
     */
    public Logger(String file, boolean useSystemError){
        createLogFile(file);
        createErrorFile(file);
        setUseSystemError(useSystemError);
    }
     
    /**
     * Constructor for file output with specified log level. Will create a log
     * and error file with the same name and no use of {@link System#err} (i.e.,
     * everything print to {@link System#out}).
     * @param file  path and name of the file
     * @param level the level
     */
    public Logger(String file, Level level){
        createLogFile(file);
        createErrorFile(file);
        setLevel(level);
    }
     
    /**
     * Constructor for file output with specified log level and use of
     * {@link System#err}. Will create a log and error file with the same name.
     * If {@code useSystemError = true}, then WARN and ERROR will print using
     * {@link System#err}, otherwise {@link System#out}. Since
     * {@link System#out} and {@link System#err} use different output streams,
     * they are not synchronized.
     * @param file            path and name of the file
     * @param level           the level
     * @param useSystemError  {@code true} to use {@link System#error},
     *                        {@code false} otherwise.
     */
    public Logger(String file, Level level, boolean useSystemError){
        createLogFile(file);
        createErrorFile(file);
        setLevel(level);
        setUseSystemError(useSystemError);
    }
     
    /**
     * Compares the passed level with the current set level. This will return
     * <code>true</code> if the passed level is more specific than the
     * current set level; <code>false</code> otherwise.
     * For example, if the passed level is INFO and the current level is DEBUG,
     * the the return value will be <code>true</code>.
     * @param level comparison level
     * @return      true is more specific, false otherwise
     */
    public boolean compareLevel(Level level){
        return printLevels.get(PrintLevel.valueOf(getLevel().toString())) <= 
               printLevels.get(PrintLevel.valueOf(level.toString()));
    }
     
    /**
     * Creates a file for non-error logs.
     * @param file path and name of the file
     */
    private void createLogFile(String file){
        try {
            LOG = new FileWriter(file + LOG_EXT);
        } catch (IOException ex) {
            terminalError("Cannot create the log file " + file + "\n" + ex);
        }
    }
     
    /**
     * Creates a file for error logs.
     * @param file path and name of the file
     */
    private void createErrorFile(String file){
        try {
            ERROR = new FileWriter(file + ERR_EXT);
        } catch (IOException ex) {
            terminalError("Cannot create the error file " + file + "\n" + ex);
        }
    }
     
    /**
     * Closes the log files.
     */
    public void closeLoggers() {
        try {
            if(LOG != null){
                LOG.flush();
                LOG.close();
            }
            if(ERROR != null){
                ERROR.flush();
                ERROR.close();
            }
        } catch (IOException ex) {
            terminalError("Error closing log files.\n" + ex);
        }
    }
     
    /***************************************************************************
     *                           GETTERS AND SETTERS                           *
     ***************************************************************************/
    /**
     * Get the current level.
     * @return  current level
     */
    public Level getLevel() {
        return LEVEL;
    }
     
    /**
     * Returns {@code true} if using {@link System#err}, {@code false} otherwise.
     * @return {@code true} if using {@link System#err}, {@code false} otherwise
     */
    public boolean isUsingSystemError(){
        return USE_SYSTEM_ERR;
    }
 
    /**
     * Set the print level.
     * @param level the new level
     */
    public void setLevel(Level level) {
        LEVEL = level;
    }
     
    /**
     * Sets the use of {@link System#err}. If {@code useSystemError = true},
     * then WARN and ERROR will print using {@link System#err}, otherwise
     * {@link System#out}. Since {@link System#out} and {@link System#err} use
     * different output streams, they are not synchronized.
     * @param useSystemError  {@code true} to use {@link System#error},
     *                        {@code false} otherwise.
     */
    public void setUseSystemError(boolean useSystemError){
        USE_SYSTEM_ERR = useSystemError;
    }
     
    /***************************************************************************
     *                              PRINT METHODS                              *
     ***************************************************************************/
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(String message){
        print(message, PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(boolean message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(byte message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(char message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(double message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(float message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(int message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(long message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     */
    public void debug(short message){
        print(message+"", PrintLevel.DEBUG, DEPTH);
    }
     
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(String message, int depth){
        print(message, PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(boolean message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(byte message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(char message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(double message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(float message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(int message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(long message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
    /**
     * Prints if level is <code>DEBUG</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void debug(short message, int depth){
        print(message+"", PrintLevel.DEBUG, depth);
    }
     
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(String message, boolean override){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(boolean message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(byte message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(char message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(double message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(float message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(int message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(long message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void debug(short message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), DEPTH);
    }
     
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(String message, boolean override, int depth){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(boolean message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(byte message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(char message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(double message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(float message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(int message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(long message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
    /**
     * Prints if level is <code>DEBUG</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void debug(short message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.DEBUG), depth);
    }
     
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(String message){
        print(message, PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(boolean message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(byte message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(char message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(double message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(float message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(int message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(long message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     */
    public void info(short message){
        print(message+"", PrintLevel.INFO, DEPTH);
    }
     
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(String message, int depth){
        print(message, PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(boolean message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(byte message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(char message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(double message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(float message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(int message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(long message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
    /**
     * Prints if level is <code>INFO</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void info(short message, int depth){
        print(message+"", PrintLevel.INFO, depth);
    }
     
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(String message, boolean override){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(boolean message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(byte message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(char message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(double message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(float message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(int message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(long message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void info(short message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), DEPTH);
    }
     
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(String message, boolean override, int depth){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(boolean message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(byte message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(char message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(double message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(float message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(int message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(long message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
    /**
     * Prints if level is <code>INFO</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void info(short message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.INFO), depth);
    }
     
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(String message){
        print(message, PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(boolean message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(byte message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(char message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(double message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(float message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(int message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(long message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     */
    public void error(short message){
        print(message+"", PrintLevel.ERROR, DEPTH);
    }
     
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(String message, int depth){
        print(message, PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(boolean message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(byte message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(char message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(double message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(float message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(int message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(long message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
    /**
     * Prints if level is <code>ERROR</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void error(short message, int depth){
        print(message+"", PrintLevel.ERROR, depth);
    }
     
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(String message, boolean override){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(boolean message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(byte message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(char message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(double message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(float message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(int message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(long message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void error(short message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), DEPTH);
    }
     
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(String message, boolean override, int depth){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(boolean message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(byte message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(char message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(double message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(float message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(int message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(long message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
    /**
     * Prints if level is <code>ERROR</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void error(short message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.ERROR), depth);
    }
     
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(String message){
        print(message, PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(boolean message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(byte message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(char message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(double message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(float message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(int message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(long message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     */
    public void warn(short message){
        print(message+"", PrintLevel.WARN, DEPTH);
    }
     
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(String message, int depth){
        print(message, PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(boolean message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(byte message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(char message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(double message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(float message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(int message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(long message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
    /**
     * Prints if level is <code>WARN</code>.
     * @param message message to print
     * @param depth     stack depth
     */
    public void warn(short message, int depth){
        print(message+"", PrintLevel.WARN, depth);
    }
     
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(String message, boolean override){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(boolean message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(byte message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(char message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(double message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(float message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(int message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(long message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     */
    public void warn(short message, boolean override){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), DEPTH);
    }
     
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(String message, boolean override, int depth){
        print(message, ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(boolean message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(byte message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(char message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(double message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(float message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(int message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(long message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
    /**
     * Prints if level is <code>WARN</code> or <code>override</code> is true.
     * @param message   message to print
     * @param override  if <code>true</code> override level and print
     * @param depth     stack depth
     */
    public void warn(short message, boolean override, int depth){
        print(message+"", ((override) ? PrintLevel.OVERRIDE : PrintLevel.WARN), depth);
    }
 
    /**
     * Prints the passed message to the log file if its level is {@literal >=} 
     * the current level. If the level is WARN or ERROR, then if prints to the 
     * error file.
     * @param message       the message
     * @param level         the level of the message
     * @param depth         stack trace depth
     */
    private void print(String message, PrintLevel level, int depth) {
        /**
         * Only prints the message if its level is greater than or
         * equal to that of the desired system level
         */
        if (printLevels.get(PrintLevel.valueOf(getLevel().toString())) <= printLevels.get(level)) {
            String prefix = "";
            switch (level) {
                case DEBUG:     prefix = "DEBUG"; break;
                case INFO:      prefix = "INFO "; break;
                case WARN:      prefix = "WARN "; break;
                case ERROR:     prefix = "ERROR"; break;
                case OVERRIDE:  prefix = "Override"; break;
            }
             
            // get calling method properties
            String classname = Thread.currentThread().getStackTrace()[depth].getClassName();
            //String methodname = Thread.currentThread().getStackTrace()[depth].getMethodName();
            int linenumber = Thread.currentThread().getStackTrace()[depth].getLineNumber();
            String msg = classname.substring(classname.lastIndexOf(".") + 1, classname.length()) + ":"
                         + linenumber + " " + prefix + " - " + message + "\n";
            if(level == PrintLevel.DEBUG || level == PrintLevel.INFO || level == PrintLevel.OVERRIDE){
                System.out.print(msg);
                if(LOG != null){
                    try {
                        // log the information
                        LOG.write(msg);
                        LOG.flush();
                    } catch (IOException ex) {
                        terminalError("Error writing to the log file.\n" + ex);
                    }
                }
            } else {
                if(isUsingSystemError()) System.err.print(msg);
                else System.out.print(msg);
                if(ERROR != null){
                    try {
                        // log the information
                        ERROR.write(msg);
                        ERROR.flush();
                    } catch (IOException ex) {
                        terminalError("Error writing to the error file.\n" + ex);
                    }
                }
            }
        }
    }
     
    /**
     * The message is called when we cannot create the appropriate log files 
     * themselves. It simply creates a local file, writes the error, and 
     * terminates.
     * @param message terminal error message
     */
    private void terminalError(String message) {
        System.out.println("There was a terminal error. Please check "
                + "terminalError.log in the root directory.");
        FileWriter fw = null;
        try {
            fw = new FileWriter("terminalError.log");
            fw.write(message);
            fw.flush();
            fw.close();
        } catch(IOException ex){
            System.err.println("Cannot even write to the terminal error.\n" + ex);
        }
        System.exit(1);
    } 
}
Test Class

This class provides examples for console and file output, setting the system log level, overriding logging precedence, and turning on System.err printing used by WARN and ERROR for supported consoles.

LoggerTest.java
import static Logger.Level.INFO;
public class LoggerTest {
    public static void main(String[] args){
        consoleOutputOnly();
        consoleAndFileOutput("LoggerTest");
    }
 
    /**
     * Outputs to the console only.
     */
    public static void consoleOutputOnly(){
        System.out.println("--------------------------------------------");
        System.out.println("Console only output");
        System.out.println("--------------------------------------------");
         
        // create new logger
        Logger log = new Logger();  // default level is DEBUG
         
        // print debug message
        System.out.println("Testing debug message at DEBUG level");
        log.debug("Test debug message");
         
        // print info message
        System.out.println("\nTesting info message at DEBUG level");
        log.info("Test info message");
         
        // set level to info
        System.out.println("\nSetting log level to INFO");
        log.setLevel(INFO);
         
        // print debug message - it will NOT print
        System.out.println("\nTesting debug message at INFO level - fails");
        log.debug("Test debug message");
         
        // force the message to print by overriding level
        System.out.println("\nTesting debug message at INFO level with override");
        log.debug("Test debug message", true);
         
        /*
         * Current stack trace depth is 3 - the class and line that
         * called the log method. This can be altered to print any depth
         * in the stack. Here we print the message and assign it to the
         * Logger output function - depth 2 or 1 up from here.
         */
        System.out.println("\nTesting info message with stack trace depth 2");
        log.info("Test info message", 2);
         
        /*
         * It is possible to print WARNING and ERROR messages using System.err
         * by calling setUseSystemError(true) (you could also do this via the
         * constructors). By default, this feature is disabled to synchronize
         * output. That is, System.err and System.out use different output
         * streams. Thus, it is not uncommon for them to print messages out of
         * call order. However, for output pains allowing color, it might be
         * desirable to print WARNING and ERROR messages in red.
         */
        System.out.println("\nTesting error and info message with System.err - "
                + "out of order");
        log.setUseSystemError(true);
        log.error("Test error message");
        log.info("Test info message");
    }
     
    /**
     * Outputs to the console and files. DEBUG and INFO will be added to
     * {@code {@literal <}filename{@literal >}.log} and WARN and ERROR to
     * {@code {@literal <}filename{@literal >}.err}.
     * @param filename file name
     */
    public static void consoleAndFileOutput(String filename){
        System.out.println("\n--------------------------------------------");
        System.out.println(String.format("Console and file (%s) output", filename));
        System.out.println("--------------------------------------------");
         
        // create new logger
        Logger log = new Logger(filename);
         
        // print debug message
        System.out.println("Testing debug message at DEBUG level");
        log.debug("Test debug message");
         
        // print info message
        System.out.println("\nTesting info message at DEBUG level");
        log.info("Test info message");
         
        // error message
        System.out.println("\nTesting error message at DEBUG level");
        log.error("Test error message");
    }
}
Output for LoggerTest.java

Notice how LoggerTest:74 ERROR (red) is printed before line 39. This highlights the asynchronous nature of the System.out and System.err streams.

--------------------------------------------
Console only output
--------------------------------------------
Testing debug message at DEBUG level
LoggerTest:35 DEBUG - Test debug message

Testing info message at DEBUG level
LoggerTest:74 ERROR - Test error message
LoggerTest:39 INFO  - Test info message

Setting log level to INFO

Testing debug message at INFO level - fails

Testing debug message at INFO level with override
LoggerTest:51 Override - Test debug message

Testing info message with stack trace depth 2
Logger:643 INFO  - Test info message

Testing error and info message with System.err - out of order
LoggerTest:75 INFO  - Test info message

--------------------------------------------
Console and file (LoggerTest) output
--------------------------------------------
Testing debug message at DEBUG level
LoggerTest:94 DEBUG - Test debug message

Testing info message at DEBUG level
LoggerTest:98 INFO  - Test info message

Testing error message at DEBUG level
LoggerTest:102 ERROR - Test error message