    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    }