/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.feedback; import org.eclipse.core.runtime.Platform; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; /** * A utility for reading eclipse platform logs. */ public class LogReader { private static class TailInputStream extends InputStream { private final RandomAccessFile randomAccessFile; private final long maxLength; public TailInputStream(File file, long maxLength) throws IOException { super(); this.maxLength = maxLength; randomAccessFile = new RandomAccessFile(file, "r"); //$NON-NLS-1$ skipHead(file); } @Override public void close() throws IOException { randomAccessFile.close(); } @Override public int read() throws IOException { byte[] b = new byte[1]; int len = randomAccessFile.read(b, 0, 1); if (len < 0) { return len; } return b[0]; } @Override public int read(byte[] b) throws IOException { return randomAccessFile.read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { return randomAccessFile.read(b, off, len); } private void skipHead(File file) throws IOException { if (file.length() > maxLength) { randomAccessFile.seek(file.length() - maxLength); // skip bytes until a new line to be sure we start from a beginning of valid UTF-8 character int c = read(); while (c != '\n' && c != 'r' && c != -1) { c = read(); } } } } public static final long MAX_FILE_LENGTH = 23 * 1024; private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; /** * Parse the given content and return the log entries therein. * * @param logContents the log contents * @return the entries or {@code null} if the log contents are null */ public static List<LogEntry> parseEntries(String logContents) { if (logContents == null) { return null; } ArrayList<LogEntry> logEntries = new ArrayList<LogEntry>(); int start = 0; while (start < logContents.length()) { // find beginning of next log entry or EOF int end = start; while (end < logContents.length()) { if (logContents.charAt(end) == '!') { if (end > start) { char ch = logContents.charAt(end - 1); if (ch == '\r' || ch == '\n') { int i = end + 1; while (i < logContents.length() && !Character.isWhitespace(logContents.charAt(i))) { i++; } String tag = logContents.substring(end, i); if (tag.equals(LogEntry.ENTRY_TAG) || tag.equals(LogEntry.SESSION_TAG)) { break; } } } } end++; } // Add the new log entry String entryContent = logContents.substring(start, end).trim(); if (entryContent.length() > 0) { logEntries.add(new LogEntry(entryContent)); } start = end; } return logEntries; } /** * Tail the contents of the log into a String up to {@link #MAX_FILE_LENGTH} characters long. If * an exception occurs in reading the log it is consumed, logged (if possible), and an error * string is returned. * * @return the log contents, or an error string on fail */ public static String readLogSafely() { try { File logFile = getLogFile(); if (logFile.exists()) { return LogReader.readLog(logFile); } else { return "<no log file available>"; } } catch (Exception e) { return "error reading log file: " + e.getMessage(); //$NON-NLS-1$ } } private static File getLogFile() { return Platform.getLogFileLocation().toFile(); } /** * Tail the contents of the log into a String up to {@link #MAX_FILE_LENGTH} characters long. * * @return the log contents * @throws UnsupportedEncodingException * @throws IOException */ private static String readLog(File file) throws UnsupportedEncodingException, IOException { return scrub(toString(new TailInputStream(file, MAX_FILE_LENGTH), "UTF-8")); //$NON-NLS-1$ } /** * Remove obvious references to user details. */ private static String scrub(String logString) { // Users/johndoe/dev/ws/... -> ${user.home}/dev/ws/... String userHome = System.getProperty("user.home"); //$NON-NLS-1$ if (userHome != null) { logString = logString.replace(userHome, "${user.home}"); //$NON-NLS-1$ // // Under Windows the user home contains backslashes but the log prints with forward slashes. // userHome.replace('\\', '/'); logString = logString.replace(userHome, "${user.home}"); //$NON-NLS-1$ } String userName = System.getProperty("user.name"); //$NON-NLS-1$ if (userName != null) { logString = logString.replace("/" + userName + "/", "/${user.name}/"); //$NON-NLS-1$ } return logString; } private static String toString(InputStream input, String encoding) throws IOException { StringWriter out = new StringWriter(); InputStreamReader in = new InputStreamReader(input); char[] buffer = new char[DEFAULT_BUFFER_SIZE]; int n = 0; while (-1 != (n = in.read(buffer))) { out.write(buffer, 0, n); } return out.toString(); } }