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}