View Javadoc
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 }