KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > adaptor > EclipseLog


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.runtime.adaptor;
12
13 import java.io.*;
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.security.AccessController JavaDoc;
16 import java.util.Calendar JavaDoc;
17 import java.util.Date JavaDoc;
18 import org.eclipse.core.runtime.internal.adaptor.EclipseEnvironmentInfo;
19 import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
20 import org.eclipse.osgi.framework.log.FrameworkLog;
21 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
22 import org.eclipse.osgi.framework.util.SecureAction;
23 import org.osgi.framework.*;
24
25 /**
26  * The FrameworkLog implementation for Eclipse.
27  * <p>
28  * Clients may extend this class.
29  * </p>
30  * @since 3.1
31  */

32 public class EclipseLog implements FrameworkLog {
33     private static final String JavaDoc PASSWORD = "-password"; //$NON-NLS-1$
34
/** The session tag */
35     protected static final String JavaDoc SESSION = "!SESSION"; //$NON-NLS-1$
36
/** The entry tag */
37     protected static final String JavaDoc ENTRY = "!ENTRY"; //$NON-NLS-1$
38
/** The sub-entry tag */
39     protected static final String JavaDoc SUBENTRY = "!SUBENTRY"; //$NON-NLS-1$
40
/** The message tag */
41     protected static final String JavaDoc MESSAGE = "!MESSAGE"; //$NON-NLS-1$
42
/** The stacktrace tag */
43     protected static final String JavaDoc STACK = "!STACK"; //$NON-NLS-1$
44

45     /** The line separator used in the log output */
46     protected static final String JavaDoc LINE_SEPARATOR;
47     /** The tab character used in the log output */
48     protected static final String JavaDoc TAB_STRING = "\t"; //$NON-NLS-1$
49

50     //Constants for rotating log file
51
/** The default size a log file can grow before it is rotated */
52     public static final int DEFAULT_LOG_SIZE = 1000;
53     /** The default number of backup log files */
54     public static final int DEFAULT_LOG_FILES = 10;
55     /** The minimum size limit for log rotation */
56     public static final int LOG_SIZE_MIN = 10;
57
58     /** The system property used to specify size a log file can grow before it is rotated */
59     public static final String JavaDoc PROP_LOG_SIZE_MAX = "eclipse.log.size.max"; //$NON-NLS-1$
60
/** The system property used to specify the maximim number of backup log files to use */
61     public static final String JavaDoc PROP_LOG_FILE_MAX = "eclipse.log.backup.max"; //$NON-NLS-1$
62
/** The extension used for log files */
63     public static final String JavaDoc LOG_EXT = ".log"; //$NON-NLS-1$
64
/** The extension markup to use for backup log files*/
65     public static final String JavaDoc BACKUP_MARK = ".bak_"; //$NON-NLS-1$
66

67     static {
68         String JavaDoc s = System.getProperty("line.separator"); //$NON-NLS-1$
69
LINE_SEPARATOR = s == null ? "\n" : s; //$NON-NLS-1$
70
}
71     private static final SecureAction secureAction = (SecureAction) AccessController.doPrivileged(SecureAction.createSecureAction());;
72
73     /** Indicates if the console messages should be printed to the console (System.out) */
74     protected boolean consoleLog = false;
75     /** Indicates if the next log message is part of a new session */
76     protected boolean newSession = true;
77     /**
78      * The File object to store messages. This value may be null.
79      */

80     protected File outFile;
81
82     /**
83      * The Writer to log messages to.
84      */

85     protected Writer writer;
86
87     int maxLogSize = DEFAULT_LOG_SIZE; // The value is in KB.
88
int maxLogFiles = DEFAULT_LOG_FILES;
89     int backupIdx = 0;
90
91     /**
92      * Constructs an EclipseLog which uses the specified File to log messages to
93      * @param outFile a file to log messages to
94      */

95     public EclipseLog(File outFile) {
96         this.outFile = outFile;
97         this.writer = null;
98         readLogProperties();
99     }
100
101     /**
102      * Constructs an EclipseLog which uses the specified Writer to log messages to
103      * @param writer a writer to log messages to
104      */

105     public EclipseLog(Writer writer) {
106         if (writer == null)
107             // log to System.err by default
108
this.writer = logForStream(System.err);
109         else
110             this.writer = writer;
111     }
112
113     /**
114      * Constructs an EclipseLog which uses System.err to write log messages to
115      *
116      */

117     public EclipseLog() {
118         this((Writer) null);
119     }
120
121     private Throwable JavaDoc getRoot(Throwable JavaDoc t) {
122         Throwable JavaDoc root = null;
123         if (t instanceof BundleException)
124             root = ((BundleException) t).getNestedException();
125         if (t instanceof InvocationTargetException JavaDoc)
126             root = ((InvocationTargetException JavaDoc) t).getTargetException();
127         // skip inner InvocationTargetExceptions and BundleExceptions
128
if (root instanceof InvocationTargetException JavaDoc || root instanceof BundleException) {
129             Throwable JavaDoc deeplyNested = getRoot(root);
130             if (deeplyNested != null)
131                 // if we have something more specific, use it, otherwise keep what we have
132
root = deeplyNested;
133         }
134         return root;
135     }
136
137     /**
138      * Helper method for writing out argument arrays.
139      * @param header the header
140      * @param args the list of arguments
141      */

142     protected void writeArgs(String JavaDoc header, String JavaDoc[] args) throws IOException {
143         if (args == null || args.length == 0)
144             return;
145         write(header);
146         for (int i = 0; i < args.length; i++) {
147             //mask out the password argument for security
148
if (i > 0 && PASSWORD.equals(args[i - 1]))
149                 write(" (omitted)"); //$NON-NLS-1$
150
else
151                 write(" " + args[i]); //$NON-NLS-1$
152
}
153         writeln();
154     }
155
156     /**
157      * Returns the session timestamp. This is the time the platform was started
158      * @return the session timestamp
159      */

160     protected String JavaDoc getSessionTimestamp() {
161         // Main should have set the session start-up timestamp so return that.
162
// Return the "now" time if not available.
163
String JavaDoc ts = FrameworkProperties.getProperty("eclipse.startTime"); //$NON-NLS-1$
164
if (ts != null) {
165             try {
166                 return getDate(new Date JavaDoc(Long.parseLong(ts)));
167             } catch (NumberFormatException JavaDoc e) {
168                 // fall through and use the timestamp from right now
169
}
170         }
171         return getDate(new Date JavaDoc());
172     }
173
174     /**
175      * Writes the session
176      * @throws IOException if an error occurs writing to the log
177      */

178     protected void writeSession() throws IOException {
179         write(SESSION);
180         writeSpace();
181         String JavaDoc date = getSessionTimestamp();
182         write(date);
183         writeSpace();
184         for (int i = SESSION.length() + date.length(); i < 78; i++) {
185             write("-"); //$NON-NLS-1$
186
}
187         writeln();
188         // Write out certain values found in System.getProperties()
189
try {
190             String JavaDoc key = "eclipse.buildId"; //$NON-NLS-1$
191
String JavaDoc value = FrameworkProperties.getProperty(key, "unknown"); //$NON-NLS-1$
192
writeln(key + "=" + value); //$NON-NLS-1$
193

194             key = "java.fullversion"; //$NON-NLS-1$
195
value = System.getProperty(key);
196             if (value == null) {
197                 key = "java.version"; //$NON-NLS-1$
198
value = System.getProperty(key);
199                 writeln(key + "=" + value); //$NON-NLS-1$
200
key = "java.vendor"; //$NON-NLS-1$
201
value = System.getProperty(key);
202                 writeln(key + "=" + value); //$NON-NLS-1$
203
} else {
204                 writeln(key + "=" + value); //$NON-NLS-1$
205
}
206         } catch (Exception JavaDoc e) {
207             // If we're not allowed to get the values of these properties
208
// then just skip over them.
209
}
210         // The Bootloader has some information that we might be interested in.
211
write("BootLoader constants: OS=" + EclipseEnvironmentInfo.getDefault().getOS()); //$NON-NLS-1$
212
write(", ARCH=" + EclipseEnvironmentInfo.getDefault().getOSArch()); //$NON-NLS-1$
213
write(", WS=" + EclipseEnvironmentInfo.getDefault().getWS()); //$NON-NLS-1$
214
writeln(", NL=" + EclipseEnvironmentInfo.getDefault().getNL()); //$NON-NLS-1$
215
// Add the command-line arguments used to invoke the platform
216
// XXX: this includes runtime-private arguments - should we do that?
217
writeArgs("Framework arguments: ", EclipseEnvironmentInfo.getDefault().getNonFrameworkArgs()); //$NON-NLS-1$
218
writeArgs("Command-line arguments: ", EclipseEnvironmentInfo.getDefault().getCommandLineArgs()); //$NON-NLS-1$
219
}
220
221     public void close() {
222         try {
223             if (writer != null) {
224                 Writer tmpWriter = writer;
225                 writer = null;
226                 tmpWriter.close();
227             }
228         } catch (IOException e) {
229             e.printStackTrace();
230         }
231     }
232
233     /**
234      * If a File is used to log messages to then the File opened and a Writer is created
235      * to log messages to.
236      */

237     protected void openFile() {
238         if (writer == null) {
239             if (outFile != null) {
240                 try {
241                     writer = logForStream(secureAction.getFileOutputStream(outFile, true));
242                 } catch (IOException e) {
243                     writer = logForStream(System.err);
244                 }
245             } else {
246                 writer = logForStream(System.err);
247             }
248         }
249     }
250
251     /**
252      * If a File is used to log messages to then the writer is closed.
253      */

254     protected void closeFile() {
255         if (outFile != null) {
256             if (writer != null) {
257                 try {
258                     writer.close();
259                 } catch (IOException e) {
260                     // we cannot log here; just print the stacktrace.
261
e.printStackTrace();
262                 }
263                 writer = null;
264             }
265         }
266     }
267
268     public void log(FrameworkEvent frameworkEvent) {
269         Bundle b = frameworkEvent.getBundle();
270         Throwable JavaDoc t = frameworkEvent.getThrowable();
271         String JavaDoc entry = b.getSymbolicName() == null ? b.getLocation() : b.getSymbolicName();
272         int severity;
273         switch (frameworkEvent.getType()) {
274             case FrameworkEvent.INFO :
275                 severity = FrameworkLogEntry.INFO;
276                 break;
277             case FrameworkEvent.ERROR :
278                 severity = FrameworkLogEntry.ERROR;
279                 break;
280             case FrameworkEvent.WARNING :
281                 severity = FrameworkLogEntry.WARNING;
282                 break;
283             default :
284                 severity = FrameworkLogEntry.OK;
285         }
286         FrameworkLogEntry logEntry = new FrameworkLogEntry(entry, severity, 0, "", 0, t, null); //$NON-NLS-1$
287
log(logEntry);
288     }
289
290     public synchronized void log(FrameworkLogEntry logEntry) {
291         if (logEntry == null)
292             return;
293         try {
294             checkLogFileSize();
295             openFile();
296             if (newSession) {
297                 writeSession();
298                 newSession = false;
299             }
300             writeLog(0, logEntry);
301             writer.flush();
302         } catch (Exception JavaDoc e) {
303             // any exceptions during logging should be caught
304
System.err.println("An exception occurred while writing to the platform log:");//$NON-NLS-1$
305
e.printStackTrace(System.err);
306             System.err.println("Logging to the console instead.");//$NON-NLS-1$
307
//we failed to write, so dump log entry to console instead
308
try {
309                 writer = logForStream(System.err);
310                 writeLog(0, logEntry);
311                 writer.flush();
312             } catch (Exception JavaDoc e2) {
313                 System.err.println("An exception occurred while logging to the console:");//$NON-NLS-1$
314
e2.printStackTrace(System.err);
315             }
316         } finally {
317             closeFile();
318         }
319     }
320
321     public synchronized void setWriter(Writer newWriter, boolean append) {
322         setOutput(null, newWriter, append);
323     }
324
325     public synchronized void setFile(File newFile, boolean append) throws IOException {
326         if (newFile != null && !newFile.equals(this.outFile)) {
327             // If it's a new file, then reset.
328
readLogProperties();
329             backupIdx = 0;
330         }
331         setOutput(newFile, null, append);
332         FrameworkProperties.setProperty(EclipseStarter.PROP_LOGFILE, newFile.getAbsolutePath());
333     }
334
335     public synchronized File getFile() {
336         return outFile;
337     }
338
339     public void setConsoleLog(boolean consoleLog) {
340         this.consoleLog = consoleLog;
341     }
342
343     private void setOutput(File newOutFile, Writer newWriter, boolean append) {
344         if (newOutFile == null || !newOutFile.equals(this.outFile)) {
345             if (this.writer != null) {
346                 try {
347                     this.writer.close();
348                 } catch (IOException e) {
349                     e.printStackTrace();
350                 }
351                 this.writer = null;
352             }
353             // Append old outFile to newWriter. We only attempt to do this
354
// if the current Writer is backed by a File and this is not
355
// a new session.
356
File oldOutFile = this.outFile;
357             this.outFile = newOutFile;
358             this.writer = newWriter;
359             boolean copyFailed = false;
360             if (append && oldOutFile != null && oldOutFile.isFile()) {
361                 Reader fileIn = null;
362                 try {
363                     openFile();
364                     fileIn = new InputStreamReader(secureAction.getFileInputStream(oldOutFile), "UTF-8"); //$NON-NLS-1$
365
copyReader(fileIn, this.writer);
366                 } catch (IOException e) {
367                     copyFailed = true;
368                     e.printStackTrace();
369                 } finally {
370                     if (fileIn != null) {
371                         try {
372                             fileIn.close();
373                         } catch (IOException e) {
374                             e.printStackTrace();
375                         }
376                         // delete the old file if copying didn't fail
377
if (!copyFailed)
378                             oldOutFile.delete();
379                     }
380                     closeFile();
381                 }
382             }
383         }
384     }
385
386     private void copyReader(Reader reader, Writer aWriter) throws IOException {
387         char buffer[] = new char[1024];
388         int count;
389         while ((count = reader.read(buffer, 0, buffer.length)) > 0) {
390             aWriter.write(buffer, 0, count);
391         }
392     }
393
394     /**
395      * Returns a date string using the correct format for the log.
396      * @param date the Date to format
397      * @return a date string.
398      */

399     protected String JavaDoc getDate(Date JavaDoc date) {
400             Calendar JavaDoc c = Calendar.getInstance();
401             c.setTime(date);
402             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
403             appendPaddedInt(c.get(Calendar.YEAR), 4, sb).append('-');
404             appendPaddedInt(c.get(Calendar.MONTH) + 1, 2, sb).append('-');
405             appendPaddedInt(c.get(Calendar.DAY_OF_MONTH), 2, sb).append(' ');
406             appendPaddedInt(c.get(Calendar.HOUR_OF_DAY), 2, sb).append(':');
407             appendPaddedInt(c.get(Calendar.MINUTE), 2, sb).append(':');
408             appendPaddedInt(c.get(Calendar.SECOND), 2, sb).append('.');
409             appendPaddedInt(c.get(Calendar.MILLISECOND), 3, sb);
410             return sb.toString();
411     }
412
413     private StringBuffer JavaDoc appendPaddedInt(int value, int pad, StringBuffer JavaDoc buffer) {
414         pad = pad - 1;
415         if (pad == 0)
416             return buffer.append(Integer.toString(value));
417         int padding = (int) Math.pow(10, pad);
418         if (value >= padding)
419             return buffer.append(Integer.toString(value));
420         while (padding > value && padding > 1) {
421             buffer.append('0');
422             padding = padding / 10;
423         }
424         buffer.append(value);
425         return buffer;
426     }
427
428     /**
429      * Returns a stacktrace string using the correct format for the log
430      * @param t the Throwable to get the stacktrace for
431      * @return a stacktrace string
432      */

433     protected String JavaDoc getStackTrace(Throwable JavaDoc t) {
434         if (t == null)
435             return null;
436
437         StringWriter sw = new StringWriter();
438         PrintWriter pw = new PrintWriter(sw);
439
440         t.printStackTrace(pw);
441         // ensure the root exception is fully logged
442
Throwable JavaDoc root = getRoot(t);
443         if (root != null) {
444             pw.println("Root exception:"); //$NON-NLS-1$
445
root.printStackTrace(pw);
446         }
447         return sw.toString();
448     }
449
450     /**
451      * Returns a Writer for the given OutputStream
452      * @param output an OutputStream to use for the Writer
453      * @return a Writer for the given OutputStream
454      */

455     protected Writer logForStream(OutputStream output) {
456         try {
457             return new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); //$NON-NLS-1$
458
} catch (UnsupportedEncodingException e) {
459             return new BufferedWriter(new OutputStreamWriter(output));
460         }
461     }
462
463     /**
464      * Writes the log entry to the log using the specified depth. A depth value of 0
465      * idicates that the log entry is the root entry. Any value greater than 0 indicates
466      * a sub-entry.
467      * @param depth the depth of th entry
468      * @param entry the entry to log
469      * @throws IOException if any error occurs writing to the log
470      */

