NEW - bug 271356: Capture logged error messages
https://bugs.eclipse.org/bugs/show_bug.cgi?id=271356
diff --git a/plugins/org.eclipse.epp.usagedata.gathering/plugin.xml b/plugins/org.eclipse.epp.usagedata.gathering/plugin.xml
index e81ba3f..ecde7f3 100644
--- a/plugins/org.eclipse.epp.usagedata.gathering/plugin.xml
+++ b/plugins/org.eclipse.epp.usagedata.gathering/plugin.xml
@@ -22,6 +22,9 @@
<monitor
class="org.eclipse.epp.usagedata.internal.gathering.monitors.SystemInfoMonitor">
</monitor>
+ <monitor
+ class="org.eclipse.epp.usagedata.internal.gathering.monitors.LogMonitor">
+ </monitor>
</extension>
<extension
point="org.eclipse.core.runtime.preferences">
diff --git a/plugins/org.eclipse.epp.usagedata.gathering/src/org/eclipse/epp/usagedata/internal/gathering/monitors/LogMonitor.java b/plugins/org.eclipse.epp.usagedata.gathering/src/org/eclipse/epp/usagedata/internal/gathering/monitors/LogMonitor.java
new file mode 100644
index 0000000..e9718df
--- /dev/null
+++ b/plugins/org.eclipse.epp.usagedata.gathering/src/org/eclipse/epp/usagedata/internal/gathering/monitors/LogMonitor.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 The Eclipse Foundation.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * The Eclipse Foundation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.epp.usagedata.internal.gathering.monitors;
+
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.epp.usagedata.internal.gathering.services.UsageDataService;
+
+/**
+ * The {@link LogMonitor} class records messages that are
+ * written to the log. Only messages with a severity of {@link IStatus#ERROR}
+ * are recorded.
+ *
+ * @see IStatus#ERROR
+ * @see Platform#addLogListener(ILogListener)
+ */
+public class LogMonitor implements UsageMonitor {
+
+ private static final String WHAT_ERROR = "error";
+ private static final String KIND_LOG = "log";
+
+ private UsageDataService usageDataService;
+ ILogListener listener = new ILogListener() {
+ public void logging(IStatus status, String plugin) {
+ if (status.getSeverity() != IStatus.ERROR) return;
+ usageDataService.recordEvent(WHAT_ERROR, KIND_LOG, status.getMessage(), null);
+ }
+ };
+
+ public void startMonitoring(UsageDataService usageDataService) {
+ this.usageDataService = usageDataService;
+ Platform.addLogListener(listener);
+ }
+
+ public void stopMonitoring() {
+ Platform.removeLogListener(listener);
+ }
+
+}
diff --git a/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/UsageDataRecorderUtils.java b/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/UsageDataRecorderUtils.java
index ff85090..88eb0fd 100644
--- a/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/UsageDataRecorderUtils.java
+++ b/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/UsageDataRecorderUtils.java
@@ -1,17 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 The Eclipse Foundation.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * The Eclipse Foundation - initial API and implementation
+ *******************************************************************************/
package org.eclipse.epp.usagedata.internal.recording;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.eclipse.epp.usagedata.internal.gathering.events.UsageDataEvent;
public class UsageDataRecorderUtils {
-
- private UsageDataRecorderUtils() {
- }
-
- public static void writeHeader(FileWriter writer) throws IOException {
+
+ /**
+ * Write a header onto the {@link Writer}.
+ *
+ * @param writer
+ * a {@link Writer}. Must not be <code>null</code>.
+ * @throws IOException
+ * if writing to the {@link Writer} fails.
+ */
+ public static void writeHeader(Writer writer) throws IOException {
writer.write("what");
writer.write(",");
writer.write("kind");
@@ -27,12 +44,17 @@
}
/**
- * Dump the event on the writer. This method assumes
- * exclusive access to the writer.
+ * Dump the event on the writer. This method assumes exclusive access to the
+ * writer.
*
- * @param writer target for the event information.
- * @param event event to write.
+ * @param writer
+ * target for the event information. Must not be
+ * <code>null</code>.
+ * @param event
+ * event to write. Must not be <code>null</code>.
+ *
* @throws IOException
+ * if writing to the {@link Writer} fails.
*/
public static void writeEvent(Writer writer, UsageDataEvent event) throws IOException {
writer.write(event.what);
@@ -43,9 +65,99 @@
writer.write(",");
writer.write(event.bundleVersion != null ? event.bundleVersion : "");
writer.write(",");
- writer.write(event.description != null ? event.description : "");
+ writer.write(event.description != null ? encode(event.description) : "");
writer.write(",");
writer.write(String.valueOf(event.when));
writer.write("\n");
}
+
+ /**
+ * This method encodes the description so that it can be successfully parsed
+ * as a single entry by a CSV parser. This takes care of most of the
+ * escape characters to ensure that the output gets dumped onto a single
+ * line. We probably take care of more escape characters than we really
+ * need to, but that's better than the opposite...
+ * <p>
+ * The escaped characters should only be an issue in the rare case when
+ * a status message contains them. Most of our monitors do not generate
+ * text that contains them.
+ *
+ * @param description
+ * a {@link String}. Must not be <code>null</code>.
+ *
+ * @return a {@link String}.
+ */
+ public static String encode(String description) {
+ StringBuilder builder = new StringBuilder();
+ builder.append('"');
+ for(int index=0;index<description.length();index++) {
+ char next = description.charAt(index);
+ switch (next) {
+ case '"' :
+ builder.append('"');
+ builder.append(next);
+ break;
+ case '\\' :
+ builder.append("\\\\");
+ break;
+ case '\n' :
+ builder.append("\\n");
+ break;
+ case '\r' :
+ builder.append("\\r");
+ break;
+ case '\b' :
+ builder.append("\\b");
+ break;
+ case '\t' :
+ builder.append("\\t");
+ break;
+ case '\f' :
+ builder.append("\\f");
+ break;
+ default :
+ builder.append(next);
+ }
+ }
+ builder.append('"');
+ return builder.toString();
+ }
+
+ /**
+ * Split the String parameter into substrings. The parameter is assumed to
+ * be in CSV format. Comma separators are assumed. An entry that starts and
+ * ends with double-quotes may contain commas or escaped double-quotes.
+ * Double-quotes are escaped by putting two one-after-the-other. This method
+ * assumes that there are no extraneous white spaces (i.e. leading or
+ * trailing) in the input.
+ * <p>
+ * Note that we don't worry about trying to re-translate escaped characters
+ * back into their unescaped form. We assume that this method is used exclusively
+ * for displaying events in a preview pane, or for applying filters; we don't
+ * need to translate the escaped characters in either of these cases.
+ * <p>
+ * The value: "first,\"\"\"second\"\", third\",fourth" will be parsed into
+ * three strings: "first", "\"second\", third", and "fourth".
+ *
+ * @param line
+ * a {@link String}. Must not be <code>null</code>.
+ *
+ * @return an array of {@link String}s.
+ */
+ public static String[] splitLine(String line) {
+ List<String> strings = new java.util.ArrayList<String>();
+ Matcher matcher = Pattern.compile("(\"([^\"]|\"\")*\"|[^,]*)(,|$)").matcher(line);
+ while (matcher.find()) {
+ String string = matcher.group();
+ // Remove leading commas.
+ string = string.replaceAll(",$", "");
+ // Remove optional leading and trailing double-quotes.
+ string = string.replaceAll("^?\"(.*)\"$", "$1");
+ // Replace double double-quotes with a single double-quote
+ string = string.replaceAll("\"\"", "\"");
+
+ strings.add( string );
+ }
+ return (String[]) strings.toArray(new String[strings.size()]);
+ }
}
diff --git a/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/uploading/UsageDataFileReader.java b/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/uploading/UsageDataFileReader.java
index 8f8c349..7b15998 100644
--- a/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/uploading/UsageDataFileReader.java
+++ b/plugins/org.eclipse.epp.usagedata.recording/src/org/eclipse/epp/usagedata/internal/recording/uploading/UsageDataFileReader.java
@@ -22,6 +22,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.epp.usagedata.internal.gathering.events.UsageDataEvent;
+import org.eclipse.epp.usagedata.internal.recording.UsageDataRecorderUtils;
public class UsageDataFileReader {
public interface Iterator {
@@ -60,7 +61,7 @@
}
private UsageDataEvent createUsageDataEvent(String line) {
- String[] tokens = line.split("\\,");
+ String[] tokens = UsageDataRecorderUtils.splitLine(line);
UsageDataEvent usageDataEvent = new UsageDataEvent(tokens[0], tokens[1], tokens[4], tokens[2], tokens[3], Long.valueOf(tokens[5]));
return usageDataEvent;
}