com.mattc.launcher.util.Console.java Source code

Java tutorial

Introduction

Here is the source code for com.mattc.launcher.util.Console.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Matthew Crocco
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */
package com.mattc.launcher.util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;

import org.apache.log4j.EnhancedPatternLayout;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.RollingFileAppender;
import org.joda.time.DateTime;

import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterators;
import com.google.common.io.Files;

public final class Console {

    private static final Logger logger;
    private static File logFile;

    /** Max Number of .log Files allowed to exist in logs directory */
    public static final int MAX_LOG_COUNT = 6;
    /** The Pattern Passed to EnhancedPatternLayout for Log Formatting */
    public static final String LOG_PATTERN = "%d{HH:mm:ss} - [%t][%-5p]: %m";
    /** The File Path Separator received from System Properties */
    public static final String SEP = System.getProperty("file.separator");

    static {
        final DateTime cur = new DateTime();
        final String logName = String.format(".%slauncher-logs%sServerLauncher-%s-%s-%s-%s#%s#%s#%s.log", SEP, SEP,
                cur.getMonthOfYear(), cur.getDayOfMonth(), cur.getYearOfCentury(), cur.getHourOfDay(),
                cur.getMinuteOfHour(), cur.getSecondOfMinute(), cur.getMillisOfSecond());
        final Layout layout = new EnhancedPatternLayout(LOG_PATTERN);
        logFile = new File(logName);

        Thread.currentThread().setName("CSLauncher");
        logger = Logger.getLogger("ServerLauncher");
        logger.setLevel(Level.ALL);

        try {
            initialize();
            logger.addAppender(new RollingFileAppender(layout, logName));
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Does the same as {@link #log(Object, Level)} but goes further by taking a
     * Throwable and <br />
     * passing it to
     * {@link Logger#log(org.apache.log4j.Priority, Object, Throwable)} to be
     * parsed. <br />
     * <br />
     * The Throwable is parsed independently by each Appender. <br />
     * In a normal use case this is ConsoleAppender and RollingFileAppender. <br />
     * 
     * @param msg
     * @param t
     * @param level
     */
    public static void log(Object msg, Throwable t, Level level) {
        String mesg = String.valueOf(msg);
        if (!mesg.endsWith(System.getProperty("line.separator"))) {
            mesg = msg + System.getProperty("line.separator");
        }
        logger.log(level, mesg, t);
    }

    /**
     * Takes an Object as a message and obtains its String Value <br />
     * via {@link String#valueOf(Object)} which is null-safe. <br />
     * <br />
     * The String Message and Level are passed to
     * {@link Logger#log(org.apache.log4j.Priority, Object)} <br />
     * to be printed to the Console and Log File (If created) <br />
     * 
     * @param msg
     * @param level
     */
    public static void log(Object msg, Level level) {
        String mesg = String.valueOf(msg);
        if (!mesg.endsWith(System.getProperty("line.separator"))) {
            mesg = msg + System.getProperty("line.separator");
        }
        logger.log(level, mesg);
    }

    /**
     * Indicates a certain point has been reached successfully and may <br />
     * print state information. <br />
     * <br />
     * This is the Level that should be generally used in normal cases. <br />
     * 
     * @param msg
     */
    public static void info(Object msg) {
        log(msg, Level.INFO);
    }

    /**
     * Indicates a Debug Message meant for the Programmer alone.
     * 
     * @param msg
     */
    public static void debug(Object msg) {
        log(msg, Level.DEBUG);
    }

    /**
     * Does the same as {@link #bigWarning(Object)} but prints "null" as the
     * message.
     */
    public static void bigWarning() {
        bigWarning(null);
    }

    /**
     * Prints a very noticeable warning that is bordered. <br />
     * <br />
     * Indicates the same thing as {@link #warn(Object)} but also prints<br />
     * a 6 line Stack Trace (will not include the call to this method). <br />
     * <br />
     * <code>
     * **************************************** <br/>
     * * Message Here<br/>
     * *  at trace(class:line)<br/>
     * *  at trace(class:line)<br/>
     * *  at trace(class:line)<br/>
     * *  at trace(class:line)<br/>
     * *  at trace(class:line)<br/>
     * *  at trace(class:Line)<br/>
     * **************************************** <br/>
     * </code>
     * 
     * @param msg
     */
    public static void bigWarning(Object msg) {
        final StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        final String border = "****************************************";

        log(border, Level.WARN);
        log("* " + String.valueOf(msg), Level.WARN);
        for (int i = 2; (i < 8) && (i < trace.length); i++) {
            log(String.format("*  at %s%s", trace[i].toString(), i < 7 ? "..." : ""), Level.WARN);
        }
        log(border, Level.WARN);
    }

    /**
     * Indicates that although the program can continue as expected, <br />
     * the program may act unexpectedly due to receiving a valid, but <br />
     * unexpected result or value.
     * 
     * @param msg
     */
    public static void warn(Object msg) {
        log(msg, Level.WARN);
    }

    /**
     * Indicates an Error that is recoverable but should be noted to the <br />
     * user or programmer since this is likely a programmer error. <br />
     * 
     * @param msg
     */
    public static void error(Object msg) {
        log(msg, Level.ERROR);
    }

    /**
     * Indicates a Fatal Error that has caused the program to terminate <br />
     * since the error is unrecoverable.
     * 
     * @param msg
     */
    public static void fatal(Object msg) {
        log(msg, Level.FATAL);
    }

    /**
     * Takes a Throwable and prints out the Stack Trace. This is <br />
     * basically equivalent to Throwable.printStackTrace().
     * 
     * @param e
     */
    public static void exception(Throwable e) {
        final Splitter splitter = Splitter.onPattern("\r?\n").trimResults().omitEmptyStrings();
        final Iterator<String> trace = splitter.split(Throwables.getStackTraceAsString(e)).iterator();

        error("===============EXCEPTION===============");
        error("");
        error(String.format("MESSAGE (T:%s): %s", e.getClass().getSimpleName(), e.getLocalizedMessage()));
        error(" " + trace.next());
        Iterators.advance(trace, 1);

        while (trace.hasNext()) {
            error(" \t" + trace.next());
        }

        logger.error("");
        logger.error("===============EXCEPTION===============");
    }

    /**
     * Ensures there are no more than MAX_LOG_COUNT files in the Logs directory. <br />
     * <br />
     * This deletes by age and attempts to delete the file immediately. If that <br />
     * fails, then File.deleteOnExit() is called and the program resumes. This <br />
     * is likely a temporary implementation since, at the moment, a user editing <br />
     * or opening a log file will likely cause the file.lastModified() value to <br />
     * change. This may or may not change in the future to account for said
     * problem.
     */
    private static void initialize() {

        // Grab All Files ending in ".log" from logs directory
        final File[] files = new File(".", "launcher-logs").listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".log");
            }
        });
        File[] copy = null;

        // Sort Files by Last Modified Time
        // Least Recent -> Most Recent (Descending)
        // Swap a and b to make order go from
        // Most Recent -> Least Recent (Ascending)
        Arrays.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                final long a = o1.lastModified();
                final long b = o2.lastModified();

                return (b - a) > 0 ? 1 : (b - a) < 0 ? -1 : 0;
            }
        });

        copy = Arrays.copyOf(files, files.length);

        // Delete Log Files until we have 10 or Less
        int size = files.length;
        for (int i = 0; (size > MAX_LOG_COUNT) && (i < files.length); i++) {
            if (!files[i].delete()) {
                files[i].deleteOnExit();
            }

            size--;
        }

        // Touch All Files to keep them up to date for future
        // Elimination
        for (final File f : copy) {
            if (!f.exists()) {
                continue;
            }
            try {
                Files.touch(f);
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static File getLogFile() {
        return logFile;
    }
}