471     protected void writeLog(int depth, FrameworkLogEntry entry) throws IOException {
472         writeEntry(depth, entry);
473         writeMessage(entry);
474         writeStack(entry);
475
476         FrameworkLogEntry[] children = entry.getChildren();
477         if (children != null) {
478             for (int i = 0; i < children.length; i++) {
479                 writeLog(depth + 1, children[i]);
480             }
481         }
482     }
483
484     /**
485      * Writes the ENTRY or SUBENTRY header for an entry. A depth value of 0
486      * idicates that the log entry is the root entry. Any value greater than 0 indicates
487      * a sub-entry.
488      * @param depth the depth of th entry
489      * @param entry the entry to write the header for
490      * @throws IOException if any error occurs writing to the log
491      */

492     protected void writeEntry(int depth, FrameworkLogEntry entry) throws IOException {
493         if (depth == 0) {
494             writeln(); // write a blank line before all !ENTRY tags bug #64406
495
write(ENTRY);
496         } else {
497             write(SUBENTRY);
498             writeSpace();
499             write(Integer.toString(depth));
500         }
501         writeSpace();
502         write(entry.getEntry());
503         if (entry.getSeverity() != 0 || entry.getBundleCode() != 0) {
504             writeSpace();
505             write(Integer.toString(entry.getSeverity()));
506             writeSpace();
507             write(Integer.toString(entry.getBundleCode()));
508         }
509         writeSpace();
510         write(getDate(new Date JavaDoc()));
511         writeln();
512     }
513
514     /**
515      * Writes the MESSAGE header to the log for the given entry.
516      * @param entry the entry to write the message for
517      * @throws IOException if any error occurs writing to the log
518      */

