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