android Log日志管理类,日志保存到文件中去

1.首先需要一个ApplicationProxy的代理类,详情见:

博文地址

2.Log日志管理类

包括保存LOG和多进程分开写日志文件,避免产生脏数据(目前最多支持一个APP有一到两个进程的log管理,有多个进程还得修改), 但是写到文本中的顺序不一定对,因为是多线程去写,不知道那个日志先写到文件中去。 使用AS的deviceFileExplorer工具可查看"data/包名/files/huang_log/",这里保存的是主进程"data/包名/files//huang_push_log/",这里保存的是另一个进程

3.直接上代码了,拿来即可用。

package com.example.huangrenqian833.testapplication.log;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;

import com.example.huangrenqian833.testapplication.proxy.ApplicationProxy;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * Log日志管理类
 * 包括保存LOG和多进程分开写日志文件,避免产生脏数据(目前最多支持一个APP有一到两个进程的log管理,有多个进程还得修改),
 * 但是写到文本中的顺序不一定对,因为是多线程去写,不知道那个日志先写到文件中去。
 * 使用AS的deviceFileExplorer工具可查看"data/包名/files/huang_log/",这里保存的是主进程
 * "data/包名/files//huang_push_log/",这里保存的是另一个进程
 */
public class HuangLog {
    private static boolean ENABLE_LOG = true;

    public static final int LEVEL_V = 5;
    public static final int LEVEL_I = 4;
    public static final int LEVEL_D = 3;
    public static final int LEVEL_W = 2;
    public static final int LEVEL_E = 1;
    public static final int LEVEL_NO_LOG = 0;

    public static int DEBUG_LEVEL = LEVEL_V;

    private static String LOG_PREFIX = "HuangLog";

    private static long SAVE_LOG_INTERVAL = 15 * 24 * 60 * 60 * 1000; //本地日志文件过期时间15天

    private static long SAVE_LOG_MAX_SIZE = 500 * 1024 * 1024; //本地日志文件最大文件大小500M

    private static final String ZIP_KEY = "huang_zip_key";

    /**
     * 设置是否是输出日志
     *
     * @param enable
     */
    public static void setEnableLog(boolean enable) {
        ENABLE_LOG = enable;
        Log.i("HuangLog", "ENABLE_LOG: " + ENABLE_LOG);
    }

    /**
     * 判断当前是否允许日志输出
     */
    public static boolean isEnableLog() {
        return ENABLE_LOG;
    }

    /**
     * 设置日志显示级别
     *
     * @param level LEVEL_V、LEVEL_I、LEVEL_D、LEVEL_W、LEVEL_E、LEVEL_NO_LOG
     */
    public static void setDebugLevel(int level) {
        DEBUG_LEVEL = level;
    }

    //********************************************************
    public static void e(String msg) {
        e(null, msg);
    }

