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    }