Back to project page android-filelogger.
The source code is released under:
Apache License
If you think the Android project android-filelogger listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.levelup.logutils; /* w w w . j a v a 2s . c o m*/ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import android.annotation.SuppressLint; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.Log; public class FileLogger { private static final String LOG_NAME = "log.csv"; private static final String LOG_NAME_ALTERNATIVE = "log_a.csv"; private static final String LOG_HEAD = "Time,Level,PID,TID,App,Tag,Message"; private static final String TAG = "FileLogger"; private static final int MSG_WRITE = 0; // paired with a LogMessage private static final int MSG_COLLECT = 1; private static final int MSG_CLEAR = 2; private static final int MSG_OPEN = 3; private final File file1; private final File file2; private long maxFileSize = 102400; private File mCurrentLogFile; private Writer writer; private String mTag; private String applicationTag; private Handler mSaveStoreHandler; /** * Path where the log must be collected */ private File finalPath; private FLogLevel logLevel = FLogLevel.I; /** * Create a file for writing logs. * * @param logFolder * the folder path where the logs are stored * @param tag * tag used for message without tag * @throws IOException */ public FileLogger(File logFolder, String tag) throws IOException { this(logFolder); this.mTag = tag; } /** * Create a file for writing logs. * * @param logFolder * the folder path where the logs are stored * @throws IOException */ @SuppressLint("HandlerLeak") public FileLogger(File logFolder) throws IOException { this.file1 = new File(logFolder, LOG_NAME); this.file2 = new File(logFolder, LOG_NAME_ALTERNATIVE); if (!logFolder.exists()) logFolder.mkdirs(); if (!logFolder.isDirectory()) { Log.e(TAG, logFolder + " is not a folder"); throw new IOException("Path is not a directory"); } if (!logFolder.canWrite()) { Log.e(TAG, logFolder + " is not a writable"); throw new IOException("Folder is not writable"); } mCurrentLogFile = chooseFileToWrite(); // Initializing the HandlerThread HandlerThread handlerThread = new HandlerThread("FileLogger", android.os.Process.THREAD_PRIORITY_BACKGROUND); if (!handlerThread.isAlive()) { handlerThread.start(); mSaveStoreHandler = new Handler(handlerThread.getLooper()) { public void handleMessage(Message msg) { switch (msg.what) { case MSG_OPEN: try { closeWriter(); } catch (IOException ignored) { } openWriter(); break; case MSG_WRITE: try { LogMessage logmsg = (LogMessage) msg.obj; if (writer==null) Log.e(TAG, "no writer"); else { writer.append(logmsg.formatCsv()); writer.flush(); } } catch (OutOfMemoryError e) { Log.e(TAG, e.getClass().getSimpleName() + " : " + e.getMessage()); } catch (IOException e) { Log.e(TAG, e.getClass().getSimpleName() + " : " + e.getMessage()); } verifyFileSize(); break; case MSG_COLLECT: if (!mCurrentLogFile.exists() || mCurrentLogFile.length() == 0) { ((LogCollecting) msg.obj).onEmptyLogCollected(); break; } // Get the phone information if (finalPath.getParentFile()!=null) finalPath.getParentFile().mkdirs(); try { finalPath.createNewFile(); if (!finalPath.canWrite()) ((LogCollecting) msg.obj).onLogCollectingError("Can't write on "+finalPath); else { finalPath.delete(); try { OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(finalPath, true), "UTF-8"); out.append(LOG_HEAD); out.append('\n'); out.flush(); } catch (UnsupportedEncodingException e) { Log.e(TAG, "UnsupportedEncodingException: " + e.getMessage(), e); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: " + e.getMessage(), e); } // Merge the files final File olderLogFile; if (mCurrentLogFile==file2) olderLogFile = file1; else olderLogFile = file2; if (olderLogFile.exists()) mergeFile(olderLogFile, finalPath); mergeFile(mCurrentLogFile, finalPath); ((LogCollecting) msg.obj).onLogCollected(finalPath, "text/csv"); } } catch (IOException e) { ((LogCollecting) msg.obj).onLogCollectingError(e.getMessage()+" - file:"+finalPath); } break; case MSG_CLEAR: try { closeWriter(); } catch (IOException e) { Log.e(TAG, e.getMessage(), e); } finally { file1.delete(); file2.delete(); mCurrentLogFile = file1; openWriter(); } break; } } }; mSaveStoreHandler.sendEmptyMessage(MSG_OPEN); } } public void setLogLevel(FLogLevel level) { logLevel = level; } public void setMaxFileSize(long maxFileSize) { this.maxFileSize = maxFileSize; } private File chooseFileToWrite() { if (!file1.exists() && !file2.exists()) return file1; if (file1.exists() && file1.length() < maxFileSize) return file1; return file2; } public void d(String tag, String msg, Throwable tr) { if (logLevel.allows(FLogLevel.D)) write('d', tag, msg, tr); } public void d(String tag, String msg) { if (logLevel.allows(FLogLevel.D)) write('d', tag, msg); } public void d(String msg) { if (logLevel.allows(FLogLevel.D)) write('d', msg); } public void e(String tag, String msg, Throwable tr) { if (logLevel.allows(FLogLevel.E)) write('e', tag, msg, tr); } public void e(String tag, String msg) { if (logLevel.allows(FLogLevel.E)) write('e', tag, msg); } public void e(String msg) { if (logLevel.allows(FLogLevel.E)) write('e', msg); } public void wtf(String tag, String msg, Throwable tr) { if (logLevel.allows(FLogLevel.WTF)) write('f', tag, msg, tr); } public void wtf(String tag, String msg) { if (logLevel.allows(FLogLevel.WTF)) write('f', tag, msg); } public void wtf(String msg) { if (logLevel.allows(FLogLevel.WTF)) write('f', msg); } public void i(String msg, String tag, Throwable tr) { if (logLevel.allows(FLogLevel.I)) write('i', tag, msg, tr); } public void i(String msg, String tag) { if (logLevel.allows(FLogLevel.I)) write('i', tag, msg); } public void i(String msg) { if (logLevel.allows(FLogLevel.I)) write('i', msg); } public void v(String msg, String tag, Throwable tr) { if (logLevel.allows(FLogLevel.V)) write('v', tag, msg, tr); } public void v(String msg, String tag) { if (logLevel.allows(FLogLevel.V)) write('v', tag, msg); } public void v(String msg) { if (logLevel.allows(FLogLevel.V)) write('v', msg); } public void w(String tag, String msg, Throwable tr) { if (logLevel.allows(FLogLevel.W)) write('w', tag, msg, tr); } public void w(String tag, String msg) { if (logLevel.allows(FLogLevel.W)) write('w', tag, msg); } public void w(String msg) { if (logLevel.allows(FLogLevel.W)) write('w', msg); } private void write(char lvl, String message) { String tag; if (mTag == null) tag = TAG; else tag = mTag; write(lvl, tag, message); } private void write(char lvl, String tag, String message) { write(lvl, tag, message, null); } protected void write(char lvl, String tag, String message, Throwable tr) { if (tag == null) { write(lvl, message); return; } Message htmsg = Message.obtain(mSaveStoreHandler, MSG_WRITE, new LogMessage(lvl, tag, getApplicationLocalTag(), Thread.currentThread().getName(), message, tr)); mSaveStoreHandler.sendMessage(htmsg); } private static class LogMessage { private static SimpleDateFormat dateFormat; // must always be used in the same thread private static Date mDate; private final long now; private final char level; private final String tag; private final String appTag; private final String threadName; private final String msg; private final Throwable cause; private String date; LogMessage(char lvl, String tag, String appTag, String threadName, String msg, Throwable tr) { this.now = System.currentTimeMillis(); this.level = lvl; this.tag = tag; this.appTag = appTag; this.threadName = threadName; this.msg = msg; this.cause = tr; if (msg == null) { Log.e(TAG, "No message"); } } private void addCsvHeader(final StringBuilder csv) { if (dateFormat==null) dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.getDefault()); if (date==null) { if (null==mDate) mDate = new Date(); mDate.setTime(now); date = dateFormat.format(mDate); } csv.append(date); csv.append(','); csv.append(level); csv.append(','); csv.append(android.os.Process.myPid()); csv.append(','); if (threadName!=null) csv.append(threadName); csv.append(','); if (appTag!=null) csv.append(appTag); csv.append(','); if (tag!=null) csv.append(tag); csv.append(','); } private void addException(final StringBuilder csv, Throwable tr) { if (tr==null) return; final StringBuilder sb = new StringBuilder(256); sb.append(tr.getClass()); sb.append(": "); sb.append(tr.getMessage()); sb.append('\n'); for (StackTraceElement trace : tr.getStackTrace()) { //addCsvHeader(csv); sb.append(" at "); sb.append(trace.getClassName()); sb.append('.'); sb.append(trace.getMethodName()); sb.append('('); sb.append(trace.getFileName()); sb.append(':'); sb.append(trace.getLineNumber()); sb.append(')'); sb.append('\n'); } addException(sb, tr.getCause()); csv.append(sb.toString().replace(';', '-').replace(',', '-').replace('"', '\'')); } public CharSequence formatCsv() { final StringBuilder csv = new StringBuilder(256); addCsvHeader(csv); csv.append('"'); if (msg != null) csv.append(msg.replace(';', '-').replace(',', '-').replace('"', '\'')); csv.append('"'); csv.append('\n'); if (cause!=null) { addCsvHeader(csv); csv.append('"'); addException(csv, cause); csv.append('"'); csv.append('\n'); } return csv; } } private String getApplicationLocalTag() { if (applicationTag == null) applicationTag = getApplicationTag(); return applicationTag; } /** * remove all the current log entries and start from scratch */ public void clear() { mSaveStoreHandler.sendEmptyMessage(MSG_CLEAR); } /** * Collects the logs. Make sure finalPath have been set before. * * @param context * @param listener */ public void collectlogs(Context context, LogCollecting listener) { if (mCurrentLogFile == null) { listener.onLogCollectingError("Log file is invalid."); } else if (finalPath==null) { listener.onLogCollectingError("Final path have not been set"); } else { Message msg = Message.obtain(mSaveStoreHandler, MSG_COLLECT, listener); mSaveStoreHandler.sendMessage(msg); } } private void mergeFile(File otherFile, File finalFile) { try { InputStream instream = new FileInputStream(otherFile); BufferedReader in = new BufferedReader(new InputStreamReader(instream)); OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(finalFile, true), "UTF-8"); String line; while ((line = in.readLine()) != null) { out.append(line); out.append('\n'); } in.close(); out.flush(); out.close(); } catch (FileNotFoundException e) { FLog.e(TAG, "FileNotFoundException: " + e.getMessage(), e); } catch (IOException e) { FLog.e(TAG, "IOException: " + e.getMessage(), e); } } /** * a special tag to be added to the logs * @return */ public String getApplicationTag() { return ""; } private void verifyFileSize() { if (mCurrentLogFile != null) { long size = mCurrentLogFile.length(); if (size > maxFileSize) { try { closeWriter(); } catch (IOException e) { FLog.e(TAG, "Can't use file : "+ mCurrentLogFile, e); } finally { if (mCurrentLogFile==file2) mCurrentLogFile = file1; else mCurrentLogFile = file2; mCurrentLogFile.delete(); openWriter(); } } } } private void openWriter() { if (writer==null) try { writer = new OutputStreamWriter(new FileOutputStream(mCurrentLogFile, true), "UTF-8"); } catch (UnsupportedEncodingException e) { Log.e(TAG, "can't get a writer for " +mCurrentLogFile+" : "+e.getMessage()); } catch (FileNotFoundException e) { Log.e(TAG, "can't get a writer for " +mCurrentLogFile+" : "+e.getMessage()); } } private void closeWriter() throws IOException { if (writer!=null) { writer.close(); writer = null; } } void setFinalPath(File finalPath) { this.finalPath = finalPath; } }