519     protected void writeMessage(FrameworkLogEntry entry) throws IOException {
520         write(MESSAGE);
521         writeSpace();
522         writeln(entry.getMessage());
523     }
524
525     /**
526      * Writes the STACK header to the log for the given entry.
527      * @param entry the entry to write the stacktrace for
528      * @throws IOException if any error occurs writing to the log
529      */

530     protected void writeStack(FrameworkLogEntry entry) throws IOException {
531         Throwable JavaDoc t = entry.getThrowable();
532         if (t != null) {
533             String JavaDoc stack = getStackTrace(t);
534             write(STACK);
535             writeSpace();
536             write(Integer.toString(entry.getStackCode()));
537             writeln();
538             write(stack);
539         }
540     }
541
542     /**
543      * Writes the given message to the log.
544      * @param message the message
545      * @throws IOException if any error occurs writing to the log
546      */

547     protected void write(String JavaDoc message) throws IOException {
548         if (message != null) {
549             writer.write(message);
550             if (consoleLog)
551                 System.out.print(message);
552         }
553     }
554
555     /**
556      * Writes the given message to the log and a newline.
557      * @param s the message
558      * @throws IOException if any error occurs writing to the log
559      */

560     protected void writeln(String JavaDoc s) throws IOException {
561         write(s);
562         writeln();
563     }
564
565     /**
566      * Writes a newline log.
567      * @throws IOException if any error occurs writing to the log
568      */

