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                try
026                {
027                    ConsoleLogThread.recordQueue.put(record);
028                }
029                catch (InterruptedException e)
030                {
031                    Thread.interrupted();
032                }
033            }
034    
035            @Override
036            public void flush()
037            {
038    
039            }
040    
041            @Override
042            public void close() throws SecurityException
043            {
044            }
045    
046        }
047        private static class ConsoleLogThread implements Runnable
048        {
049            static ConsoleHandler wrappedHandler = new ConsoleHandler();
050            static LinkedBlockingQueue<LogRecord> recordQueue = new LinkedBlockingQueue<LogRecord>();
051            @Override
052            public void run()
053            {
054                do
055                {
056                    LogRecord lr;
057                    try
058                    {
059                        lr = recordQueue.take();
060                        wrappedHandler.publish(lr);
061                    }
062                    catch (InterruptedException e)
063                    {
064                        Thread.interrupted();
065                        // Stupid
066                    }
067                }
068                while (true);
069            }
070        }
071        private static class LoggingOutStream extends ByteArrayOutputStream
072        {
073            private Logger log;
074            private StringBuilder currentMessage;
075    
076            public LoggingOutStream(Logger log)
077            {
078                this.log = log;
079                this.currentMessage = new StringBuilder();
080            }
081    
082            @Override
083            public void flush() throws IOException
084            {
085                String record;
086                synchronized(FMLRelaunchLog.class)
087                {
088                    super.flush();
089                    record = this.toString();
090                    super.reset();
091    
092                    currentMessage.append(record);
093                    if (currentMessage.lastIndexOf(FMLLogFormatter.LINE_SEPARATOR)>=0)
094                    {
095                        // Are we longer than just the line separator?
096                        if (currentMessage.length()>FMLLogFormatter.LINE_SEPARATOR.length())
097                        {
098                            // Trim the line separator
099                            currentMessage.setLength(currentMessage.length()-FMLLogFormatter.LINE_SEPARATOR.length());
100                            log.log(Level.INFO, currentMessage.toString());
101                        }
102                        currentMessage.setLength(0);
103                    }
104                }
105            }
106        }
107        /**
108         * Our special logger for logging issues to. We copy various assets from the
109         * Minecraft logger to acheive a similar appearance.
110         */
111        public static FMLRelaunchLog log = new FMLRelaunchLog();
112    
113        static File minecraftHome;
114        private static boolean configured;
115    
116        private static Thread consoleLogThread;
117        private Logger myLog;
118    
119        private FMLRelaunchLog()
120        {
121        }
122        /**
123         * Configure the FML logger
124         */
125        private static void configureLogging()
126        {
127            LogManager.getLogManager().reset();
128            Logger globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
129            globalLogger.setLevel(Level.OFF);
130    
131            log.myLog = Logger.getLogger("ForgeModLoader");
132    
133            Logger stdOut = Logger.getLogger("STDOUT");
134            stdOut.setParent(log.myLog);
135            Logger stdErr = Logger.getLogger("STDERR");
136            stdErr.setParent(log.myLog);
137            FMLLogFormatter formatter = new FMLLogFormatter();
138    
139            // Console handler captures the normal stderr before it gets replaced
140            log.myLog.setUseParentHandlers(false);
141            log.myLog.addHandler(new ConsoleLogWrapper());
142            consoleLogThread = new Thread(new ConsoleLogThread());
143            consoleLogThread.start();
144            ConsoleLogThread.wrappedHandler.setLevel(Level.parse(System.getProperty("fml.log.level","INFO")));
145            ConsoleLogThread.wrappedHandler.setFormatter(formatter);
146            log.myLog.setLevel(Level.ALL);
147            try
148            {
149                File logPath = new File(minecraftHome, FMLRelauncher.logFileNamePattern);
150                FileHandler fileHandler = new FileHandler(logPath.getPath(), 0, 3);
151                fileHandler.setFormatter(formatter);
152                fileHandler.setLevel(Level.ALL);
153                log.myLog.addHandler(fileHandler);
154            }
155            catch (Exception e)
156            {
157            }
158    
159            // Set system out to a log stream
160            System.setOut(new PrintStream(new LoggingOutStream(stdOut), true));
161            System.setErr(new PrintStream(new LoggingOutStream(stdErr), true));
162    
163            // Reset global logging to shut up other logging sources (thanks guava!)
164            configured = true;
165        }
166    
167        public static void log(Level level, String format, Object... data)
168        {
169            if (!configured)
170            {
171                configureLogging();
172            }
173            log.myLog.log(level, String.format(format, data));
174        }
175    
176        public static void log(Level level, Throwable ex, String format, Object... data)
177        {
178            if (!configured)
179            {
180                configureLogging();
181            }
182            log.myLog.log(level, String.format(format, data), ex);
183        }
184    
185        public static void severe(String format, Object... data)
186        {
187            log(Level.SEVERE, format, data);
188        }
189    
190        public static void warning(String format, Object... data)
191        {
192            log(Level.WARNING, format, data);
193        }
194    
195        public static void info(String format, Object... data)
196        {
197            log(Level.INFO, format, data);
198        }
199    
200        public static void fine(String format, Object... data)
201        {
202            log(Level.FINE, format, data);
203        }
204    
205        public static void finer(String format, Object... data)
206        {
207            log(Level.FINER, format, data);
208        }
209    
210        public static void finest(String format, Object... data)
211        {
212            log(Level.FINEST, format, data);
213        }
214        public Logger getLogger()
215        {
216            return myLog;
217        }
218    }