diff options
Diffstat (limited to 'src/com/android/launcher3/logging/FileLog.java')
-rw-r--r-- | src/com/android/launcher3/logging/FileLog.java | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java new file mode 100644 index 000000000..68d9b8c92 --- /dev/null +++ b/src/com/android/launcher3/logging/FileLog.java @@ -0,0 +1,216 @@ +package com.android.launcher3.logging; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; +import android.util.Pair; + +import com.android.launcher3.LauncherModel; +import com.android.launcher3.Utilities; +import com.android.launcher3.config.ProviderConfig; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Wrapper around {@link Log} to allow writing to a file. + * This class can safely be called from main thread. + * + * Note: This should only be used for logging errors which have a persistent effect on user's data, + * but whose effect may not be visible immediately. + */ +public final class FileLog { + + private static final String FILE_NAME_PREFIX = "log-"; + private static final DateFormat DATE_FORMAT = + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); + + private static final long MAX_LOG_FILE_SIZE = 4 << 20; // 4 mb + + private static Handler sHandler = null; + private static File sLogsDirectory = null; + + public static void setDir(File logsDir) { + sLogsDirectory = logsDir; + } + + public static void d(String tag, String msg, Exception e) { + Log.d(tag, msg, e); + print(tag, msg, e); + } + + public static void d(String tag, String msg) { + Log.d(tag, msg); + print(tag, msg); + } + + public static void e(String tag, String msg, Exception e) { + Log.e(tag, msg, e); + print(tag, msg, e); + } + + public static void e(String tag, String msg) { + Log.e(tag, msg); + print(tag, msg); + } + + public static void print(String tag, String msg) { + print(tag, msg, null); + } + + public static void print(String tag, String msg, Exception e) { + if (!ProviderConfig.IS_DOGFOOD_BUILD) { + return; + } + String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg); + if (e != null) { + out += "\n" + Log.getStackTraceString(e); + } + Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget(); + } + + private static Handler getHandler() { + synchronized (DATE_FORMAT) { + if (sHandler == null) { + HandlerThread thread = new HandlerThread("file-logger"); + thread.start(); + sHandler = new Handler(thread.getLooper(), new LogWriterCallback()); + } + } + return sHandler; + } + + /** + * Blocks until all the pending logs are written to the disk + * @param out if not null, all the persisted logs are copied to the writer. + */ + public static void flushAll(PrintWriter out) throws InterruptedException { + if (!ProviderConfig.IS_DOGFOOD_BUILD) { + return; + } + CountDownLatch latch = new CountDownLatch(1); + Message.obtain(getHandler(), LogWriterCallback.MSG_FLUSH, + Pair.create(out, latch)).sendToTarget(); + + latch.await(2, TimeUnit.SECONDS); + } + + /** + * Writes logs to the file. + * Log files are named log-0 for even days of the year and log-1 for odd days of the year. + * Logs older than 36 hours are purged. + */ + private static class LogWriterCallback implements Handler.Callback { + + private static final long CLOSE_DELAY = 5000; // 5 seconds + + private static final int MSG_WRITE = 1; + private static final int MSG_CLOSE = 2; + private static final int MSG_FLUSH = 3; + + private String mCurrentFileName = null; + private PrintWriter mCurrentWriter = null; + + private void closeWriter() { + Utilities.closeSilently(mCurrentWriter); + mCurrentWriter = null; + } + + @Override + public boolean handleMessage(Message msg) { + if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) { + return true; + } + switch (msg.what) { + case MSG_WRITE: { + Calendar cal = Calendar.getInstance(); + // suffix with 0 or 1 based on the day of the year. + String fileName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) & 1); + + if (!fileName.equals(mCurrentFileName)) { + closeWriter(); + } + + try { + if (mCurrentWriter == null) { + mCurrentFileName = fileName; + + boolean append = false; + File logFile = new File(sLogsDirectory, fileName); + if (logFile.exists()) { + Calendar modifiedTime = Calendar.getInstance(); + modifiedTime.setTimeInMillis(logFile.lastModified()); + + // If the file was modified more that 36 hours ago, purge the file. + // We use instead of 24 to account for day-365 followed by day-1 + modifiedTime.add(Calendar.HOUR, 36); + append = cal.before(modifiedTime) + && logFile.length() < MAX_LOG_FILE_SIZE; + } + mCurrentWriter = new PrintWriter(new FileWriter(logFile, append)); + } + + mCurrentWriter.println((String) msg.obj); + mCurrentWriter.flush(); + + // Auto close file stream after some time. + sHandler.removeMessages(MSG_CLOSE); + sHandler.sendEmptyMessageDelayed(MSG_CLOSE, CLOSE_DELAY); + } catch (Exception e) { + Log.e("FileLog", "Error writing logs to file", e); + // Close stream, will try reopening during next log + closeWriter(); + } + return true; + } + case MSG_CLOSE: { + closeWriter(); + return true; + } + case MSG_FLUSH: { + closeWriter(); + Pair<PrintWriter, CountDownLatch> p = + (Pair<PrintWriter, CountDownLatch>) msg.obj; + + if (p.first != null) { + dumpFile(p.first, FILE_NAME_PREFIX + 0); + dumpFile(p.first, FILE_NAME_PREFIX + 1); + } + p.second.countDown(); + return true; + } + } + return true; + } + } + + private static void dumpFile(PrintWriter out, String fileName) { + File logFile = new File(sLogsDirectory, fileName); + if (logFile.exists()) { + + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(logFile)); + out.println(); + out.println("--- logfile: " + fileName + " ---"); + String line; + while ((line = in.readLine()) != null) { + out.println(line); + } + } catch (Exception e) { + // ignore + } finally { + Utilities.closeSilently(in); + } + } + } +} |