package com.bgy.autosale.utils; import android.content.Context; import android.os.Environment; import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; /** * Created by cjx on 2019/8/7 * 说明:保存日志到本地文件 */ public class PlcLog { private static PlcLog plcLog; private boolean open = true; private final ClickHelper checkFileHelper = new ClickHelper(1000 * 60 * 30);//半小时允许检查一次 private HashMap logPaths; private String defaultFile = ""; ArrayList retFilePaths = new ArrayList<>(); private Context appContext; private int count = 0; Disposable disposable; public static PlcLog getInstance() { if (plcLog == null) { synchronized (PlcLog.class) { if (plcLog == null) { plcLog = new PlcLog(); } } } return plcLog; } public void initLogFile(Context context) { initLogFile(context, "", null, new Date()); } public void initLogFile(Context context, String file) { initLogFile(context, file, null, new Date()); } public void initLogFile(Context context, String file, String dir) { initLogFile(context, file, dir, new Date()); } public void initLogFile(Context context, String file, String dir, Date newDate) { appContext = context.getApplicationContext(); defaultFile = file; SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd", Locale.CHINA); String fileName = sdf.format(newDate) + file; String logFilePath; synchronized (PlcLog.class) { if (dir == null) { dir = getDiskCacheDir(context) + "/log/"; } else { dir = dir + "/log/"; } mkDir(dir); logFilePath = dir + fileName + ".txt"; File logFile = new File(logFilePath); if (!logFile.exists()) { try { logFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } logPaths.remove(file); logPaths.put(file, logFilePath); System.out.println("初始化日志 " + logFilePath); File logFileParent = logFile.getParentFile(); retFilePaths.clear(); if (logFileParent.listFiles()!=null) { for (File log : logFileParent.listFiles()) { String name = log.getName(); if (name.length() > 2) { retFilePaths.add(log.getAbsolutePath()); } } } } //考虑到网络时间没校准的时候 checkAndDelLog(logFilePath); } private void checkAndDelLog(final String logFilePath) { if (disposable != null) {//销毁多余的延时 disposable.dispose(); disposable = null; } File logFile = (new File(logFilePath)).getParentFile(); long current = System.currentTimeMillis(); long checkTime = getEffectiveTime(); //这么做是为了确保一下时间是准的网络时间 if (current > checkTime) { if (logFile.listFiles()!=null) { for (File log : logFile.listFiles()) { String name = log.getName(); if (name.startsWith("PLC_")) { try { if (current - log.lastModified() > 2592000000L) { // 30天前的日志, 要删除 // if (current - log.lastModified() > 172800000) { // 2天前的日志, 要删除 e("CJX", "删除 " + name + " = " + log.delete()); } } catch (Exception e) { e.printStackTrace(); } } } } } else { disposable = Schedulers.io().scheduleDirect(new Runnable() { @Override public void run() { checkAndDelLog(logFilePath); } }, 30, TimeUnit.SECONDS); } } public void setLogSwitch(boolean open) { this.open = open; } private PlcLog() { logPaths = new HashMap<>(2); } public String getLogFilePath() { return getLogFilePath(""); } public String getLogFilePath(String file) { return logPaths.get(file); } private void internalSaveLog(String file, String tag, String msg) { if (!open) { return; } if (checkFileHelper.canClick() && appContext != null) { initLogFile(appContext); } String filePath = logPaths.get(file); if (TextUtils.isEmpty(filePath)) { filePath = logPaths.get(defaultFile); } if (!TextUtils.isEmpty(filePath)) { try { // 2021-09-26 15:05:39.714 6218-6251/ FileOutputStream fileOutputStream = new FileOutputStream(filePath, true); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.CHINA); byte[] bytes = (sdf.format(new Date()) + " " + android.os.Process.myPid() + "-" + android.os.Process.myTid() + "/" + tag + ":" + msg + "\n").getBytes(); fileOutputStream.write(bytes); fileOutputStream.flush(); if (count++ > 8) { //将数据同步到达物理存储设备 try { FileDescriptor fd = fileOutputStream.getFD(); fd.sync(); } catch (Exception e) { } count = 0; } fileOutputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } e(tag, msg); } public void ui(String tag, String msg) { ui("", tag, msg); } public void ui(String file, String tag, String msg) { e_s(file, tag, msg); //也要保持 } /** * 打印日志并保存到文件,需要先调用{@link #initLogFile(Context)}初始化日志路径才生效 * * @param tag 日志标签 * @param msg 日志内容 */ public void e_s(String tag, String msg) { e_s("", tag, msg); } public void e_s(String file, String tag, String msg) { if (msg.length() > 4000) { for (int i = 0; i < msg.length(); i += 4000) { if (i + 4000 < msg.length()) internalSaveLog(file, tag, msg.substring(i, i + 4000)); else internalSaveLog(file, tag, msg.substring(i, msg.length())); } } else { //直接打印 internalSaveLog(file, tag, msg); } } /** * 打印日志并保存到文件,需要先调用{@link #initLogFile(Context)}初始化日志路径才生效 * * @param tag 日志标签 * @param msg 日志内容 */ public void d_s(String tag, String msg) { d_s("", tag, msg); } public void d_s(String file, String tag, String msg) { //直接打印 e_s(file, tag, msg); } public void e(String tag, String msg) { Log.e(tag, msg); } public void d(String tag, String msg) { if (!open) { return; } Log.d(tag, msg); } /** * 获取程序缓存路径 */ public static String getDiskCacheDir(Context context) { boolean hasExternal = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); File cacheDir = hasExternal ? context.getExternalCacheDir() : context.getCacheDir(); return cacheDir.getAbsolutePath(); } /** * 创建当前目录的文件夹 */ private void mkDir(String filePath) { File file = new File(filePath); if (!file.exists()) { file.mkdirs(); } } /** * 删除当前日志 */ public void delectLogFile() { delectLogFile(""); } public void delectLogFile(String file) { String logFilePath = logPaths.get(file); if (logFilePath != null) { synchronized (PlcLog.class) { File f = new File(logFilePath); f.delete(); } } } public String e_s(String tag, Exception ex) { try { StringBuilder stringBuilder = new StringBuilder(); StackTraceElement[] stackTraceElements = ex.getStackTrace(); for (StackTraceElement element : stackTraceElements) { stringBuilder.append("\n" + element.toString()); } e_s(tag, stringBuilder.toString()); return stringBuilder.toString(); } catch (Exception e) { } return ""; } public String e_s(String tag, Throwable ex) { try { StringBuilder stringBuilder = new StringBuilder(); StackTraceElement[] stackTraceElements = ex.getStackTrace(); for (StackTraceElement element : stackTraceElements) { stringBuilder.append("\n" + element.toString()); } e_s(tag, stringBuilder.toString()); return stringBuilder.toString(); } catch (Exception e) { } return ""; } public void printCallerStracks() { e_s("Stack", getCallers()); } public static String getCallers() { final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); StringBuilder stringBuilder = new StringBuilder(); try { for (int i = 4; i < 10; i++) { stringBuilder.append("\n" + "class:" + callStack[i].getClassName() + " line:" + callStack[i].getLineNumber() + ":" + callStack[i].getMethodName()); } } catch (Exception e) { } return stringBuilder.toString(); } public static long getEffectiveTime() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");// HH:mm:ss simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); try { return simpleDateFormat.parse("2021-01-01").getTime(); } catch (ParseException e) { e.printStackTrace(); } return 0; } public ArrayList getLogFilePaths() { return retFilePaths; } class ClickHelper { private long currentTime; private int timeOut; private volatile boolean lock = false; public ClickHelper(int timeOut) { this.timeOut = timeOut; } public boolean canClick() { if (lock) { currentTime = SystemClock.elapsedRealtime(); //直接返回 false return false; } long time = SystemClock.elapsedRealtime(); if ((time - currentTime) >= timeOut) { currentTime = time; return true; } return false; } public void setClickLock(boolean lock) { this.lock = lock; } } }