/* * Forge Mod Loader * Copyright (c) 2012-2013 cpw. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * cpw - implementation */ package cpw.mods.fml.relauncher; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; public class FMLRelaunchLog { private static class ConsoleLogWrapper extends Handler { @Override public void publish(LogRecord record) { boolean currInt = Thread.interrupted(); try { ConsoleLogThread.recordQueue.put(record); } catch (InterruptedException e) { e.printStackTrace(errCache); } if (currInt) { Thread.currentThread().interrupt(); } } @Override public void flush() { } @Override public void close() throws SecurityException { } } private static class ConsoleLogThread implements Runnable { static ConsoleHandler wrappedHandler = new ConsoleHandler(); static LinkedBlockingQueue<LogRecord> recordQueue = new LinkedBlockingQueue<LogRecord>(); @Override public void run() { do { LogRecord lr; try { lr = recordQueue.take(); wrappedHandler.publish(lr); } catch (InterruptedException e) { e.printStackTrace(errCache); Thread.interrupted(); // Stupid } } while (true); } } private static class LoggingOutStream extends ByteArrayOutputStream { private Logger log; private StringBuilder currentMessage; public LoggingOutStream(Logger log) { this.log = log; this.currentMessage = new StringBuilder(); } @Override public void flush() throws IOException { String record; synchronized(FMLRelaunchLog.class) { super.flush(); record = this.toString(); super.reset(); currentMessage.append(record.replace(FMLLogFormatter.LINE_SEPARATOR, "\n")); // Are we longer than just the line separator? int lastIdx = -1; int idx = currentMessage.indexOf("\n",lastIdx+1); while (idx >= 0) { log.log(Level.INFO, currentMessage.substring(lastIdx+1,idx)); lastIdx = idx; idx = currentMessage.indexOf("\n",lastIdx+1); } if (lastIdx >= 0) { currentMessage.setLength(0); } } } } /** * Our special logger for logging issues to. We copy various assets from the * Minecraft logger to achieve a similar appearance. */ public static FMLRelaunchLog log = new FMLRelaunchLog(); static File minecraftHome; private static boolean configured; private static Thread consoleLogThread; private static PrintStream errCache; private Logger myLog; private static FileHandler fileHandler; private static FMLLogFormatter formatter; private FMLRelaunchLog() { } /** * Configure the FML logger */ private static void configureLogging() { LogManager.getLogManager().reset(); Logger globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); globalLogger.setLevel(Level.OFF); log.myLog = Logger.getLogger("ForgeModLoader"); Logger stdOut = Logger.getLogger("STDOUT"); stdOut.setParent(log.myLog); Logger stdErr = Logger.getLogger("STDERR"); stdErr.setParent(log.myLog); log.myLog.setLevel(Level.ALL); log.myLog.setUseParentHandlers(false); consoleLogThread = new Thread(new ConsoleLogThread()); consoleLogThread.setDaemon(true); consoleLogThread.start(); formatter = new FMLLogFormatter(); try { File logPath = new File(minecraftHome, FMLRelauncher.logFileNamePattern); fileHandler = new FileHandler(logPath.getPath(), 0, 3) { public synchronized void close() throws SecurityException { // We don't want this handler to reset } }; } catch (Exception e) { } resetLoggingHandlers(); // Set system out to a log stream errCache = System.err; System.setOut(new PrintStream(new LoggingOutStream(stdOut), true)); System.setErr(new PrintStream(new LoggingOutStream(stdErr), true)); configured = true; } private static void resetLoggingHandlers() { ConsoleLogThread.wrappedHandler.setLevel(Level.parse(System.getProperty("fml.log.level","INFO"))); // Console handler captures the normal stderr before it gets replaced log.myLog.addHandler(new ConsoleLogWrapper()); ConsoleLogThread.wrappedHandler.setFormatter(formatter); fileHandler.setLevel(Level.ALL); fileHandler.setFormatter(formatter); log.myLog.addHandler(fileHandler); } public static void loadLogConfiguration(File logConfigFile) { if (logConfigFile!=null && logConfigFile.exists() && logConfigFile.canRead()) { try { LogManager.getLogManager().readConfiguration(new FileInputStream(logConfigFile)); resetLoggingHandlers(); } catch (Exception e) { log(Level.SEVERE, e, "Error reading logging configuration file %s", logConfigFile.getName()); } } } public static void log(String logChannel, Level level, String format, Object... data) { makeLog(logChannel); Logger.getLogger(logChannel).log(level, String.format(format, data)); } public static void log(Level level, String format, Object... data) { if (!configured) { configureLogging(); } log.myLog.log(level, String.format(format, data)); } public static void log(String logChannel, Level level, Throwable ex, String format, Object... data) { makeLog(logChannel); Logger.getLogger(logChannel).log(level, String.format(format, data), ex); } public static void log(Level level, Throwable ex, String format, Object... data) { if (!configured) { configureLogging(); } log.myLog.log(level, String.format(format, data), ex); } public static void severe(String format, Object... data) { log(Level.SEVERE, format, data); } public static void warning(String format, Object... data) { log(Level.WARNING, format, data); } public static void info(String format, Object... data) { log(Level.INFO, format, data); } public static void fine(String format, Object... data) { log(Level.FINE, format, data); } public static void finer(String format, Object... data) { log(Level.FINER, format, data); } public static void finest(String format, Object... data) { log(Level.FINEST, format, data); } public Logger getLogger() { return myLog; } public static void makeLog(String logChannel) { Logger l = Logger.getLogger(logChannel); l.setParent(log.myLog); } }