001 package cpw.mods.fml.relauncher; 002 003 import java.io.ByteArrayOutputStream; 004 import java.io.File; 005 import java.io.IOException; 006 import java.io.PrintStream; 007 import java.util.concurrent.Executors; 008 import java.util.concurrent.LinkedBlockingQueue; 009 import java.util.logging.ConsoleHandler; 010 import java.util.logging.FileHandler; 011 import java.util.logging.Handler; 012 import java.util.logging.Level; 013 import java.util.logging.LogManager; 014 import java.util.logging.LogRecord; 015 import java.util.logging.Logger; 016 017 public class FMLRelaunchLog 018 { 019 020 private static class ConsoleLogWrapper extends Handler 021 { 022 @Override 023 public void publish(LogRecord record) 024 { 025 boolean currInt = Thread.interrupted(); 026 try 027 { 028 ConsoleLogThread.recordQueue.put(record); 029 } 030 catch (InterruptedException e) 031 { 032 e.printStackTrace(errCache); 033 } 034 if (currInt) 035 { 036 Thread.currentThread().interrupt(); 037 } 038 } 039 040 @Override 041 public void flush() 042 { 043 044 } 045 046 @Override 047 public void close() throws SecurityException 048 { 049 } 050 051 } 052 private static class ConsoleLogThread implements Runnable 053 { 054 static ConsoleHandler wrappedHandler = new ConsoleHandler(); 055 static LinkedBlockingQueue<LogRecord> recordQueue = new LinkedBlockingQueue<LogRecord>(); 056 @Override 057 public void run() 058 { 059 do 060 { 061 LogRecord lr; 062 try 063 { 064 lr = recordQueue.take(); 065 wrappedHandler.publish(lr); 066 } 067 catch (InterruptedException e) 068 { 069 e.printStackTrace(errCache); 070 Thread.interrupted(); 071 // Stupid 072 } 073 } 074 while (true); 075 } 076 } 077 private static class LoggingOutStream extends ByteArrayOutputStream 078 { 079 private Logger log; 080 private StringBuilder currentMessage; 081 082 public LoggingOutStream(Logger log) 083 { 084 this.log = log; 085 this.currentMessage = new StringBuilder(); 086 } 087 088 @Override 089 public void flush() throws IOException 090 { 091 String record; 092 synchronized(FMLRelaunchLog.class) 093 { 094 super.flush(); 095 record = this.toString(); 096 super.reset(); 097 098 currentMessage.append(record.replace(FMLLogFormatter.LINE_SEPARATOR, "\n")); 099 if (currentMessage.lastIndexOf("\n")>=0) 100 { 101 // Are we longer than just the line separator? 102 if (currentMessage.length()>1) 103 { 104 // Trim the line separator 105 currentMessage.setLength(currentMessage.length()-1); 106 log.log(Level.INFO, currentMessage.toString()); 107 } 108 currentMessage.setLength(0); 109 } 110 } 111 } 112 } 113 /** 114 * Our special logger for logging issues to. We copy various assets from the 115 * Minecraft logger to acheive a similar appearance. 116 */ 117 public static FMLRelaunchLog log = new FMLRelaunchLog(); 118 119 static File minecraftHome; 120 private static boolean configured; 121 122 private static Thread consoleLogThread; 123 124 private static PrintStream errCache; 125 private Logger myLog; 126 127 private FMLRelaunchLog() 128 { 129 } 130 /** 131 * Configure the FML logger 132 */ 133 private static void configureLogging() 134 { 135 LogManager.getLogManager().reset(); 136 Logger globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); 137 globalLogger.setLevel(Level.OFF); 138 139 log.myLog = Logger.getLogger("ForgeModLoader"); 140 141 Logger stdOut = Logger.getLogger("STDOUT"); 142 stdOut.setParent(log.myLog); 143 Logger stdErr = Logger.getLogger("STDERR"); 144 stdErr.setParent(log.myLog); 145 FMLLogFormatter formatter = new FMLLogFormatter(); 146 147 // Console handler captures the normal stderr before it gets replaced 148 log.myLog.setUseParentHandlers(false); 149 log.myLog.addHandler(new ConsoleLogWrapper()); 150 consoleLogThread = new Thread(new ConsoleLogThread()); 151 consoleLogThread.start(); 152 ConsoleLogThread.wrappedHandler.setLevel(Level.parse(System.getProperty("fml.log.level","INFO"))); 153 ConsoleLogThread.wrappedHandler.setFormatter(formatter); 154 log.myLog.setLevel(Level.ALL); 155 try 156 { 157 File logPath = new File(minecraftHome, FMLRelauncher.logFileNamePattern); 158 FileHandler fileHandler = new FileHandler(logPath.getPath(), 0, 3); 159 fileHandler.setFormatter(formatter); 160 fileHandler.setLevel(Level.ALL); 161 log.myLog.addHandler(fileHandler); 162 } 163 catch (Exception e) 164 { 165 } 166 167 // Set system out to a log stream 168 errCache = System.err; 169 170 System.setOut(new PrintStream(new LoggingOutStream(stdOut), true)); 171 System.setErr(new PrintStream(new LoggingOutStream(stdErr), true)); 172 173 // Reset global logging to shut up other logging sources (thanks guava!) 174 configured = true; 175 } 176 177 public static void log(Level level, String format, Object... data) 178 { 179 if (!configured) 180 { 181 configureLogging(); 182 } 183 log.myLog.log(level, String.format(format, data)); 184 } 185 186 public static void log(Level level, Throwable ex, String format, Object... data) 187 { 188 if (!configured) 189 { 190 configureLogging(); 191 } 192 log.myLog.log(level, String.format(format, data), ex); 193 } 194 195 public static void severe(String format, Object... data) 196 { 197 log(Level.SEVERE, format, data); 198 } 199 200 public static void warning(String format, Object... data) 201 { 202 log(Level.WARNING, format, data); 203 } 204 205 public static void info(String format, Object... data) 206 { 207 log(Level.INFO, format, data); 208 } 209 210 public static void fine(String format, Object... data) 211 { 212 log(Level.FINE, format, data); 213 } 214 215 public static void finer(String format, Object... data) 216 { 217 log(Level.FINER, format, data); 218 } 219 220 public static void finest(String format, Object... data) 221 { 222 log(Level.FINEST, format, data); 223 } 224 public Logger getLogger() 225 { 226 return myLog; 227 } 228 }