Android實現輸出到Logcat的Log日誌,能夠定位到代碼行數,可跳轉至對應的代碼行,並且可以設置日誌緩存

      實現輸出到Logcat的Log日誌,能夠定位到代碼行數,可跳轉至對應的代碼行,並且可以設置日誌緩存;

      比較簡單的實現方式,少囉嗦了:

package com.icbc.utils;

import android.os.Environment;
import android.util.Log;
import com.icbc.emallmobile.BuildConfig;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author windfallsheng
 * <p>
 * 輸出日誌,且定位代碼行數,可緩存日誌到本地文件;
 */
public class Logger {

    private final static String TAG = "Logger";
    private static boolean isDebug = BuildConfig.DEBUG ? true : false;
    private static boolean sCacheable  = true;

    /**
     * 設置一個總開關,決定是否緩存日誌,前提條件是{@link this#isDebug}爲true;
     * <p>
     * 如果{@link this#sCacheable}爲false,即使用戶調用了緩存日誌的方法,緩存也不生效;
     * <p>
     * 如果{@link this#sCacheable}爲true,那麼是否緩存日誌,取決於是否調用了緩存日誌的方法;
     *
     * @param cacheable
     */
    public static void initCacheSwitch( boolean cacheable){
        sCacheable = cacheable;
    }

    /**
     * 輸出當前日誌,並且定位代碼行數;
     *
     * @param content
     */
    public static void w(String content) {
        if (isDebug) {
            log(TAG, content, Log.WARN, false);
        }
    }

    public static void w(String tag, String content) {
        if (isDebug) {
            log(tag, content, Log.WARN, false);
        }
    }

     /**
     * 輸出當前日誌,定位代碼行數,並且將日誌緩存至本地;
     *
     * @param content
     */
    public static void wc(String content) {
        if (isDebug) {
            log(TAG, content, Log.WARN, true);
        }
    }
   
    public static void wc(String tag, String content) {
        if (isDebug) {
            log(tag, content, Log.WARN, true);
        }
    }

    public static void e(String content) {
        if (isDebug) {
            log(TAG, content, Log.ERROR, false);
        }
    }

    public static void e(String tag, String content) {
        if (isDebug) {
            log(tag, content, Log.ERROR, false);
        }
    }

    public static void ec(String tag, String content) {
        if (isDebug) {
            log(tag, content, Log.ERROR, true);
        }
    }

    /**
     * 輸出方法調用棧鏈的日誌,並且定位代碼行數;
     *
     * @param content
     */
    public static void wl(String tag, String content) {
        if (isDebug) {
            logLinked(tag, content, Log.WARN, false);
        }
    }

    public static void wl(String content) {
        if (isDebug) {
            logLinked(TAG, content, Log.WARN, false);
        }
    }

    public static void el(String tag, String content) {
        if (isDebug) {
            logLinked(tag, content, Log.ERROR, false);
        }
    }

    public static void el(String content) {
        if (isDebug) {
            logLinked(TAG, content, Log.ERROR, false);
        }
    }

    /**
     * 輸出方法調用棧鏈的日誌,定位代碼行數,並且將日誌緩存到本地;
     *
     * @param content
     */
    public static void wlc(String tag, String content) {
        if (isDebug) {
            logLinked(tag, content, Log.WARN, true);
        }
    }

    public static void wlc(String content) {
        if (isDebug) {
            logLinked(TAG, content, Log.WARN, true);
        }
    }

    public static void elc(String tag, String content) {
        if (isDebug) {
            logLinked(tag, content, Log.ERROR, true);
        }
    }

    public static void elc(String content) {
        if (isDebug) {
            logLinked(TAG, content, Log.ERROR, true);
        }
    }

    private static void log(String tag, String content, int level, boolean cacheable) {
        logAndCache(tag, content, level, false, cacheable);
    }

    private static void logLinked(String tag, String content, int level, boolean cacheable) {
        logAndCache(tag, content, level, true, cacheable);
    }

    private static void logAndCache(String tag, String content, int level, boolean isLinked, boolean cacheable) {
//        logTraceInfo(tag, content, level, isLinked);
        String info = logTraceInfo(tag, content, level, isLinked);
        if (sCacheable && cacheable) {
            cacheLogInfo(info);
        }
    }

    /**
     * 獲取相關調用棧的信息,並且打印相關日誌及代碼行數;
     * <p>
     * <p>
     * 相關調用棧的信息,按照:類名,方法名,行號等,這樣的格式拼接,可以用來定位代碼行,
     * 如:
     * at cn.xx.ui.MainActivity.onCreate(MainActivity.java:23) 定位代碼行;
     *
     * @param tag
     * @param content
     * @param level    日誌級別;
     * @param isLinked 是否輸出所有相關調用棧的信息;
     */
    private static String logTraceInfo(String tag, String content, int level, boolean isLinked) {
        StackTraceElement[] stes = Thread.currentThread().getStackTrace();
        if (stes == null) {
            Log.w(tag, "logTraceLinkInfo#return#stes == null");
            return "";
        }
        StringBuilder result = null;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < stes.length; i++) {
            StackTraceElement ste = stes[i];
            if (ignorable(ste)) {
                continue;
            }
            sb.append(content).append("[Thread:")
                    .append(Thread.currentThread().getName())
                    .append(", at ").append(ste.getClassName())
                    .append(".").append(ste.getMethodName())
                    .append("(").append(ste.getFileName())
                    .append(":").append(ste.getLineNumber())
                    .append(")]");
            String info = sb.toString();
            if (isLinked) {
                if (result == null) {
                    result = new StringBuilder();
                }
                result.append(info).append("\n");
                sb.delete(0, info.length());
                logInfo(tag, level, info);
            } else {
                logInfo(tag, level, info);
                return info;
            }
        }
        return result.toString();
    }

    private static boolean ignorable(StackTraceElement ste) {
        if (ste.isNativeMethod() ||
                ste.getClassName().equals(Thread.class.getName()) ||
                ste.getClassName().equals(Logger.class.getName())) {
            return true;
        }
        return false;
    }

    private static void logInfo(String tag, int level, String info) {
        switch (level) {
            case Log.ERROR:
                Log.e(tag, info);
                break;
            case Log.INFO:
                Log.i(tag, info);
                break;
            case Log.VERBOSE:
                Log.v(tag, info);
                break;
            case Log.WARN:
                Log.w(tag, info);
                break;
            case Log.DEBUG:
            default:
                Log.d(tag, info);
                break;
        }
    }

    private static void cacheLogInfo(String info) {
        SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSS");
        String savePath = Environment.getExternalStorageDirectory().toString() + File.separator + "ForLog";
        File saveFile = new File(savePath);
        if (!saveFile.exists()) {
            saveFile.mkdir();
        }
        File file = new File(saveFile, "log.txt");
        info = dataFormat.format(new Date()) + ":" + info + "\n\n";
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file, true);
            byte[] bytes = info.getBytes();
            fos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

由於作者水平有限,語言描述及代碼實現中難免有紕漏,望各位看官多提寶貴意見!

Hello , World !

感謝所有!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章