com.lazy.gank.logging.Logcat.java Source code

Java tutorial

Introduction

Here is the source code for com.lazy.gank.logging.Logcat.java

Source

package com.lazy.gank.logging;

import android.content.Context;
import android.os.Environment;
import android.os.Process;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Copyright  2016
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * <ol>
 * <li>Log ->?   {@link Logcat#v(Object, String...)}</li>
 * <li> Log->  {@link Logcat#vv(Object, String...)}</li>
 * <li>Log ->?, Log->  {@link Logcat#vvv(Object, String...)}</li>
 * <li> Log->,Log ??  {@link Logcat#fv(Object, String, String...)}</li>
 * <li>Log ->?,Log->,Log ??  {@link Logcat#fvv(Object, String, String...)}</li>
 * </ol>
 * 1. Android Log 
 * <p/>
 * 2.Log ,?Log ,Log ??
 * <p/>
 * 3.Log
 * <p/>
 *
 * @author Lazy
 */
//@SuppressWarnings("ALL")
public final class Logcat {
    //1  Tag
    public static final String TAG = "Logcat";

    // (??)
    public static final char SHOW_VERBOSE_LOG = 0x01;
    public static final char SHOW_DEBUG_LOG = 0x01 << 1;
    public static final char SHOW_INFO_LOG = 0x01 << 2;
    public static final char SHOW_WARN_LOG = 0x01 << 3;
    public static final char SHOW_ERROR_LOG = 0x01 << 4;
    //json ??
    private static final int SHOW_JSON_LOG = 0x01 << 5;
    //??
    public static final char OPERATION_BIT = 0; //? ?Log
    public static final char NOT_SHOW_LOG = 0; //? Log

    //
    public static final String V = "V/";
    public static final String D = "D/";
    public static final String I = "I/";
    public static final String W = "W/";
    public static final String E = "E/";
    public static final String JSON = "JSON/";

    //Tag ?
    public static final String TAG_SEPARATOR = "->";
    public static final String DEFAULT_LOG_DIR = "logs";

    //    // ?? LogCat 
    private static char m_cLogCatShowLogType = SHOW_VERBOSE_LOG | SHOW_DEBUG_LOG | SHOW_INFO_LOG | SHOW_WARN_LOG
            | SHOW_ERROR_LOG | SHOW_JSON_LOG;

    // ??  ?

    // ????
    private static char m_cFileSaveLogType = SHOW_VERBOSE_LOG | SHOW_DEBUG_LOG | SHOW_INFO_LOG | SHOW_WARN_LOG
            | SHOW_ERROR_LOG;

    // 
    public static String sLogFolderPath = "";
    //Application Context 
    private static Context mContext;
    //?
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    // separator
    private static final String FILE_SEPARATOR = File.separator;

    private static final int JSON_INDENT = 3;
    private static final String LOGFILE_SUFFIX = ".log";

    //?
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
    //Log ??
    private static SimpleDateFormat fileSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    //?   anr
    private static ExecutorService mSingleExecutors = Executors.newSingleThreadExecutor();
    public static final int INDEX = 5;

    @IntDef({ SHOW_VERBOSE_LOG, SHOW_DEBUG_LOG, SHOW_INFO_LOG, SHOW_WARN_LOG, SHOW_ERROR_LOG, SHOW_JSON_LOG,
            NOT_SHOW_LOG })
    @Retention(RetentionPolicy.SOURCE)
    private @interface LockLevel {
    }

    private Logcat() {
        throw new UnsupportedOperationException();
    }

    /**
     * @param context
     */
    public static void initialize(@NonNull Context context) {
        mContext = context.getApplicationContext();
        initialize(context, defaultConfig());
    }

    /**
     * @param context
     * @param config
     */
    public static void initialize(@NonNull Context context, @NonNull Config config) {
        mContext = context.getApplicationContext();
        if (config.logSavePath == null || config.logSavePath.trim().equals("")) {
            defaultConfig();
        } else {
            checkSaveLogPath(config.logSavePath);
        }
        if (config.logCatLogLevel != null) {
            m_cLogCatShowLogType = config.logCatLogLevel;
        }
        if (config.fileLogLevel != null) {
            m_cFileSaveLogType = config.fileLogLevel;
            if (m_cFileSaveLogType == NOT_SHOW_LOG)
                mSingleExecutors = null; //Recycle
        }

    }

    public static Config defaultConfig() {
        Builder builder = newBuilder();

        // ???
        do {
            String state = Environment.getExternalStorageState();
            //  SD ?
            if (!Environment.MEDIA_MOUNTED.equals(state)) {
                Log.w(TAG, "Not mount SD card!");
                break;
            }

            // SD ???
            if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                Log.w(TAG, "Not allow write SD card!");
                break;
            }

            File externalCacheDir = mContext.getExternalCacheDir();
            // context.getExternalCacheDir() maybe null
            if (externalCacheDir != null) {
                builder.logSavePath = externalCacheDir.getAbsolutePath() + FILE_SEPARATOR + DEFAULT_LOG_DIR;
            } else {
                Log.e(TAG, "externalCacheDir is null!");
                builder.fileLogLevel(OPERATION_BIT);
                break;
            }

            // ? SD ?????
            // ???? ? SD ? Cache/Log 
            String strSaveLogPath = builder.logSavePath;

            checkSaveLogPath(strSaveLogPath);
        } while (false);

        Config config = new Config(builder);
        return config;
    }

    private static void checkSaveLogPath(@NonNull String strSaveLogPath) {
        if (sLogFolderPath.trim().equals("")) {
            File fileSaveLogFolderPath = new File(strSaveLogPath);
            // ???
            if (!fileSaveLogFolderPath.exists()) {
                boolean mkdirs = fileSaveLogFolderPath.mkdirs();
                if (mkdirs) {
                    Log.i(TAG, "Create log folder success!");
                } else {
                    Log.i(TAG, "Create log folder failed!");
                }
            }

            // ????
            sLogFolderPath = strSaveLogPath;
        }
    }

    //1.?Log

    /**
     * @param msg
     * @param tag
     */
    public static void v(Object msg, String... tag) {
        consoleLog(SHOW_VERBOSE_LOG, msg, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void d(Object msg, String... tag) {
        consoleLog(SHOW_DEBUG_LOG, msg, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void i(Object msg, String... tag) {
        consoleLog(SHOW_INFO_LOG, msg, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void w(Object msg, String... tag) {
        consoleLog(SHOW_WARN_LOG, msg, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void e(Object msg, String... tag) {
        consoleLog(SHOW_ERROR_LOG, msg, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void json(String msg, String... tag) {
        consoleLog(SHOW_JSON_LOG, msg, tag);
    }

    //2.Log

    /**
     * @param msg
     * @param tag
     */
    public static void vv(final Object msg, final String... tag) {
        writeLog(SHOW_VERBOSE_LOG, msg, null, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void dd(final Object msg, final String... tag) {
        writeLog(SHOW_DEBUG_LOG, msg, null, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void ii(final Object msg, final String... tag) {
        writeLog(SHOW_INFO_LOG, msg, null, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void ww(final Object msg, final String... tag) {
        writeLog(SHOW_WARN_LOG, msg, null, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void ee(final Object msg, final String... tag) {
        writeLog(SHOW_ERROR_LOG, msg, null, tag);
    }

    /**
     * @param msg
     * @param tag
     */
    public static void fjson(final String msg, final String... tag) {
        writeLog(SHOW_JSON_LOG, msg, null, tag);
    }

    //3.? + Log

    /**
     * 3.? + Log
     *
     * @param msg
     * @param tag
     */
    public static void vvv(final Object msg, final String... tag) {
        //?
        consoleLog(SHOW_VERBOSE_LOG, msg, tag);
        //Log
        writeLog(SHOW_VERBOSE_LOG, msg, null, tag);
    }

    public static void ddd(final Object msg, final String... tag) {
        consoleLog(SHOW_DEBUG_LOG, msg, tag);

        writeLog(SHOW_DEBUG_LOG, msg, null, tag);
    }

    public static void iii(final Object msg, final String... tag) {
        consoleLog(SHOW_INFO_LOG, msg, tag);

        writeLog(SHOW_INFO_LOG, msg, null, tag);
    }

    public static void www(final Object msg, final String... tag) {
        consoleLog(SHOW_WARN_LOG, msg, tag);

        writeLog(SHOW_WARN_LOG, msg, null, tag);
    }

    public static void eee(final Object msg, final String... tag) {
        consoleLog(SHOW_ERROR_LOG, msg, tag);
        writeLog(SHOW_ERROR_LOG, msg, null, tag);
    }

    /**
     * 3.? + Log
     *
     * @param msg
     * @param tag
     */
    public static void cfjson(final Object msg, final String... tag) {
        //?log
        consoleLog(SHOW_JSON_LOG, msg, tag);
        //Log
        writeLog(SHOW_JSON_LOG, msg, null, tag);
    }

    //4.Log??,Log 

    /**
     * 4.Log??,Log 
     *
     * @param msg
     * @param logFileName
     * @param tag
     */
    public static void fv(final Object msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_VERBOSE_LOG, msg, logFileName, tag);
    }

    public static void fd(final Object msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_DEBUG_LOG, msg, logFileName, tag);
    }

    public static void fi(final Object msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_INFO_LOG, msg, logFileName, tag);
    }

    public static void fw(final Object msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_WARN_LOG, msg, logFileName, tag);
    }

    public static void fe(final Object msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_ERROR_LOG, msg, logFileName, tag);
    }

    /**
     * 4.Log??,Log 
     *
     * @param msg
     * @param logFileName ??
     * @param tag
     */
    public static void ffjson(final String msg, @Nullable final String logFileName, final String... tag) {
        writeLog(SHOW_JSON_LOG, msg, logFileName, tag);
    }

    //4.? + Log??,Log 

    /**
     * 4.? + Log??,Log 
     *
     * @param msg
     * @param logFileName
     * @param tag
     */
    public static void fvv(final Object msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_VERBOSE_LOG, msg, tag);

        writeLog(SHOW_VERBOSE_LOG, msg, logFileName, tag);
    }

    public static void fdd(final Object msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_DEBUG_LOG, msg, tag);

        writeLog(SHOW_DEBUG_LOG, msg, logFileName, tag);
    }

    public static void fii(final Object msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_INFO_LOG, msg, tag);

        writeLog(SHOW_INFO_LOG, msg, logFileName, tag);
    }

    public static void fww(final Object msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_WARN_LOG, msg, tag);

        writeLog(SHOW_WARN_LOG, msg, logFileName, tag);
    }

    public static void fee(final Object msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_ERROR_LOG, msg, tag);

        writeLog(SHOW_ERROR_LOG, msg, logFileName, tag);

    }

    /**
     * 4.? + Log??,Log 
     *
     * @param msg
     * @param logFileName
     * @param tag
     */
    public static void fcfjson(final String msg, @Nullable final String logFileName, final String... tag) {
        consoleLog(SHOW_JSON_LOG, msg, tag);

        writeLog(SHOW_JSON_LOG, msg, logFileName, tag);
    }

    /**
     * msg 
     *
     * @param msg
     */
    private static void saveLog2File(String msg) {
        // ??
        String strDateTimeFileName = fileSimpleDateFormat.format(new Date());
        saveLog2File(msg, strDateTimeFileName + LOGFILE_SUFFIX);
    }

    /**
     * msg 
     *
     * @param msg
     * @param logFileName log ??
     */
    private static void saveLog2File(String msg, String logFileName) {
        FileWriter objFilerWriter = null;
        BufferedWriter objBufferedWriter = null;

        do { // ???
            String state = Environment.getExternalStorageState();
            //  SD ?
            if (!Environment.MEDIA_MOUNTED.equals(state)) {
                Log.d(TAG, "Not mount SD card!");
                break;
            }

            // SD ???
            if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                Log.d(TAG, "Not allow write SD card!");
                break;
            }

            File rootPath = new File(sLogFolderPath);
            if (rootPath.exists()) {

                File fileLogFilePath = new File(sLogFolderPath, logFileName);
                // ?
                if (true != fileLogFilePath.exists()) {
                    try {
                        fileLogFilePath.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                        break;
                    }
                }

                // ??
                if (true != fileLogFilePath.exists()) {
                    Log.d(TAG, "Create log file failed!");
                    break;
                }

                try {
                    objFilerWriter = new FileWriter(fileLogFilePath, //
                            true); // ?
                } catch (IOException e1) {
                    Log.d(TAG, "New FileWriter Instance failed");
                    e1.printStackTrace();
                    break;
                }

                objBufferedWriter = new BufferedWriter(objFilerWriter);

                try {
                    objBufferedWriter.write(msg);
                    objBufferedWriter.flush();
                } catch (IOException e) {
                    Log.d(TAG, "objBufferedWriter.write or objBufferedWriter.flush failed");
                    e.printStackTrace();
                }
            } else {
                Log.d(TAG, "Log savePaht invalid!");
            }

        } while (false);

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

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

    /**
     * @param type
     * @param objectMsg
     * @param tagArgs
     */
    private static void printLog(int type, Object objectMsg, @Nullable String... tagArgs) {
        //?
        int index = 4;
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final StackTraceElement stackTraceElement = stackTrace[index];
        printLog(stackTraceElement, type, objectMsg, tagArgs);
    }

    /**
     * @param stackTraceElement
     * @param type
     * @param objectMsg
     * @param tagArgs
     */
    private static void printLog(final StackTraceElement stackTraceElement, int type, Object objectMsg,
            @Nullable String... tagArgs) {
        String msg;
        if (m_cLogCatShowLogType == OPERATION_BIT) {
            return;
        }

        String fileName = stackTraceElement.getFileName();
        String methodName = stackTraceElement.getMethodName();
        int lineNumber = stackTraceElement.getLineNumber();

        StringBuilder tagBuilder = new StringBuilder();
        tagBuilder.append(TAG);
        if (tagArgs == null) {
            tagBuilder.append(TAG_SEPARATOR);
            tagBuilder.append(fileName);
        } else {
            for (String tagArg : tagArgs) {
                tagBuilder.append(TAG_SEPARATOR);
                tagBuilder.append(tagArg);
            }
        }

        methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[ (").append(fileName).append(":").append(lineNumber).append(")#").append(methodName)
                .append(" ] ");

        if (objectMsg == null) {
            msg = "Log with null Object";
        } else {
            msg = objectMsg.toString();
        }
        if (msg != null && type != SHOW_JSON_LOG) {
            stringBuilder.append(msg);
        }

        String logStr = stringBuilder.toString();

        switch (type) {
        case SHOW_VERBOSE_LOG:
            Log.v(tagBuilder.toString(), logStr);
            break;
        case SHOW_DEBUG_LOG:
            Log.d(tagBuilder.toString(), logStr);
            break;
        case SHOW_INFO_LOG:
            Log.i(tagBuilder.toString(), logStr);
            break;
        case SHOW_WARN_LOG:
            Log.w(tagBuilder.toString(), logStr);
            break;
        case SHOW_ERROR_LOG:
            Log.e(tagBuilder.toString(), logStr);
            break;
        case SHOW_JSON_LOG: {
            if (TextUtils.isEmpty(msg)) {
                Log.d(tagBuilder.toString(), "Empty or Null json content");
                return;
            }

            String message = null;

            try {
                if (msg.startsWith("{")) {
                    JSONObject jsonObject = new JSONObject(msg);
                    message = jsonObject.toString(JSON_INDENT);
                } else if (msg.startsWith("[")) {
                    JSONArray jsonArray = new JSONArray(msg);
                    message = jsonArray.toString(JSON_INDENT);
                }
            } catch (JSONException e) {
                e("JSONException/" + tagBuilder.toString(), e.getCause().getMessage() + LINE_SEPARATOR + msg);
                return;
            }

            printLine(JSON + tagBuilder.toString(), true);
            message = logStr + LINE_SEPARATOR + message;
            String[] lines = message.split(LINE_SEPARATOR);
            StringBuilder jsonContent = new StringBuilder();
            for (String line : lines) {
                jsonContent.append(" ").append(line).append(LINE_SEPARATOR);
            }
            Log.d(JSON + tagBuilder.toString(), jsonContent.toString());
            printLine(JSON + tagBuilder.toString(), false);
        }
            break;
        }

    }

    /**
     * Log 
     *
     * @param logLevel
     * @param msg
     * @param logFileName
     * @param tag
     */
    private static void writeLog(@LockLevel final int logLevel, final Object msg,
            @Nullable final String logFileName, final String... tag) {
        if (OPERATION_BIT != (logLevel & m_cFileSaveLogType)) {
            //?
            final StackTraceElement stackTraceElement = getStackTraceElement(INDEX);
            mSingleExecutors.execute(new Runnable() {
                @Override
                public void run() {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    fileLog(stackTraceElement, logLevel, msg, logFileName, tag);
                }
            });
        }
    }

    /**
     * ?
     *
     * @param logLevel
     * @param msg
     * @param tag
     */
    private static void consoleLog(@LockLevel final int logLevel, Object msg, String[] tag) {
        //?
        consoleLog(logLevel, getStackTraceElement(INDEX), msg, tag);
    }

    /**
     * ?
     *
     * @param logLevel
     * @param msg
     * @param tag
     */
    private static void consoleLog(@LockLevel final int logLevel, final StackTraceElement stackTraceElement,
            Object msg, String[] tag) {
        if (OPERATION_BIT != (logLevel & m_cLogCatShowLogType)) {
            printLog(stackTraceElement, logLevel, msg, tag);
        }
    }

    /**
     * @param stackTraceElement
     * @param type
     * @param objectMsg
     * @param tagArgs
     */
    private static void fileLog(StackTraceElement stackTraceElement, int type, Object objectMsg,
            @Nullable String... tagArgs) {
        fileLog(stackTraceElement, type, objectMsg, null, tagArgs);
    }

    private static StackTraceElement getStackTraceElement(int index) {
        //?
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return stackTrace[index];
    }

    /**
     * @param stackTraceElement
     * @param type
     * @param objectMsg
     * @param logFileName
     * @param tagArgs
     */
    private static void fileLog(StackTraceElement stackTraceElement, int type, Object objectMsg,
            @Nullable String logFileName, @Nullable String... tagArgs) {
        String msg;
        if (m_cFileSaveLogType == OPERATION_BIT) {
            return;
        }

        String fileName = stackTraceElement.getFileName();
        String className = stackTraceElement.getClassName();
        String methodName = stackTraceElement.getMethodName();
        int lineNumber = stackTraceElement.getLineNumber();

        StringBuilder tagBuilder = new StringBuilder();
        tagBuilder.append(TAG);
        if (tagArgs == null) {
            tagBuilder.append(TAG_SEPARATOR);
            tagBuilder.append(className);
        } else {
            for (String tagArg : tagArgs) {
                tagBuilder.append(TAG_SEPARATOR);
                tagBuilder.append(tagArg);
            }
        }

        methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

        StringBuilder stringBuilder = new StringBuilder();

        // ??

        String strDateTimeLogHead = simpleDateFormat.format(new Date());

        // ?????
        stringBuilder.append(tagBuilder.toString()).append(" ").append(strDateTimeLogHead).append(LINE_SEPARATOR)
                .append("fileName:").append(fileName).append(LINE_SEPARATOR).append("className:").append(className)
                .append(LINE_SEPARATOR).append("methodName:").append(methodName).append(LINE_SEPARATOR)
                .append("lineNumber:").append(lineNumber).append(LINE_SEPARATOR);

        if (objectMsg == null) {
            msg = "Log with null Object";
        } else {
            msg = objectMsg.toString();
        }
        if (msg != null && type != SHOW_JSON_LOG) {
            stringBuilder.append(msg);
        }
        stringBuilder.append(LINE_SEPARATOR + LINE_SEPARATOR);

        switch (type) {
        case SHOW_VERBOSE_LOG:
            if (logFileName == null) {
                saveLog2File(V + stringBuilder.toString());
            } else {
                saveLog2File(V + stringBuilder.toString(), logFileName);
            }

            break;
        case SHOW_DEBUG_LOG:
            if (logFileName == null) {
                saveLog2File(D + stringBuilder.toString());
            } else {
                saveLog2File(D + stringBuilder.toString(), logFileName);
            }
            break;
        case SHOW_INFO_LOG:
            if (logFileName == null) {
                saveLog2File(I + stringBuilder.toString());
            } else {
                saveLog2File(I + stringBuilder.toString(), logFileName);
            }

            break;
        case SHOW_WARN_LOG:
            if (logFileName == null) {
                saveLog2File(W + stringBuilder.toString());
            } else {
                saveLog2File(W + stringBuilder.toString(), logFileName);
            }
            break;
        case SHOW_ERROR_LOG:
            if (logFileName == null) {
                saveLog2File(E + stringBuilder.toString());
            } else {
                saveLog2File(E + stringBuilder.toString(), logFileName);
            }
            break;
        case SHOW_JSON_LOG: {
            if (TextUtils.isEmpty(msg)) {
                Log.d(tagBuilder.toString(), "Empty or Null json content");
                return;
            }

            String message = null;

            try {
                if (msg.startsWith("{")) {
                    JSONObject jsonObject = new JSONObject(msg);
                    message = jsonObject.toString(JSON_INDENT);
                } else if (msg.startsWith("[")) {
                    JSONArray jsonArray = new JSONArray(msg);
                    message = jsonArray.toString(JSON_INDENT);
                }
            } catch (JSONException e) {
                e("JSONException/" + tagBuilder.toString(), e.getCause().getMessage() + LINE_SEPARATOR + msg);
                return;
            }

            stringBuilder.append(JSON);
            stringBuilder.append(LINE_SEPARATOR);
            stringBuilder.append(
                    "??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????");
            message = stringBuilder.toString() + LINE_SEPARATOR + message;
            String[] lines = message.split(LINE_SEPARATOR);
            StringBuilder jsonContent = new StringBuilder();
            for (String line : lines) {
                jsonContent.append(" ").append(line).append(LINE_SEPARATOR);
            }
            jsonContent.append(
                    "????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????");
            if (logFileName == null) {
                saveLog2File(jsonContent.toString());
            } else {
                saveLog2File(stringBuilder.toString(), logFileName);
            }

        }
            break;
        }

    }

    private static void printLine(String tag, boolean isTop) {
        if (isTop) {
            Log.d(tag,
                    "???????????????????????????????????????????????????????????????????????????????????????");
        } else {
            Log.d(tag,
                    "???????????????????????????????????????????????????????????????????????????????????????");
        }
    }

    @NonNull
    public static Builder newBuilder() {
        return new Builder();
    }
}