Copyright (c) 2009, 2017 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
/*******************************************************************************
* Copyright (c) 2009, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.debug;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.eclipse.core.runtime.adaptor.EclipseStarter;
import org.eclipse.osgi.framework.util.SecureAction;
import org.eclipse.osgi.service.debug.DebugTrace;
The DebugTrace implementation for Eclipse.
/**
* The DebugTrace implementation for Eclipse.
*/
class EclipseDebugTrace implements DebugTrace {
The system property used to specify size a trace file can grow before it is rotated /** The system property used to specify size a trace file can grow before it is rotated */
private static final String PROP_TRACE_SIZE_MAX = "eclipse.trace.size.max"; //$NON-NLS-1$
The system property used to specify the maximum number of backup trace files to use /** The system property used to specify the maximum number of backup trace files to use */
private static final String PROP_TRACE_FILE_MAX = "eclipse.trace.backup.max"; //$NON-NLS-1$
The trace message for a thread stack dump /** The trace message for a thread stack dump */
private final static String MESSAGE_THREAD_DUMP = "Thread Stack dump: "; //$NON-NLS-1$
The trace message for a method completing with a return value /** The trace message for a method completing with a return value */
private final static String MESSAGE_EXIT_METHOD_WITH_RESULTS = "Exiting method {0}with result: "; //$NON-NLS-1$
The trace message for a method completing with no return value /** The trace message for a method completing with no return value */
private final static String MESSAGE_EXIT_METHOD_NO_RESULTS = "Exiting method {0}with a void return"; //$NON-NLS-1$
The trace message for a method starting with a set of arguments /** The trace message for a method starting with a set of arguments */
private final static String MESSAGE_ENTER_METHOD_WITH_PARAMS = "Entering method {0}with parameters: ("; //$NON-NLS-1$
The trace message for a method starting with no arguments /** The trace message for a method starting with no arguments */
private final static String MESSAGE_ENTER_METHOD_NO_PARAMS = "Entering method {0}with no parameters"; //$NON-NLS-1$
The version attribute written in the header of a new session /** The version attribute written in the header of a new session */
private final static String TRACE_FILE_VERSION_COMMENT = "version: "; //$NON-NLS-1$
The verbose attribute written in the header of a new session /** The verbose attribute written in the header of a new session */
private final static String TRACE_FILE_VERBOSE_COMMENT = "verbose: "; //$NON-NLS-1$
The version value written in the header of a new session /** The version value written in the header of a new session */
private final static String TRACE_FILE_VERSION = "1.1"; //$NON-NLS-1$
The new session identifier to be written whenever a new session starts /** The new session identifier to be written whenever a new session starts */
private final static String TRACE_NEW_SESSION = "!SESSION "; //$NON-NLS-1$
The date attribute written to the header of the trace file to show when this file was created /** The date attribute written to the header of the trace file to show when this file was created */
private final static String TRACE_FILE_DATE = "Time of creation: "; //$NON-NLS-1$
Trace date formatter using the pattern: yyyy-MM-dd HH:mm:ss.SSS /** Trace date formatter using the pattern: yyyy-MM-dd HH:mm:ss.SSS */
private final static SimpleDateFormat TRACE_FILE_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //$NON-NLS-1$
The comment character used by the trace file /** The comment character used by the trace file */
private final static String TRACE_COMMENT = "#"; //$NON-NLS-1$
The delimiter used to separate trace elements such as the time stamp, message, etc /** The delimiter used to separate trace elements such as the time stamp, message, etc */
private final static String TRACE_ELEMENT_DELIMITER = "|"; //$NON-NLS-1$
The string written in place of the TRACE_TRACE_ELEMENT_DELIMITER
in entries /** The string written in place of the {@link EclipseDebugTrace#TRACE_TRACE_ELEMENT_DELIMITER} in entries */
private final static String TRACE_ELEMENT_DELIMITER_ENCODED = "|"; //$NON-NLS-1$
OS-specific line separator /** OS-specific line separator */
private static final String LINE_SEPARATOR;
static {
String s = System.getProperty("line.separator"); //$NON-NLS-1$
LINE_SEPARATOR = s == null ? "\n" : s; //$NON-NLS-1$
}
The value written to the trace file if a null object is being traced /** The value written to the trace file if a null object is being traced */
private final static String NULL_VALUE = "<null>"; //$NON-NLS-1$
/** */
private final static SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
/******************* Tracing file attributes **************************/
The default size a trace file can grow before it is rotated /** The default size a trace file can grow before it is rotated */
private static final int DEFAULT_TRACE_FILE_SIZE = 1000; // The value is in KB.
The default number of backup trace files /** The default number of backup trace files */
private static final int DEFAULT_TRACE_FILES = 10;
The minimum size limit for trace file rotation /** The minimum size limit for trace file rotation */
private static final int DEFAULT_TRACE_FILE_MIN_SIZE = 10;
The extension used for log files /** The extension used for log files */
private static final String TRACE_FILE_EXTENSION = ".trace"; //$NON-NLS-1$
The extension markup to use for backup log files/** The extension markup to use for backup log files*/
private static final String BACKUP_MARK = ".bak_"; //$NON-NLS-1$
The maximum size that a trace file should grow (0 = unlimited) /** The maximum size that a trace file should grow (0 = unlimited) */
private int maxTraceFileSize = DEFAULT_TRACE_FILE_SIZE; // The value is in KB.
The maximum number of trace files that should be saved /** The maximum number of trace files that should be saved */
private int maxTraceFiles = DEFAULT_TRACE_FILES;
The index of the currently backed-up trace file /** The index of the currently backed-up trace file */
private int backupTraceFileIndex = 0;
An optional argument to specify the name of the class used by clients to trace messages. If no trace class is specified
then the class calling this API is assumed to be the class being traced.
/** An optional argument to specify the name of the class used by clients to trace messages. If no trace class is specified
* then the class calling this API is assumed to be the class being traced.
*/
private String traceClass = null;
The symbolic name of the bundle being traced /** The symbolic name of the bundle being traced */
private String bundleSymbolicName = null;
DebugOptions are used to determine if the specified bundle symbolic name + option-path has debugging enabled /** DebugOptions are used to determine if the specified bundle symbolic name + option-path has debugging enabled */
private FrameworkDebugOptions debugOptions = null;
private final boolean consoleLog;
Construct a new EclipseDebugTrace for the specified bundle symbolic name and write messages to the specified
trace file.
Params: - bundleSymbolicName – The symbolic name of the bundle being traced
- debugOptions – Used to determine if the specified bundle symbolic name + option-path has tracing enabled
- traceClass – The class that the client is using to perform trace API calls
/**
* Construct a new EclipseDebugTrace for the specified bundle symbolic name and write messages to the specified
* trace file.
*
* @param bundleSymbolicName The symbolic name of the bundle being traced
* @param debugOptions Used to determine if the specified bundle symbolic name + option-path has tracing enabled
* @param traceClass The class that the client is using to perform trace API calls
*/
EclipseDebugTrace(final String bundleSymbolicName, final FrameworkDebugOptions debugOptions, final Class<?> traceClass) {
this.consoleLog = "true".equals(debugOptions.getConfiguration().getConfiguration(EclipseStarter.PROP_CONSOLE_LOG)); //$NON-NLS-1$
this.traceClass = traceClass != null ? traceClass.getName() : null;
this.debugOptions = debugOptions;
this.bundleSymbolicName = bundleSymbolicName;
readLogProperties();
}
Is debugging enabled for the specified option-path
Params: - optionPath – The option-path
Returns: Returns true if debugging is enabled for the specified option-path on this bundle; Otherwise false.
/**
* Is debugging enabled for the specified option-path
*
* @param optionPath The <i>option-path</i>
* @return Returns true if debugging is enabled for the specified option-path on this bundle; Otherwise false.
*/
private final boolean isDebuggingEnabled(final String optionPath) {
if (optionPath == null)
return true;
boolean debugEnabled = false;
if (debugOptions.isDebugEnabled()) {
final String option = bundleSymbolicName + optionPath;
debugEnabled = debugOptions.getBooleanOption(option, false);
}
return debugEnabled;
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#trace(java.lang.String, java.lang.String)
*/
@Override
public void trace(final String optionPath, final String message) {
if (isDebuggingEnabled(optionPath)) {
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, message, traceClass);
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#trace(java.lang.String, java.lang.String, java.lang.Throwable)
*/
@Override
public void trace(final String optionPath, final String message, final Throwable error) {
if (isDebuggingEnabled(optionPath)) {
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, message, error, traceClass);
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceEntry(java.lang.String)
*/
@Override
public void traceEntry(final String optionPath) {
if (isDebuggingEnabled(optionPath)) {
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, null, traceClass);
setMessage(record, EclipseDebugTrace.MESSAGE_ENTER_METHOD_NO_PARAMS);
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceEntry(java.lang.String, java.lang.Object)
*/
@Override
public void traceEntry(final String optionPath, final Object methodArgument) {
if (isDebuggingEnabled(optionPath)) {
traceEntry(optionPath, new Object[] {methodArgument});
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceEntry(java.lang.String, java.lang.Object[])
*/
@Override
public void traceEntry(final String optionPath, final Object[] methodArguments) {
if (isDebuggingEnabled(optionPath)) {
final StringBuilder messageBuffer = new StringBuilder(EclipseDebugTrace.MESSAGE_ENTER_METHOD_WITH_PARAMS);
if (methodArguments != null) {
int i = 0;
while (i < methodArguments.length) {
if (methodArguments[i] != null) {
messageBuffer.append(methodArguments[i].toString());
} else {
messageBuffer.append(EclipseDebugTrace.NULL_VALUE);
}
i++;
if (i < methodArguments.length) {
messageBuffer.append(" "); //$NON-NLS-1$
}
}
messageBuffer.append(")"); //$NON-NLS-1$
}
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, null, traceClass);
setMessage(record, messageBuffer.toString());
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceExit(java.lang.String)
*/
@Override
public void traceExit(final String optionPath) {
if (isDebuggingEnabled(optionPath)) {
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, null, traceClass);
setMessage(record, EclipseDebugTrace.MESSAGE_EXIT_METHOD_NO_RESULTS);
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceExit(java.lang.String, java.lang.Object)
*/
@Override
public void traceExit(final String optionPath, final Object result) {
if (isDebuggingEnabled(optionPath)) {
final StringBuilder messageBuffer = new StringBuilder(EclipseDebugTrace.MESSAGE_EXIT_METHOD_WITH_RESULTS);
if (result == null) {
messageBuffer.append(EclipseDebugTrace.NULL_VALUE);
} else {
messageBuffer.append(result.toString());
}
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, null, traceClass);
setMessage(record, messageBuffer.toString());
writeRecord(record);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.osgi.framework.debug.FrameworkDebugTrace#traceDumpStack(java.lang.String)
*/
@Override
public void traceDumpStack(final String optionPath) {
if (isDebuggingEnabled(optionPath)) {
final StringBuilder messageBuffer = new StringBuilder(EclipseDebugTrace.MESSAGE_THREAD_DUMP);
StackTraceElement[] elements = new Exception().getStackTrace();
// the first element in this stack trace is going to be this class, so ignore it
// the second element in this stack trace is going to either be the caller or the trace class. Ignore it only if a traceClass is defined
// the rest of the elements should be included in the file array
int firstIndex = (traceClass == null) ? 1 : 2;
int endIndex = elements.length - firstIndex;
final StackTraceElement[] newElements = new StackTraceElement[endIndex];
int i = 0;
while (i < endIndex) {
newElements[i] = elements[firstIndex];
i++;
firstIndex++;
}
messageBuffer.append(convertStackTraceElementsToString(newElements));
final FrameworkDebugTraceEntry record = new FrameworkDebugTraceEntry(bundleSymbolicName, optionPath, messageBuffer.toString(), traceClass);
writeRecord(record);
}
}
Set the trace message for the specified record to include class and method information
if verbose debugging is disabled.
Params: - record – The
FrameworkDebugTraceEntry
containing the information to persist to the trace file. - originalMessage – The original tracing message
/**
* Set the trace message for the specified record to include class and method information
* if verbose debugging is disabled.
*
* @param record The {@link FrameworkDebugTraceEntry} containing the information to persist to the trace file.
* @param originalMessage The original tracing message
*/
private final void setMessage(final FrameworkDebugTraceEntry record, final String originalMessage) {
String argument = null;
if (!debugOptions.isVerbose()) {
final StringBuilder classMethodName = new StringBuilder(record.getClassName());
classMethodName.append("#"); //$NON-NLS-1$
classMethodName.append(record.getMethodName());
classMethodName.append(" "); //$NON-NLS-1$
argument = classMethodName.toString();
} else {
argument = ""; //$NON-NLS-1$
}
String newMessage = MessageFormat.format(originalMessage, new Object[] {argument});
record.setMessage(newMessage);
}
Utility method to convert an array of StackTraceElement objects to form a String representation of a stack dump
Params: - elements –
The array of StackTraceElement objects
Returns: A String of the stack dump produced by the list of elements
/**
* Utility method to convert an array of StackTraceElement objects to form a String representation of a stack dump
*
* @param elements
* The array of StackTraceElement objects
* @return A String of the stack dump produced by the list of elements
*/
private final String convertStackTraceElementsToString(final StackTraceElement[] elements) {
final StringBuilder buffer = new StringBuilder();
if (elements != null) {
buffer.append("java.lang.Throwable: "); //$NON-NLS-1$
buffer.append(EclipseDebugTrace.LINE_SEPARATOR);
int i = 0;
while (i < elements.length) {
if (elements[i] != null) {
buffer.append("\tat "); //$NON-NLS-1$
buffer.append(elements[i].toString());
buffer.append(EclipseDebugTrace.LINE_SEPARATOR);
}
i++;
}
}
return buffer.toString();
}
Write the specified FrameworkTraceEntry to trace file
Params: - entry – The FrameworkTraceEntry to write to the log file.
/**
* Write the specified FrameworkTraceEntry to trace file
*
* @param entry The FrameworkTraceEntry to write to the log file.
*/
private void writeRecord(final FrameworkDebugTraceEntry entry) {
if (entry != null) {
synchronized (debugOptions.getWriteLock()) {
final File tracingFile = debugOptions.getFile(); // the tracing file may be null if it has not been set
Writer traceWriter = null;
try {
// check to see if the file should be rotated
checkTraceFileSize(tracingFile, entry.getTimestamp());
// open the trace file
traceWriter = openWriter(tracingFile);
if (debugOptions.newSession()) {
writeSession(traceWriter, entry.getTimestamp());
}
writeMessage(traceWriter, entry);
// flush the writer
traceWriter.flush();
} catch (Exception ex) {
// any exceptions during tracing should be caught
System.err.println("An exception occurred while writing to the platform trace file: ");//$NON-NLS-1$
ex.printStackTrace(System.err);
} finally {
// close the trace writer
closeWriter(traceWriter);
}
}
}
}
Reads the PROP_TRACE_SIZE_MAX and PROP_TRACE_FILE_MAX properties.
/**
* Reads the PROP_TRACE_SIZE_MAX and PROP_TRACE_FILE_MAX properties.
*/
private void readLogProperties() {
String newMaxTraceFileSize = debugOptions.getConfiguration().getConfiguration(PROP_TRACE_SIZE_MAX);
if (newMaxTraceFileSize != null) {
maxTraceFileSize = Integer.parseInt(newMaxTraceFileSize);
if (maxTraceFileSize != 0 && maxTraceFileSize < DEFAULT_TRACE_FILE_MIN_SIZE) {
// If the value is '0', then it means no size limitation.
// Also, make sure no inappropriate(too small) assigned value.
maxTraceFileSize = DEFAULT_TRACE_FILE_MIN_SIZE;
}
}
String newMaxLogFiles = debugOptions.getConfiguration().getConfiguration(PROP_TRACE_FILE_MAX);
if (newMaxLogFiles != null) {
maxTraceFiles = Integer.parseInt(newMaxLogFiles);
if (maxTraceFiles < 1) {
// Make sure no invalid assigned value. (at least >= 1)
maxTraceFiles = DEFAULT_TRACE_FILES;
}
}
}
Checks the trace file size. If the file size reaches the limit then the trace file is rotated.
Params: - traceFile – The tracing file
- timestamp – the timestamp for the session; this is the same timestamp as the first entry
Returns: false if an error occurred trying to rotate the trace file
/**
* Checks the trace file size. If the file size reaches the limit then the trace file is rotated.
*
* @param traceFile The tracing file
* @param timestamp the timestamp for the session; this is the same timestamp as the first entry
* @return false if an error occurred trying to rotate the trace file
*/
private boolean checkTraceFileSize(final File traceFile, long timestamp) {
// 0 file size means there is no size limit
boolean isBackupOK = true;
if (maxTraceFileSize > 0) {
if ((traceFile != null) && traceFile.exists()) {
if ((traceFile.length() >> 10) > maxTraceFileSize) { // Use KB as file size unit.
final String traceFileName = traceFile.getAbsolutePath();
// Delete old backup file that will be replaced.
String backupFilename = ""; //$NON-NLS-1$
if (traceFileName.toLowerCase().endsWith(TRACE_FILE_EXTENSION)) {
backupFilename = traceFileName.substring(0, traceFileName.length() - TRACE_FILE_EXTENSION.length()) + BACKUP_MARK + backupTraceFileIndex + TRACE_FILE_EXTENSION;
} else {
backupFilename = traceFileName + BACKUP_MARK + backupTraceFileIndex;
}
final File backupFile = new File(backupFilename);
if (backupFile.exists()) {
if (!backupFile.delete()) {
System.err.println("Error when trying to delete old trace file: " + backupFile.getName());//$NON-NLS-1$
if (backupFile.renameTo(new File(backupFile.getAbsolutePath() + System.currentTimeMillis()))) {
System.err.println("So we rename it to filename: " + backupFile.getName()); //$NON-NLS-1$
} else {
System.err.println("And we also cannot rename it!"); //$NON-NLS-1$
isBackupOK = false;
}
}
}
// Rename current log file to backup one.
boolean isRenameOK = traceFile.renameTo(backupFile);
if (!isRenameOK) {
System.err.println("Error when trying to rename trace file to backup one."); //$NON-NLS-1$
isBackupOK = false;
}
/*
* Write a header to new log file stating that this new file is a continuation file.
* This method should already be called with the file lock set so we should be safe
* to update it here.
*/
Writer traceWriter = null;
try {
traceWriter = openWriter(traceFile);
writeComment(traceWriter, "This is a continuation of trace file " + backupFile.getAbsolutePath()); //$NON-NLS-1$
writeComment(traceWriter, EclipseDebugTrace.TRACE_FILE_VERSION_COMMENT + EclipseDebugTrace.TRACE_FILE_VERSION);
writeComment(traceWriter, EclipseDebugTrace.TRACE_FILE_VERBOSE_COMMENT + debugOptions.isVerbose());
writeComment(traceWriter, EclipseDebugTrace.TRACE_FILE_DATE + getFormattedDate(timestamp));
traceWriter.flush();
} catch (IOException ioEx) {
ioEx.printStackTrace();
} finally {
closeWriter(traceWriter);
}
backupTraceFileIndex = (++backupTraceFileIndex) % maxTraceFiles;
}
}
}
return isBackupOK;
}
Writes a comment to the trace file
Params: - traceWriter – the trace writer
- comment – the comment to be written to the trace file
Throws: - IOException – If an error occurs while writing the comment
/**
* Writes a comment to the trace file
*
* @param traceWriter the trace writer
* @param comment the comment to be written to the trace file
* @throws IOException If an error occurs while writing the comment
*/
private void writeComment(final Writer traceWriter, final String comment) throws IOException {
StringBuilder commentText = new StringBuilder(EclipseDebugTrace.TRACE_COMMENT);
commentText.append(" "); //$NON-NLS-1$
commentText.append(comment);
commentText.append(EclipseDebugTrace.LINE_SEPARATOR);
traceWriter.write(commentText.toString());
}
Accessor to retrieve the time stamp in a formatted manner.
Returns: A formatted time stamp based on the TRACE_FILE_DATE_FORMATTER
formatter
/**
* Accessor to retrieve the time stamp in a formatted manner.
*
* @return A formatted time stamp based on the {@link EclipseDebugTrace#TRACE_FILE_DATE_FORMATTER} formatter
*/
private final String getFormattedDate(long timestamp) {
return EclipseDebugTrace.TRACE_FILE_DATE_FORMATTER.format(new Date(timestamp));
}
Accessor to retrieve the text of a Throwable
in a formatted manner so that it can be written to the trace file. Params: - error – The {@lnk Throwable} to format
Returns: The complete text of a Throwable
as a String
or null if the input error is null.
/**
* Accessor to retrieve the text of a {@link Throwable} in a formatted manner so that it can be written to the
* trace file.
*
* @param error The {@lnk Throwable} to format
* @return The complete text of a {@link Throwable} as a {@link String} or null if the input error is null.
*/
private final String getFormattedThrowable(Throwable error) {
String result = null;
if (error != null) {
PrintStream throwableStream = null;
try {
ByteArrayOutputStream throwableByteOutputStream = new ByteArrayOutputStream();
throwableStream = new PrintStream(throwableByteOutputStream, false);
error.printStackTrace(throwableStream);
result = encodeText(throwableByteOutputStream.toString());
} finally {
if (throwableStream != null) {
throwableStream.close();
}
}
}
return result;
}
Writes header information to a new trace file
Params: - traceWriter – the trace writer
- timestamp – the timestamp for the session; this is the same timestamp as the first entry
Throws: - IOException – If an error occurs while writing this session information
/**
* Writes header information to a new trace file
*
* @param traceWriter the trace writer
* @param timestamp the timestamp for the session; this is the same timestamp as the first entry
* @throws IOException If an error occurs while writing this session information
*/
private void writeSession(final Writer traceWriter, long timestamp) throws IOException {
writeComment(traceWriter, EclipseDebugTrace.TRACE_NEW_SESSION + this.getFormattedDate(timestamp));
writeComment(traceWriter, EclipseDebugTrace.TRACE_FILE_VERSION_COMMENT + EclipseDebugTrace.TRACE_FILE_VERSION);
writeComment(traceWriter, EclipseDebugTrace.TRACE_FILE_VERBOSE_COMMENT + debugOptions.isVerbose());
writeComment(traceWriter, "The following option strings are specified for this debug session:"); //$NON-NLS-1$
final String[] allOptions = debugOptions.getAllOptions();
for (String allOption : allOptions) {
writeComment(traceWriter, "\t" + allOption); //$NON-NLS-1$
}
}
Writes the specified trace entry object to the trace file using the TRACE_ELEMENT_DELIMITER
as the delimiter between each element of the entry. Params: - traceWriter – the trace writer
- entry – The trace entry object to write to the trace file
Throws: - IOException – If an error occurs while writing this message
/**
* Writes the specified trace entry object to the trace file using the
* {@link EclipseDebugTrace#TRACE_ELEMENT_DELIMITER} as the delimiter between
* each element of the entry.
*
* @param traceWriter the trace writer
* @param entry The trace entry object to write to the trace file
* @throws IOException If an error occurs while writing this message
*/
private void writeMessage(final Writer traceWriter, final FrameworkDebugTraceEntry entry) throws IOException {
final StringBuilder message = new StringBuilder(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(encodeText(entry.getThreadName()));
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(this.getFormattedDate(entry.getTimestamp()));
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
if (!debugOptions.isVerbose()) {
// format the trace entry for quiet tracing: only the thread name, timestamp, trace message, and exception (if necessary)
message.append(encodeText(entry.getMessage()));
} else {
// format the trace entry for verbose tracing
message.append(entry.getBundleSymbolicName());
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(encodeText(entry.getOptionPath()));
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(entry.getClassName());
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(entry.getMethodName());
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(entry.getLineNumber());
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(encodeText(entry.getMessage()));
}
if (entry.getThrowable() != null) {
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(" "); //$NON-NLS-1$
message.append(this.getFormattedThrowable(entry.getThrowable()));
}
message.append(" "); //$NON-NLS-1$
message.append(EclipseDebugTrace.TRACE_ELEMENT_DELIMITER);
message.append(EclipseDebugTrace.LINE_SEPARATOR);
// write the message
if ((traceWriter != null) && (message != null)) {
traceWriter.write(message.toString());
}
}
Encodes the specified string to replace any occurrence of the TRACE_ELEMENT_DELIMITER
string with the TRACE_ELEMENT_DELIMITER_ENCODED
string. This can be used to ensure that the delimiter character does not break parsing when the entry text contains the delimiter character. Params: - inputString – The original string to be written to the trace file.
Returns: The original input string with all occurrences of TRACE_ELEMENT_DELIMITER
replaced with TRACE_ELEMENT_DELIMITER_ENCODED
. A null
value will be
returned if the input string is null
.
/**
* Encodes the specified string to replace any occurrence of the {@link EclipseDebugTrace#TRACE_ELEMENT_DELIMITER}
* string with the {@link EclipseDebugTrace#TRACE_ELEMENT_DELIMITER_ENCODED}
* string. This can be used to ensure that the delimiter character does not break parsing when
* the entry text contains the delimiter character.
*
* @param inputString The original string to be written to the trace file.
* @return The original input string with all occurrences of
* {@link EclipseDebugTrace#TRACE_ELEMENT_DELIMITER} replaced with
* {@link EclipseDebugTrace#TRACE_ELEMENT_DELIMITER_ENCODED}. A <code>null</code> value will be
* returned if the input string is <code>null</code>.
*/
private static String encodeText(final String inputString) {
if (inputString == null || inputString.indexOf(TRACE_ELEMENT_DELIMITER) < 0)
return inputString;
final StringBuilder tempBuffer = new StringBuilder(inputString);
int currentIndex = tempBuffer.indexOf(TRACE_ELEMENT_DELIMITER);
while (currentIndex >= 0) {
tempBuffer.replace(currentIndex, currentIndex + TRACE_ELEMENT_DELIMITER.length(), TRACE_ELEMENT_DELIMITER_ENCODED);
currentIndex = tempBuffer.indexOf(TRACE_ELEMENT_DELIMITER);
}
return tempBuffer.toString();
}
Returns a Writer for the given OutputStream
Params: - output – an OutputStream to use for the Writer
Returns: A Writer for the given OutputStream
/**
* Returns a Writer for the given OutputStream
* @param output an OutputStream to use for the Writer
* @return A Writer for the given OutputStream
*/
private Writer logForStream(OutputStream output) {
return new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
}
Creates the trace writer.
If the tracing file is null then the writer will use System.out to print any messages.
Params: - traceFile – The tracing file
Returns: Returns a new Writer object
/**
* Creates the trace writer.
* If the tracing file is null then the writer will use System.out to print any messages.
*
* @param traceFile The tracing file
* @return Returns a new Writer object
*/
private Writer openWriter(final File traceFile) {
OutputStream out = null;
if (traceFile != null) {
try {
out = secureAction.getFileOutputStream(traceFile, true);
} catch (IOException ioEx) {
// ignore and fall back to system.out; but print error message to indicate what happened
System.err.println("Unable to open trace file: " + traceFile + ": " + ioEx.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (out == null) {
out = new FilterOutputStream(System.out) {
Throws: - IOException –
/**
* @throws IOException
*/
@Override
public void close() throws IOException {
// We don't want to close System.out
}
@Override
public void write(byte[] var0, int var1, int var2) throws IOException {
this.out.write(var0, var1, var2);
}
};
} else if (consoleLog) {
out = new FilterOutputStream(out) {
@Override
public void write(int b) throws IOException {
System.out.write(b);
out.write(b);
}
@Override
public void write(byte[] b) throws IOException {
System.out.write(b);
out.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
System.out.write(b, off, len);
out.write(b, off, len);
}
};
}
return logForStream(out);
}
Close the trace writer
Params: - traceWriter – The trace writer
/**
* Close the trace writer
*
* @param traceWriter The trace writer
*/
private void closeWriter(Writer traceWriter) {
if (traceWriter != null) {
try {
traceWriter.close();
} catch (IOException ioEx) {
// we cannot log here; just print the stacktrace.
ioEx.printStackTrace();
}
traceWriter = null;
}
}
}