1 // ****************************************************************************** 2 // 3 // Title: Force Field X. 4 // Description: Force Field X - Software for Molecular Biophysics. 5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2024. 6 // 7 // This file is part of Force Field X. 8 // 9 // Force Field X is free software; you can redistribute it and/or modify it 10 // under the terms of the GNU General Public License version 3 as published by 11 // the Free Software Foundation. 12 // 13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT 14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 // details. 17 // 18 // You should have received a copy of the GNU General Public License along with 19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple 20 // Place, Suite 330, Boston, MA 02111-1307 USA 21 // 22 // Linking this library statically or dynamically with other modules is making a 23 // combined work based on this library. Thus, the terms and conditions of the 24 // GNU General Public License cover the whole combination. 25 // 26 // As a special exception, the copyright holders of this library give you 27 // permission to link this library with independent modules to produce an 28 // executable, regardless of the license terms of these independent modules, and 29 // to copy and distribute the resulting executable under terms of your choice, 30 // provided that you also meet, for each linked independent module, the terms 31 // and conditions of the license of that module. An independent module is a 32 // module which is not derived from or based on this library. If you modify this 33 // library, you may extend this exception to your version of the library, but 34 // you are not obligated to do so. If you do not wish to do so, delete this 35 // exception statement from your version. 36 // 37 // ****************************************************************************** 38 package ffx.ui; 39 40 import edu.rit.pj.Comm; 41 import ffx.potential.Utilities; 42 import ffx.ui.MainPanel.ExitStatus; 43 import java.awt.GraphicsEnvironment; 44 import java.io.File; 45 import java.io.FileOutputStream; 46 import java.io.PrintStream; 47 import java.util.logging.ErrorManager; 48 import java.util.logging.Handler; 49 import java.util.logging.Level; 50 import java.util.logging.LogRecord; 51 52 /** 53 * The default ConsoleHanlder publishes logging to System.err. This class publishes to System.out, 54 * which is normally intercepted by the Force Field X Shell. 55 * 56 * <p>The formatter used reduces verbosity relative to the default SimpleFormatter. 57 * 58 * @author Michael J. Schnieders 59 * @since 1.0 60 */ 61 public class LogHandler extends Handler { 62 63 /** 64 * If true, FFX is running in a headless environment. 65 */ 66 private static final boolean headless = GraphicsEnvironment.isHeadless(); 67 68 /** 69 * If the MainPanel variable has been set, we can publish records to the ModelingShell. 70 */ 71 private MainPanel mainPanel = null; 72 73 /** 74 * If true, we have received a Record at the Level.SEVERE and FFX will exit. 75 */ 76 private boolean fatal = false; 77 78 /** 79 * Construct the Force Field X Log Handler. 80 * 81 * @since 1.0 82 */ 83 public LogHandler() { 84 setLevel(Level.ALL); 85 86 // Log all messages to the file specified by "ffx.log.file" if in Headless mode. 87 // For MPI jobs, a separate file is used for each process. 88 boolean prependRank = false; 89 if (headless) { 90 String log = System.getProperty("ffx.log.file", ""); 91 if (log != null && !log.equalsIgnoreCase("")) { 92 Comm comm = Comm.world(); 93 int np = comm.size(); 94 int rank = comm.rank(); 95 96 // Define the log file directory and filename. 97 String logFile; 98 if (np == 1) { 99 logFile = new File(log).getAbsolutePath(); 100 } else { 101 prependRank = true; 102 File dir = new File(Integer.toString(rank)); 103 if (!dir.exists()) { 104 dir.mkdir(); 105 } 106 logFile = dir.getAbsolutePath() + File.separator + log; 107 } 108 109 try { 110 PrintStream printStream = new PrintStream(new FileOutputStream(logFile, true)); 111 System.setOut(printStream); 112 System.setErr(printStream); 113 } catch (Exception e) { 114 System.err.println(Utilities.stackTraceToString(e)); 115 } 116 } 117 } 118 119 // If each process has a separate log file, then it's not necessary to prepend a rank. 120 if (prependRank) { 121 setFormatter(new LogFormatter(false)); 122 } else { 123 setFormatter(new LogFormatter()); 124 } 125 126 } 127 128 /** 129 * {@inheritDoc} 130 * 131 * <p>Flush, but do not close System.out or the Shell. 132 * 133 * @since 1.0 134 */ 135 @Override 136 public void close() { 137 flush(); 138 } 139 140 /** {@inheritDoc} */ 141 @Override 142 public void flush() { 143 System.out.flush(); 144 System.err.flush(); 145 if (mainPanel.getModelingShell() != null) { 146 // Scroll to visible! 147 } 148 } 149 150 /** 151 * {@inheritDoc} 152 * 153 * <p>Publish a LogRecord. 154 * 155 * @since 1.0. 156 */ 157 @Override 158 public synchronized void publish(LogRecord record) { 159 160 // Check if the record is loggable and that we have not already encountered a fatal error. 161 if (!isLoggable(record) || fatal) { 162 return; 163 } 164 165 String msg; 166 try { 167 msg = getFormatter().format(record); 168 } catch (Exception e) { 169 // Report the exception to any registered ErrorManager. 170 reportError(null, e, ErrorManager.FORMAT_FAILURE); 171 return; 172 } 173 174 try { 175 // FFX logs severe messages to System.err and then exits. 176 if (record.getLevel() == Level.SEVERE) { 177 // Set the fatal flag to true; this is the final record that will be logged. 178 fatal = true; 179 180 // Log the message to System.err. 181 System.err.println(msg); 182 Throwable throwable = record.getThrown(); 183 if (throwable != null) { 184 System.err.printf(" %s%n", throwable); 185 } 186 187 System.err.println(" Force Field X will not continue."); 188 System.err.println(" Shutting down..."); 189 flush(); 190 mainPanel.setExitType(ExitStatus.SEVERE); 191 mainPanel.exit(); 192 } 193 194 ModelingShell shell = null; 195 if (mainPanel != null) { 196 shell = mainPanel.getModelingShell(); 197 } 198 199 if (!headless && shell != null) { 200 shell.appendOutputNl(msg, shell.getResultStyle()); 201 } else { 202 System.out.println(msg); 203 } 204 } catch (Exception e) { 205 // Report the exception to any registered ErrorManager. 206 reportError(null, e, ErrorManager.WRITE_FAILURE); 207 } 208 } 209 210 /** 211 * A reference to the Force Field X MainPanel container to shut down if we encounter a fatal 212 * (SEVERE) exception. If we are not in Headless mode, then LogRecords can be published to the 213 * ModelingShell. 214 * 215 * @param mainPanel the Force Field X MainPanel. 216 * @since 1.0 217 */ 218 public void setMainPanel(MainPanel mainPanel) { 219 this.mainPanel = mainPanel; 220 } 221 }