package tws.component.log.impl; import java.io.File; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import tws.component.log.TwsLog; import tws.component.log.TwsLogBaseConfig; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.text.TextUtils; /** * Title: TwsLogHandler * Package: tws.component.log.impl * Author: interzhang * Date: 14-3-17 下午3:58 * Version: v1.0 */ public class TwsLogHandler extends Handler { protected static final int LOG_MSG_LOG_WRITE = 1; protected static final int LOG_MSG_LOG_RESET = 2; protected static final int LOG_MSG_TRACE_WRITE = 3; protected static final int LOG_MSG_TRACE_RESET = 4; protected static final int LOG_MSG_TRACE_CLOSE = 5; protected static final int LOG_MSG_FILE_CLEAN = 6; private static final int FILE_TRACE_MAX_LENGTH = 1024 * 1024; private static final int FILE_LOG_MAX_LENGTH = 8 * 1024 * 1024; private static final long LOG_DAYMILLISECOND = 24 * 60 * 60 * 1000; // 一天的毫秒数 private Calendar mCalendar = null; private boolean mTraceWriterFlag = false; private FileWriter mTraceWriter = null; private File mTraceFile = null; private long mTraceLength = 0; private int mTraceFileDate = -1; private boolean mLogWriterFlag = false; private FileWriter mLogWriter = null; private File mLogFile = null; private long mLogLength = 0; private int mLogFileDate = -1; private String mProcessName = null; private TwsLogBaseConfig mConfig = null; public TwsLogHandler(Looper looper, TwsLogBaseConfig config) { super(looper); mConfig = config; mCalendar = Calendar.getInstance(); } private String getLogFileSubName(boolean isTrace) { if (mProcessName == null) { mProcessName = TwsLogUtils.getProcessName(); if (!TextUtils.isEmpty(mProcessName)) { mProcessName = mProcessName.replaceAll(":", "_"); } } if (TextUtils.isEmpty(mProcessName)) { mProcessName = "pid-" + android.os.Process.myPid(); } if (!isTrace) { Calendar cal = Calendar.getInstance(); return String.format("%s_%d%02d%02d", mProcessName, cal.get(Calendar.YEAR), (cal.get(Calendar.MONTH) + 1), cal.get(Calendar.DAY_OF_MONTH)); } else { return mProcessName; } } /** * 公共的关闭TraceWriter的关闭方法,清零状态 */ private void closeTraceWriter() { if (mTraceWriter != null) { try { mTraceWriter.close(); } catch (IOException e) { System.out.println(e.getMessage()); } mTraceWriter = null; } mTraceWriterFlag = false; mTraceLength = 0; mTraceFile = null; mTraceFileDate = -1; } private void closeLogWriter() { if (mLogWriter != null) { try { mLogWriter.close(); } catch (IOException e) { System.out.println(e.getMessage()); } mLogWriter = null; } mLogWriterFlag = false; mLogLength = 0; mLogFile = null; mLogFileDate = -1; } /** * 保存当前的trace log到重命名的文件中 * (在文件超过大小,或者准备开始上传前需要restore) */ private void restoreCurTraceLog() { if (mTraceWriter != null) { try { mTraceWriter.close(); } catch (IOException e) { System.out.println(e.getMessage()); } mTraceWriter = null; mTraceLength = 0; } if (mTraceFile != null) { renameTraceLog(mTraceFile); } mTraceWriterFlag = false; mTraceFile = null; mTraceFileDate = -1; } public ArrayList<File> prepareUploadTraceLog() { ArrayList<File> list = new ArrayList<File>(); File dir1 = TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), true); File dir2 = TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), false); File[] files = dir1.listFiles(); if (files != null && files.length > 0) { for (File f : files) { File out = renameTraceLog(f); if (out != null) { list.add(out); } } } files = dir2.listFiles(); if (files != null && files.length > 0) { for (File f : files) { File out = renameTraceLog(f); if (out != null) { list.add(out); } } } return list; } private File renameTraceLog(File src) { if (src.getName().startsWith("trace_") && src.getName().endsWith(".txt")) { Calendar cal = Calendar.getInstance(); String subName = String.format("%d%02d%02d%02d%02d%02d", cal.get(Calendar.YEAR), (cal.get(Calendar.MONTH) + 1), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)); String newPath = src.getAbsolutePath().replace(".txt", "_" + subName + ".log"); File newFile = new File(newPath); try { newFile.createNewFile(); src.renameTo(newFile); return newFile; } catch (Exception e) { src.delete(); if (newFile.exists()) { newFile.delete(); } return null; } } return null; } /** * 清空File的内容,重头开始写 */ private void resetCurFileLog() { if (mLogWriter != null) { try { mLogWriter.close(); } catch (IOException e) { System.out.println(e.getMessage()); } mLogWriter = null; } if (mLogFile != null) { final String name = mLogFile.getName().replace(".log", ""); File dir = TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), false); String[] files = dir.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return filename.contains(name); } }); int index = (files == null) ? 1 : files.length; mLogFile.renameTo(new File(mLogFile.getParent(), name+"(" + index +").log")); } mLogWriterFlag = false; mLogFile = null; } private void delExpiredLogFile(File dir, long expiredTime) { if (dir != null && dir.exists()) { File[] files = dir.listFiles(); if (files != null && files.length > 0) { for (File file : files) { if ((file.getName().startsWith("log") || file.getName().startsWith("crash")) && (System.currentTimeMillis() - file.lastModified() > expiredTime)) { file.delete(); } } } } } private void closeTraceLog(boolean quit) { removeMessages(LOG_MSG_TRACE_WRITE); removeMessages(LOG_MSG_TRACE_RESET); restoreCurTraceLog(); Thread thread = Thread.currentThread(); if (HandlerThread.class.isInstance(thread)) { ((HandlerThread) thread).quit(); } } @Override public void handleMessage(Message msg) { switch (msg.what) { case LOG_MSG_LOG_WRITE: // debug的文件日志 mCalendar.setTimeInMillis(System.currentTimeMillis()); try { if (!mLogWriterFlag || mLogFileDate != mCalendar.get(Calendar.DAY_OF_MONTH)) { File dir = TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), false); if (dir != null) { mLogFile = TwsLogUtils.createNewFile(dir.getAbsolutePath(), "log_" + getLogFileSubName(false) + ".log"); mLogFileDate = mCalendar.get(Calendar.DAY_OF_MONTH); if (mLogFile != null) { try { mLogWriter = new FileWriter(mLogFile, true); mLogWriterFlag = true; mLogLength = mLogFile.length(); } catch (IOException ex) { mLogWriter = null; mLogLength = 0; } } } if (mLogWriter == null) { // mLogWriter创建失败,延时再创建 sendEmptyMessageDelayed(LOG_MSG_LOG_RESET, 3 * 60000); return; } } if (mLogWriter != null) { try { mLogWriter.write((String) msg.obj); mLogWriter.flush(); mLogLength += ((String) msg.obj).length(); } catch (IOException e) { // mLogWriter发生IOException,重新创建 closeLogWriter(); } } if (mLogLength > FILE_LOG_MAX_LENGTH) { resetCurFileLog(); return; } } catch (Exception ex) { if (ex.getMessage() != null) TwsLog.w("TraceLog", ex.getMessage()); else TwsLog.w("TraceLog", "Write log catch a null exception!!!"); } break; case LOG_MSG_TRACE_WRITE: // 用户行为的trace日志 mCalendar.setTimeInMillis(System.currentTimeMillis()); if (!mTraceWriterFlag || mTraceFileDate != mCalendar.get(Calendar.DAY_OF_MONTH)) { File dir = TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), false); if (dir != null) { mTraceFile = TwsLogUtils.createNewFile(dir.getAbsolutePath(), "trace_" + getLogFileSubName(true) + ".txt"); if (mTraceFile != null) { mTraceFileDate = mCalendar.get(Calendar.DAY_OF_MONTH); try { mTraceWriter = new FileWriter(mTraceFile, true); mTraceWriterFlag = true; mTraceLength = mTraceFile.length(); } catch (IOException ex) { closeTraceWriter(); } } } if (mTraceWriter == null) { // mLogWriter创建失败,延时再创建 sendEmptyMessageDelayed(LOG_MSG_TRACE_RESET, 3 * 60000); return; } } if (mTraceWriter != null) { try { mTraceWriter.write((String) msg.obj); mTraceWriter.flush(); mTraceLength += ((String) msg.obj).length(); } catch (IOException e) { // mLogWriter发生IOException,重新创建 closeTraceWriter(); } } // 检查文件是否超过大小 if (mTraceLength > FILE_TRACE_MAX_LENGTH) { restoreCurTraceLog(); } break; case LOG_MSG_LOG_RESET: // 重新初始化mLogWriter closeLogWriter(); break; case LOG_MSG_TRACE_RESET: // 重新初始化mTraceWriter closeTraceWriter(); break; case LOG_MSG_TRACE_CLOSE: if (msg.obj == null) { closeTraceLog(false); } else { closeTraceLog(true); } break; case LOG_MSG_FILE_CLEAN: // 清理过期日志 delExpiredLogFile(TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), true), 3 * LOG_DAYMILLISECOND); delExpiredLogFile(TwsLogUtils.getLogFileDirectory(mConfig.getPackageName(), false), 3 * LOG_DAYMILLISECOND); delExpiredLogFile(TwsLogUtils.getCrashFileDirectory(mConfig.getPackageName()), 3 * LOG_DAYMILLISECOND); break; default: } } }