    public static void e(String tag, String msg) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_E) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String eTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String eMsg = getClassMsg().append(msg).toString();
        Log.e(eTag, eMsg);
        saveToSDCard(eMsg, LEVEL_E, SAVE_LOG_INTERVAL);
    }

    public static void e(String tag, String msg, Throwable e) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_E) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String eTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String eMsg = getClassMsg().append(msg).toString();
        Log.e(eTag, eMsg, e);
        saveToSDCard(eMsg, LEVEL_E, SAVE_LOG_INTERVAL);

    }

    //********************************************************
    public static void w(String msg) {
        w(null, msg);
    }

    public static void w(String tag, String msg) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_W) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String wTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String wMsg = getClassMsg().append(msg).toString();
        Log.w(wTag, wMsg);
        saveToSDCard(wMsg, LEVEL_W, SAVE_LOG_INTERVAL);

    }

    //********************************************************
    public static void d(String msg) {
        d(null, msg);
    }

    public static void d(String tag, String msg) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_D) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String dTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String dMsg = getClassMsg().append(msg).toString();
        Log.d(dTag, dMsg);
        saveToSDCard(dMsg, LEVEL_D, SAVE_LOG_INTERVAL);

    }

    //********************************************************
    public static void i(String msg) {
        i(null, msg);
    }

    public static void i(String tag, String msg) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_I) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String iTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String iMsg = getClassMsg().append(msg).toString();
        Log.i(iTag, iMsg);
        saveToSDCard(iMsg, LEVEL_I, SAVE_LOG_INTERVAL);

    }

    //********************************************************
    public static void v(String msg) {
        v(null, msg);
    }

    public static void v(String tag, String msg) {
        if (!ENABLE_LOG) {
            return;
        }
        if (DEBUG_LEVEL < LEVEL_V) {
            return;
        }
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        String vTag = TextUtils.isEmpty(tag) ? LOG_PREFIX : LOG_PREFIX + ":" + tag;
        String vMsg = getClassMsg().append(msg).toString();
        Log.v(vTag, vMsg);
        saveToSDCard(vMsg, LEVEL_V, SAVE_LOG_INTERVAL);

    }

    /**
     * 获取指定异常的全部堆栈信息
     *
     * @param ex
     * @return
     */
    public static String getExceptionAllInfo(Exception ex) {
        if (ex == null) {
            return "";
        }
        String sOut = "" + ex.toString() + "\n";
        StackTraceElement[] trace = ex.getStackTrace();
        for (StackTraceElement s : trace) {
            sOut += "\tat " + s + "\r\n";
        }
        return sOut;
    }

    /**
     * 获取打印日志的详细信息
     *
     * @return
     */
    private static StringBuilder getClassMsg() {
        StackTraceElement caller = new Throwable().fillInStackTrace().getStackTrace()[2];
        return new StringBuilder().append("[")
                .append(caller.getFileName())
                .append(" ")
                .append(caller.getMethodName())
                .append("() :: line ")
                .append(caller.getLineNumber())
                .append("] ");
    }

    /**
     * 保存log到文件中
     *
     * @param logStr       要保存的内容
     * @param level        要保存的内容等级  1[error]>2[warn]>3[debug]>4[info]>5[verbose]
     * @param intervalTime 文件保存内容的间隔时间(毫秒级)
     */
    private static void saveToSDCard(final String logStr, final int level, final long intervalTime) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                saveToSDCard_2(logStr, level, intervalTime);
            }
        }).start();// TODO 这里要放线程池管理,不然开的线程过多会OOM
    }

    /**
     * 保存log到文件中
     *
     * @param logStr       要保存的内容
     * @param level        要保存的内容等级  1[error]>2[warn]>3[debug]>4[info]>5[verbose]
     * @param intervalTime 文件保存内容的间隔时间(毫秒级)
     */
    private synchronized static void saveToSDCard_2(final String logStr, final int level, final long intervalTime) {
        StringBuilder sb = new StringBuilder();
        Context mContext = ApplicationProxy.getInstance().getApplication();
        boolean isOpenSave = true;// 默认保存日志

        String levelStr = "v";// 默认是 v 等级


        if (!isOpenSave) {
            return;
        }

        int spLevel = 5;
        if ("i".equals(levelStr)) {
            spLevel = 4;
        } else if ("d".equals(levelStr)) {
            spLevel = 3;
        } else if ("w".equals(levelStr)) {
            spLevel = 2;
        } else if ("e".equals(levelStr)) {
            spLevel = 1;
        }

        if (level > spLevel) {
            return;
        }

        try {
            PackageInfo pkInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), PackageManager.GET_CONFIGURATIONS);
            sb.append("[applicationID").append(" = ").append(mContext.getPackageName());
            sb.append(",versionName").append(" = ").append(pkInfo.versionName);
            sb.append(",versionCode").append(" = ").append(pkInfo.versionCode).append("]");
            sb.append(format(System.currentTimeMillis(), YYYY_MM_DD2_HH_MM_SS)).append(logStr);
        } catch (PackageManager.NameNotFoundException e1) {
            e1.printStackTrace();
        }

        String PATH;
        if (isCurrentAppProcess(ApplicationProxy.getInstance().getApplicationContext())) {
            PATH = "/huang_log/";// 主进程
        } else {
            PATH = "/huang_push_log/";// push进程
        }
        String path = mContext.getFilesDir() + PATH;//文件夹名
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();//创建文件夹
        }

        //log_创建文件的时间_保存多少天的时间.log
        //1.首先从文件夹遍历文件
        File[] files = dir.listFiles();
        String time = format(new Date(), YYYY_MM_DD_HH_MM_SS_SSS);
        String fileName = mContext.getFilesDir().getPath() + PATH + "/log_" + time + "_" + intervalTime + ".log";
        if (files == null || files.length <= 0) {//2.1空文件夹
            //2.1.1那就创建文件
            writeLog(fileName, sb);
        } else {//2.2非空文件夹
            List nameList = new ArrayList<>();
            List fileList = Arrays.asList(files);
            getSortList(fileList);//降序排列这样的话log保存到最新的log文件中
            for (File f : fileList) {//2.2.1将已有的文件名全部放置列表中
                if (f.length() <= SAVE_LOG_MAX_SIZE) {
                    nameList.add(f.getName());
                } else {//超过最大的大小(先不删除,感觉删除最好还是超过期限为主)
//                         deleteFile(f.getAbsolutePath());
                }
            }


            //2.2.2,文件名牵涉到三个属性:1.日志文件的创建时间,2.文件保存的日志间隔时间
            List createTimeList = new ArrayList<>();
            List intervalTimeList = new ArrayList<>();

            //log_2018-10-29-17:00:12.123_86400000.log
            for (String s : nameList) {
                if (!TextUtils.isEmpty(s)
                        && s.contains("_")) {
                    String[] nameArr = s.split("_");
                    if (nameArr != null && nameArr.length > 0) {
                        // TODO: 2018/10/29 这里不去判断长度了,信任文件名,不然没得玩
                        createTimeList.add(getTime(nameArr[1], YYYY_MM_DD_HH_MM_SS_SSS));
                        intervalTimeList.add(Long.parseLong(nameArr[2].replace(".log", "")));
                    }
                }
            }
            //2.3.1先判断时间
            List indexList = new ArrayList<>();
            for (int i = 0; i < createTimeList.size(); i++) {
                if ((System.currentTimeMillis() - createTimeList.get(i)) < intervalTime) {
                    indexList.add(i);
                } else {
                    //超过最长保存文件时间
                    deleteFile(mContext.getFilesDir().getPath() + PATH + "/" + nameList.get(i));
                }
            }

            if (indexList != null && indexList.size() > 0) {//在时间范围内的
                //判断文件是否存在
                File saveFile = new File(mContext.getFilesDir().getPath() + PATH + "/" + nameList.get(indexList.get(0)));
                if (!saveFile.exists()) {
                    writeLog(fileName, sb);
                } else {
                    writeLog(saveFile.getAbsolutePath(), sb);
                }

            } else {//不在时间范围内的直接创建log
                writeLog(fileName, sb);
            }
        }
    }

    /**
     * 降序
     *
     * @param list
     * @return
     */
    private static List getSortList(List list) {
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(File o1, File o2) {
                if (o1.lastModified() < o2.lastModified()) {
                    return 1;
                }
                if (o1.lastModified() == o2.lastModified()) {
                    return 0;
                }
                return -1;
            }
        });
        return list;
    }

    /**
     * 写log
     *
     * @param fileName
     * @param sb
     */
    private static void writeLog(String fileName, StringBuilder sb) {
        File file = new File(fileName);
        RandomAccessFile raf = null;
        try {
            if (!file.exists()) {
                boolean isSuccess = file.createNewFile();
                if (!isSuccess) {
                    return;
                }
            }
            raf = new RandomAccessFile(file, "rw");
            raf.seek(file.length());
            raf.write(sb.toString().getBytes());
            raf.write("\n".getBytes());

            String PATH_ZIP;
            if (isCurrentAppProcess(ApplicationProxy.getInstance().getApplicationContext())) {
                PATH_ZIP = "/huang_log_zip/";// 主进程
            } else {
                PATH_ZIP = "/huang_push_log_zip/";// push进程
            }

//            String dest = ApplicationProxy.getInstance().getApplication().getFilesDir().getPath() + PATH_ZIP;
//            CompressOperateUtil.zip(fileName, dest, true, ZIP_KEY); 不再做压缩,故注掉

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileName
     */
    private static void deleteFile(String fileName) {
        // TODO: 2018/11/20 这里只删除了源文件,压缩文件没有删除 
        File file = new File(fileName);
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 判断是否为当前的app进程
     *
     * @return 是否为当前的app进程
     */
    public static boolean isCurrentAppProcess(Context context) {
        int pid = android.os.Process.myPid();
        ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if (mActivityManager != null && mActivityManager.getRunningAppProcesses() != null) {
            for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
                if (appProcess.pid == pid && context.getPackageName().equals(appProcess.processName)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static final String YYYY_MM_DD2_HH_MM_SS = "yyyy年MM月dd日 HH:mm:ss";

    private static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd-HH:mm:ss.SSS";

    /**
     * 格式化日期时间
     *
     * @param milliseconds 毫秒数
     * @param format       format
     * @return String
     */
    private static String format(long milliseconds, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINESE);
        return sdf.format(new Date(milliseconds));
    }

    /**
     * 格式化日期时间
     *
     * @param date   date
     * @param format format
     * @return String
     */
    private static String format(Date date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINESE);
        return sdf.format(date);
    }

    /**
     * 转换时间日期
     *
     * @param time 时间字符串
     * @return long
     */
    private static long getTime(String time, String pattern) {
        DateFormat dateFormat = new SimpleDateFormat(pattern);
        try {
            return dateFormat.parse(time).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return -1;
    }
}

 

你可能感兴趣的:(android)