569     protected void writeln() throws IOException {
570         write(LINE_SEPARATOR);
571     }
572
573     /**
574      * Writes a space to the log.
575      * @throws IOException if any error occurs writing to the log
576      */

577     protected void writeSpace() throws IOException {
578         write(" "); //$NON-NLS-1$
579
}
580
581     /**
582      * Checks the log file size. If the log file size reaches the limit then the log
583      * is rotated
584      * @return false if an error occured trying to rotate the log
585      */

586     protected boolean checkLogFileSize() {
587         if (maxLogSize == 0)
588             return true; // no size limitation.
589

590         boolean isBackupOK = true;
591         if (outFile != null) {
592             if ((outFile.length() >> 10) > maxLogSize) { // Use KB as file size unit.
593
String JavaDoc logFilename = outFile.getAbsolutePath();
594
595                 // Delete old backup file that will be replaced.
596
String JavaDoc backupFilename = ""; //$NON-NLS-1$
597
if (logFilename.toLowerCase().endsWith(LOG_EXT)) {
598                     backupFilename = logFilename.substring(0, logFilename.length() - LOG_EXT.length()) + BACKUP_MARK + backupIdx + LOG_EXT;
599                 } else {
600                     backupFilename = logFilename + BACKUP_MARK + backupIdx;
601                 }
602                 File backupFile = new File(backupFilename);
603                 if (backupFile.exists()) {
604                     if (!backupFile.delete()) {
605                         System.err.println("Error when trying to delete old log file: " + backupFile.getName());//$NON-NLS-1$
606
if (backupFile.renameTo(new File(backupFile.getAbsolutePath() + System.currentTimeMillis()))) {
607                             System.err.println("So we rename it to filename: " + backupFile.getName()); //$NON-NLS-1$
608
} else {
609                             System.err.println("And we also cannot rename it!"); //$NON-NLS-1$
610
isBackupOK = false;
611                         }
612                     }
613                 }
614
615                 // Rename current log file to backup one.
616
boolean isRenameOK = outFile.renameTo(backupFile);
617                 if (!isRenameOK) {
618                     System.err.println("Error when trying to rename log file to backup one."); //$NON-NLS-1$
619
isBackupOK = false;
620                 }
621                 File newFile = new File(logFilename);
622                 setOutput(newFile, null, false);
623
624                 // Write a new SESSION header to new log file.
625
openFile();
626                 try {
627                     writeSession();
628                     writeln();
629                     writeln("This is a continuation of log file " + backupFile.getAbsolutePath());//$NON-NLS-1$
630
writeln("Created Time: " + getDate(new Date JavaDoc(System.currentTimeMillis()))); //$NON-NLS-1$
631
writer.flush();
632                 } catch (IOException ioe) {
633                     ioe.printStackTrace(System.err);
634                 }
635                 closeFile();
636                 backupIdx = (++backupIdx) % maxLogFiles;
637             }
638         }
639         return isBackupOK;
640     }
641
642     /**
643      * Reads the PROP_LOG_SIZE_MAX and PROP_LOG_FILE_MAX properties.
644      */

645     protected void readLogProperties() {
646         String JavaDoc newMaxLogSize = secureAction.getProperty(PROP_LOG_SIZE_MAX);
647         if (newMaxLogSize != null) {
648             maxLogSize = Integer.parseInt(newMaxLogSize);
649             if (maxLogSize != 0 && maxLogSize < LOG_SIZE_MIN) {
650                 // If the value is '0', then it means no size limitation.
651
// Also, make sure no inappropriate(too small) assigned value.
652
maxLogSize = LOG_SIZE_MIN;
653             }
654         }
655
656         String JavaDoc newMaxLogFiles = secureAction.getProperty(PROP_LOG_FILE_MAX);
657         if (newMaxLogFiles != null) {
658             maxLogFiles = Integer.parseInt(newMaxLogFiles);
659             if (maxLogFiles < 1) {
660                 // Make sure no invalid assigned value. (at least >= 1)
661
maxLogFiles = DEFAULT_LOG_FILES;
662             }
663         }
664     }
665 }
666
Popular Tags