    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}