Java tutorial
/* * 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; } }