001package cpw.mods.fml.relauncher;
002
003import java.io.ByteArrayOutputStream;
004import java.io.File;
005import java.io.IOException;
006import java.io.PrintStream;
007import java.util.concurrent.Executors;
008import java.util.concurrent.LinkedBlockingQueue;
009import java.util.logging.ConsoleHandler;
010import java.util.logging.FileHandler;
011import java.util.logging.Handler;
012import java.util.logging.Level;
013import java.util.logging.LogManager;
014import java.util.logging.LogRecord;
015import java.util.logging.Logger;
016
017public 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}