Android App Logging 组件客制化

来源:互联网 发布:linux中安装anaconda 编辑:程序博客网 时间:2024/06/15 19:19

目录

一.日志的作用与注意事项

二.常规崩溃日志的处理

三.日志开源框架:Logger的介绍

四.利用java自带的logger封装和处理


日志的作用

在日常开发过程中,日志是代码的必要组成部分,一个好的代码也必然有一个好的日志输出,优秀的日志不仅可以快速帮助我们分析定位问题还可以在对日志的数据挖掘中产生很大的威力。

Java自带日志的局限性                   

java在发生异常时可以打印它的堆栈信息以帮助调试,但是java的异常也有下列问题:

1)java出现异常时只能展示静态的调用堆栈信息,对应异常之前的调用参数则无法进行展示,也就是只知道哪里发生了异常,而不知道是哪些数据导致了异常; 
2)java的异常堆栈通常是直接定位到某个文件的某一行,在发生异常时,如果不看代码都不知道异常详细情况,可读性比较差;


日志输出注意事项:

1)在任务执行之前和执行之后打印log,任务执行之前的log上带上当前任务的所有参数,任务执行结束的log尽量带上当前的处理结果; 
2)在关键函数入口处(即进入该函数的开始位置)和出口前打印log,入口处的log带上当前的参数,出口处的log尽量带上当前的处理结果; 
3)在错误和异常日志中带上错误和异常执行前的关键参数;在实际开发中经常会遇到各种条件判断,如果条件不满足,则返回错误,此时再返回之前尽量要输出一条log,并在该log上带上引起错误的参数,对于异常也是如此;

4)在关键代码前加上log,log要带上当前的重点参数;例如,有一段代码是向另外一个模块发送消息,则在发送消息之前最好加上log,log中要带上向哪里发送什么; 
5)在有关键信息出现的地方加log,并在log中输出这些关键的信息; 
6)日志描述要简洁清晰;


常规崩溃日志的处理:

1.自定义CrashHandler

先介绍一下常用的异常处理CrashHandler类:

public class CrashHandler implements UncaughtExceptionHandler {
    
    public static final String TAG = "CrashHandler";
      
    //系统默认的UncaughtException处理类   
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例  
    private static CrashHandler INSTANCE = new CrashHandler();  
    //程序的Context对象  
    private Context mContext;
    //用来存储设备信息和异常信息  
    private Map<String, String> infos = new HashMap<String, String>();
  
    //用于格式化日期,作为日志文件名的一部分  
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
  
    /** 保证只有一个CrashHandler实例 */  
    private CrashHandler() {  
    }  
  
    /** 获取CrashHandler实例 ,单例模式 */  
    public static CrashHandler getInstance() {  
        return INSTANCE;  
    }  
  
    /** 
     * 初始化 
     *  
     * @param context 
     */  
    public void init(Context context) {
        mContext = context;  
        //获取系统默认的UncaughtException处理器  
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器  
        Thread.setDefaultUncaughtExceptionHandler(this);
    }  
  
    /** 
     * 当UncaughtException发生时会转入该函数来处理 
     */  
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
    ex.printStackTrace();
        if (!handleException(ex) && mDefaultHandler != null) {  
            //如果用户没有处理则让系统默认的异常处理器来处理  
            mDefaultHandler.uncaughtException(thread, ex);  
        } else {  
            try {  
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }  
            //退出程序  
            android.os.Process.killProcess(android.os.Process.myPid());  
            
            System.exit(1);
        }  
    }  
  
    /** 
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 
     *  
     * @param ex 
     * @return true:如果处理了该异常信息;否则返回false. 
     */  
    private boolean handleException(Throwable ex) {
        if (ex == null) {  
            return false;  
        }  
        //使用Toast来显示异常信息  
        new Thread() {
            @Override
            public void run() {  
                Looper.prepare();
                Toast.makeText(mContext, "出现异常 即将退出app...", Toast.LENGTH_SHORT).show();
                Looper.loop();
            }  
        }.start();  
        //收集设备参数信息   
        collectDeviceInfo(mContext);  
        //保存日志文件   
        saveCrashInfo2File(ex);  
        return true;  
    }  
      
    /** 
     * 收集设备参数信息 
     * @param ctx 
     */  
    public void collectDeviceInfo(Context ctx) {
        try {  
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {  
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);  
                infos.put("versionCode", versionCode);  
            }  
        } catch (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }  
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {  
                field.setAccessible(true);  
                infos.put(field.getName(), field.get(null).toString());  
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }  
        }  
    }  
  
    /** 
     * 保存错误信息到文件中 
     *  
     * @param ex 
     * @return  返回文件名称,便于将文件传送到服务器 
     */  
    private String saveCrashInfo2File(Throwable ex) {
          
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");  
        }  
          
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);  
        Throwable cause = ex.getCause();
        while (cause != null) {  
            cause.printStackTrace(printWriter);  
            cause = cause.getCause();  
        }  
        printWriter.close();  
        String result = writer.toString();
        sb.append(result);  
        try {  
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".txt";
             
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = Environment.getExternalStorageDirectory().getPath() + File.separatorChar + "Android" + File.separatorChar + "data" + File.separatorChar + mContext.getPackageName() + File.separatorChar;
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
            }


            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }  
        return null;  
    }  
}  


还有以邮件发送的形式:

/** 
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告. 
 *  
 * @author way 
 *  
 */  
public class CrashHandler implements UncaughtExceptionHandler {  
    private Thread.UncaughtExceptionHandler mDefaultHandler;// 系统默认的UncaughtException处理类  
    private static CrashHandler INSTANCE;// CrashHandler实例  
    private Context mContext;// 程序的Context对象  
  
    /** 保证只有一个CrashHandler实例 */  
    private CrashHandler() {  
  
    }  
  
    /** 获取CrashHandler实例 ,单例模式 */  
    public static CrashHandler getInstance() {  
        if (INSTANCE == null)  
            INSTANCE = new CrashHandler();  
        return INSTANCE;  
    }  
  
    /** 
     * 初始化 
     *  
     * @param context 
     */  
    public void init(Context context) {  
        mContext = context;  
  
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器  
        Thread.setDefaultUncaughtExceptionHandler(this);// 设置该CrashHandler为程序的默认处理器  
    }  
  
    /** 
     * 当UncaughtException发生时会转入该重写的方法来处理 
     */  
    public void uncaughtException(Thread thread, Throwable ex) {  
        if (!handleException(ex) && mDefaultHandler != null) {  
            // 如果自定义的没有处理则让系统默认的异常处理器来处理  
            mDefaultHandler.uncaughtException(thread, ex);  
        }  
    }  
  
    /** 
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 
     *  
     * @param ex 
     *            异常信息 
     * @return true 如果处理了该异常信息;否则返回false. 
     */  
    public boolean handleException(Throwable ex) {  
        if (ex == null || mContext == null)  
            return false;  
        final String crashReport = getCrashReport(mContext, ex);  
        Log.i("error", crashReport);  
        new Thread() {  
            public void run() {  
                Looper.prepare();  
                File file = save2File(crashReport);  
                sendAppCrashReport(mContext, crashReport, file);  
                Looper.loop();  
            }  
  
        }.start();  
        return true;  
    }  
  
    private File save2File(String crashReport) {  
        // TODO Auto-generated method stub  
        String fileName = "crash-" + System.currentTimeMillis() + ".txt";  
        if (Environment.getExternalStorageState().equals(  
                Environment.MEDIA_MOUNTED)) {  
            try {  
                File dir = new File(Environment.getExternalStorageDirectory()  
                        .getAbsolutePath() + File.separator + "crash");  
                if (!dir.exists())  
                    dir.mkdir();  
                File file = new File(dir, fileName);  
                FileOutputStream fos = new FileOutputStream(file);  
                fos.write(crashReport.toString().getBytes());  
                fos.close();  
                return file;  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return null;  
    }  
  
    private void sendAppCrashReport(final Context context,  
            final String crashReport, final File file) {  
        // TODO Auto-generated method stub  
        AlertDialog mDialog = null;  
        AlertDialog.Builder builder = new AlertDialog.Builder(context);  
        builder.setIcon(android.R.drawable.ic_dialog_info);  
        builder.setTitle("程序出错啦");  
        builder.setMessage("请把错误报告以邮件的形式提交给我们,谢谢!");  
        builder.setPositiveButton(android.R.string.ok,  
                new DialogInterface.OnClickListener() {  
                    public void onClick(DialogInterface dialog, int which) {  
  
                        // 发送异常报告  
                        try {  
                            //注释部分是已文字内容形式发送错误信息  
                            // Intent intent = new Intent(Intent.ACTION_SENDTO);  
                            // intent.setType("text/plain");  
                            // intent.putExtra(Intent.EXTRA_SUBJECT,   
                            // intent.putExtra(Intent.EXTRA_TEXT, crashReport);  
                            // intent.setData(Uri  
                            // .parse("mailto:way.ping.li@gmail.com"));  
                            // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                            // context.startActivity(intent);  
                              
                            //下面是以附件形式发送邮件  
                            Intent intent = new Intent(Intent.ACTION_SEND);  
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                            String[] tos = { "812410915@qq.com" };  
                            intent.putExtra(Intent.EXTRA_EMAIL, tos);  
  
                            intent.putExtra(Intent.EXTRA_SUBJECT,  
                                    "Android客户端 - 错误报告");  
                            if (file != null) {  
                                intent.putExtra(Intent.EXTRA_STREAM,  
                                        Uri.fromFile(file));  
                                intent.putExtra(Intent.EXTRA_TEXT,  
                                        "请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n");  
                            } else {  
                                intent.putExtra(Intent.EXTRA_TEXT,  
                                        "请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n"  
                                                + crashReport);  
                            }  
                            intent.setType("text/plain");  
                            intent.setType("message/rfc882");  
                            Intent.createChooser(intent, "Choose Email Client");  
                            context.startActivity(intent);  
                        } catch (Exception e) {  
                            Toast.makeText(context,  
                                    "There are no email clients installed.",  
                                    Toast.LENGTH_SHORT).show();  
                        } finally {  
                            dialog.dismiss();  
                            // 退出  
                            android.os.Process.killProcess(android.os.Process  
                                    .myPid());  
                            System.exit(1);  
                        }  
                    }  
                });  
        builder.setNegativeButton(android.R.string.cancel,  
                new DialogInterface.OnClickListener() {  
                    public void onClick(DialogInterface dialog, int which) {  
                        dialog.dismiss();  
                        // 退出  
                        android.os.Process.killProcess(android.os.Process  
                                .myPid());  
                        System.exit(1);  
                    }  
                });  
        mDialog = builder.create();  
        mDialog.getWindow().setType(  
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
        mDialog.show();  
    }  
  
    /** 
     * 获取APP崩溃异常报告 
     *  
     * @param ex 
     * @return 
     */  
    private String getCrashReport(Context context, Throwable ex) {  
        PackageInfo pinfo = getPackageInfo(context);  
        StringBuffer exceptionStr = new StringBuffer();  
        exceptionStr.append("Version: " + pinfo.versionName + "("  
                + pinfo.versionCode + ")\n");  
        exceptionStr.append("Android: " + android.os.Build.VERSION.RELEASE  
                + "(" + android.os.Build.MODEL + ")\n");  
        exceptionStr.append("Exception: " + ex.getMessage() + "\n");  
        StackTraceElement[] elements = ex.getStackTrace();  
        for (int i = 0; i < elements.length; i++) {  
            exceptionStr.append(elements[i].toString() + "\n");  
        }  
        return exceptionStr.toString();  
    }  
  
    /** 
     * 获取App安装包信息 
     *  
     * @return 
     */  
    private PackageInfo getPackageInfo(Context context) {  
        PackageInfo info = null;  
        try {  
            info = context.getPackageManager().getPackageInfo(  
                    context.getPackageName(), 0);  
        } catch (NameNotFoundException e) {  
            // e.printStackTrace(System.err);  
            // L.i("getPackageInfo err = " + e.getMessage());  
        }  
        if (info == null)  
            info = new PackageInfo();  
        return info;  
    }  
  
}  


然后是使用方法(只需在application中初始化即可):




以下是crash之后的效果


分享一个应用crash后重启应用的代码



日志开源框架:Logger的介绍

作者:orhanobut

Logger库能提供的功能:

线程的信息

类的信息

方法的信息

格式打印json、xml等

点击链接跳转到源码打印处

库的地址:https://github.com/orhanobut/logger


1.添加、初始化、使用


项目中书写



调用



控制台上的预览



注意点:adapter是增量的

比如下面调用了两次addLogAdapter()方法



2.自定义打印格式



效果预览



将日志保存到手机中:DiskLogAdapter适配器



log存储在手机的位置



日志大小需知:每个log日志文件的大小最大为500K,超过后会自动新建



DisLogAdapter源码&CsvFormatStrategy源码



查看源码可以发现,日志是保存在外置SD卡根目录的“logger”文件夹下

以logs_x打头,文件格式是.csv,直接可用excel打开

如果要修改成自己想要的存储路径、文件大小等信息,可以仿照DiskLogAdapter

写一个自定义的DiskLogAdapter


日志的内容预览



腾讯Bugly介绍(强大的第三方bug追踪平台)强烈推荐


首页



崩溃分析页



崩溃分析详情页



最后介绍一下我自己项目中用到的log处理方式


主要类就三个,其实就两个,HttpLoggerInterceptor拦截器和LogHelper是一样的,都是和我们LogFileHandler中的fileHandler关联起来,然后生成文件。利用的是JDK内置Logger的类,在java.util.logging包下。里面FileHandler是可以把log日志写到本地,而SocketHandler是可以通过TCP的socket连接发送到远程端口,如和串口服务器联调也是可以的。

logger类的详细介绍可参考这篇文章:http://mouselearnjava.iteye.com/blog/1926353


JDK内置Logger大致的类图



Logger和fileHandler的关联


LogFileHandler.java

public class LogFileHandler {

    private static final long             SAVE_DAYS   = 1000 * 60 * 60 * 24 * 3; // 保留3天的日志
    private static       SimpleDateFormat dirFormat   = new SimpleDateFormat("yyyy-MM-dd");// 日志文件夹格式
    private static       SimpleDateFormat logFormat   = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss.SSS");// 日志的输出格式
    public static        LogFileHandler   instance    = null;
    private              FileHandler      fileHandler = null;
    private Application application;


    public static LogFileHandler init(Application application) {
        if (instance == null) {
            // [1]
            synchronized (LogHelper.class) {
                if (instance == null) {
                    //单例模式之双重检测:线程一在此之前线程二到达了位置[1],如果此处不二次判断,那么线程二执行到这里的时候还会重新new
                    instance = new LogFileHandler(application);
                }
            }
        }
        return instance;
    }


    public synchronized static LogFileHandler getInstance() {
        if (instance == null) {
            synchronized (LogFileHandler.class) {
                if (instance == null) {
                    throw new UnsupportedOperationException("Didn't finish the LogHelper init");
                }
            }
        }
        return instance;
    }


    private LogFileHandler(Application application) {
        this.application = application;
        deleteLoggerFile();
        initFileHandler();
    }


    private synchronized void initFileHandler() {
        try {
            String path = getLoggerFilepath() + dirFormat.format(new Date()) + File.separatorChar;
            if (!new File(path).exists()) {
                new File(path).mkdirs();
            }
            path += logFormat.format(new Date()) + ".txt";
            if (!new File(path).exists()) {
                new File(path).createNewFile();
            }
            fileHandler = new FileHandler(path, true);
            fileHandler.setLevel(Level.FINE);
            fileHandler.setFormatter(new Formatter() {
                @Override
                public String format(LogRecord record) {
                    return logFormat.format(new Date(record.getMillis())) + " " + record.getLoggerName() + " " + record.getMessage() + "\n";
                }
            });
        } catch (IOException e) {
            fileHandler = null;
            e.printStackTrace();
        }
    }


    public synchronized String getLoggerFilepath() {
        return Environment.getExternalStorageDirectory().getPath() + File.separatorChar + "Android" + File.separatorChar + "data" + File.separatorChar + application.getPackageName() + File.separatorChar + "log" + File.separatorChar;
    }


    public synchronized boolean bindLogger(Logger logger) {
        if (fileHandler == null) {
            initFileHandler();
        }
        if (logger == null || fileHandler == null) {
            return false;
        }
        logger.addHandler(fileHandler);
        return true;
    }


    public synchronized void deleteLoggerFile() {
        File file = new File(getLoggerFilepath());
        if (!file.exists()) {
            return;
        }
        File[] files = file.listFiles();
        for (File fil : files) {
            // 删除最后修改日期早于三天前的日志
            if (System.currentTimeMillis() - fil.lastModified() > SAVE_DAYS) {
                deleteDirWihtFile(fil);
            }
        }
    }


    // 删除指定文件目录下的所有文件
    public synchronized static void deleteDirWihtFile(File dir) {
        if (dir == null || !dir.exists() || !dir.isDirectory()) {
            return;
        }
        for (File file : dir.listFiles()) {
            if (file.isFile())
                file.delete(); // 删除所有文件
            else if (file.isDirectory())
                deleteDirWihtFile(file); // 递规的方式删除文件夹
        }
        dir.delete();// 删除目录本身
    }
}


HttpLoggerInterceptor.java


public class HttpLoggerInterceptor implements Interceptor {
    private static final Charset UTF8 = Charset.forName("UTF-8");
    private volatile HttpLoggerInterceptor.Level printLevel = HttpLoggerInterceptor.Level.NONE;
    private boolean isBindFileHandler = false, isBindSocketHandler = false;
    private Logger logger;


    public enum Level {
        NONE,       //不打印log
        BASIC,      //只打印 请求首行 和 响应首行
        HEADERS,    //打印请求和响应的所有 Header
        BODY        //所有数据全部打印
    }


    public HttpLoggerInterceptor(String tag) {
        logger = Logger.getLogger(tag);
    }


    public void setPrintLevel(HttpLoggerInterceptor.Level level) {
        if (printLevel == null) throw new NullPointerException("printLevel == null. Use Level.NONE instead.");
        printLevel = level;
    }


    public void setLoggerLevel(java.util.logging.Level level) {
        logger.setLevel(level);
    }


    private void log(String message) {
        // 如果需要写文件
        if (!isBindFileHandler && needBindFileHandler) {
            isBindFileHandler = LogFileHelper.getInstance().bindLogger(logger);
        }
        // 如果需要将日志发送到网络
        if (!isBindSocketHandler && needBindSocketHandler) {
            isBindSocketHandler = LogSocketHelper.getInstance().bindLogger(logger);
        }
        logger.info(message);
    }


    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (printLevel == HttpLoggerInterceptor.Level.NONE) {
            return chain.proceed(request);
        }


        //请求日志拦截
        logForRequest(request, chain.connection());


        //执行请求,计算请求时间
        long startNs = System.nanoTime();
        Response response;
        try {
            response = chain.proceed(request);
        } catch (Exception e) {
            log("<--返回 HTTP FAILED: " + e);
            throw e;
        }
        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);


        //响应日志拦截
        return logForResponse(response, tookMs);
    }


    private void logForRequest(Request request, Connection connection) throws IOException {
        boolean logBody = (printLevel == HttpLoggerInterceptor.Level.BODY);
        boolean logHeaders = (printLevel == HttpLoggerInterceptor.Level.BODY || printLevel == HttpLoggerInterceptor.Level.HEADERS);
        RequestBody requestBody = request.body();
        boolean hasRequestBody = requestBody != null;
        Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;


        try {
            String requestStartMessage = "请求--> " + request.method() + ' ' + request.url() + ' ' + protocol;
            log(requestStartMessage);


            if (logHeaders) {
                if (hasRequestBody) {
                    // Request body headers are only present when installed as a network interceptor. Force
                    // them to be included (when available) so there values are known.
                    if (requestBody.contentType() != null) {
                        log("\tContent-Type: " + requestBody.contentType());
                    }
                    if (requestBody.contentLength() != -1) {
                        log("\tContent-Length: " + requestBody.contentLength());
                    }
                }
                Headers headers = request.headers();
                for (int i = 0, count = headers.size(); i < count; i++) {
                    String name = headers.name(i);
                    // Skip headers from the request body as they are explicitly logged above.
                    if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                        log("\t" + name + ": " + headers.value(i));
                    }
                }


                log(" ");
                if (logBody && hasRequestBody) {
                    if (isPlaintext(requestBody.contentType())) {
                        bodyToString(request);
                    } else {
                        log("\tbody: maybe [binary body], omitted!");
                    }
                }
            }
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            log("请求--> END " + request.method());
        }
    }


    private Response logForResponse(Response response, long tookMs) {
        Response.Builder builder = response.newBuilder();
        Response clone = builder.build();
        ResponseBody responseBody = clone.body();
        boolean logBody = (printLevel == HttpLoggerInterceptor.Level.BODY);
        boolean logHeaders = (printLevel == HttpLoggerInterceptor.Level.BODY || printLevel == HttpLoggerInterceptor.Level.HEADERS);


        try {
            log("<--返回 " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms)");
            if (logHeaders) {
                Headers headers = clone.headers();
                for (int i = 0, count = headers.size(); i < count; i++) {
                    log("\t" + headers.name(i) + ": " + headers.value(i));
                }
                log(" ");
                if (logBody && HttpHeaders.hasBody(clone)) {
                    if (responseBody == null) return response;


                    if (isPlaintext(responseBody.contentType())) {
                        byte[] bytes = IOUtils.toByteArray(responseBody.byteStream());
                        MediaType contentType = responseBody.contentType();
                        String body = new String(bytes, getCharset(contentType));
                        log("\tbody:" + body);
                        responseBody = ResponseBody.create(responseBody.contentType(), bytes);
                        return response.newBuilder().body(responseBody).build();
                    } else {
                        log("\tbody: maybe [binary body], omitted!");
                    }
                }
            }
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        } finally {
            log("<--返回 END HTTP");
        }
        return response;
    }


    private static Charset getCharset(MediaType contentType) {
        Charset charset = contentType != null ? contentType.charset(UTF8) : UTF8;
        if (charset == null) charset = UTF8;
        return charset;
    }


    /**
     * Returns true if the body in question probably contains human readable text. Uses a small sample
     * of code points to detect unicode control characters commonly used in binary file signatures.
     */
    private static boolean isPlaintext(MediaType mediaType) {
        if (mediaType == null) return false;
        if (mediaType.type() != null && mediaType.type().equals("text")) {
            return true;
        }
        String subtype = mediaType.subtype();
        if (subtype != null) {
            subtype = subtype.toLowerCase();
            if (subtype.contains("x-www-form-urlencoded") || subtype.contains("json") || subtype.contains("xml") || subtype.contains("html")) //
                return true;
        }
        return false;
    }


    private void bodyToString(Request request) {
        try {
            Request copy = request.newBuilder().build();
            RequestBody body = copy.body();
            if (body == null) return;
            Buffer buffer = new Buffer();
            body.writeTo(buffer);
            Charset charset = getCharset(body.contentType());
            log("\tbody:" + buffer.readString(charset));
        } catch (Exception e) {
            OkLogger.printStackTrace(e);
        }
    }
}


LogHelper.java

/**
 * 日志工具类
 * Created by kieedi on 2017/6/8.
 */


public class LogHelper {
    public static LogHelper instance = null;
    private boolean isBindFileHandler = false;
    private Application application;
    private Logger logger;


    public static LogHelper init(Application application, String tag) {
        LogFileHandler.init(application);
        if (instance == null) {
            // [1]
            synchronized (LogHelper.class) {
                if (instance == null) {
                    //单例模式之双重检测:线程一在此之前线程二到达了位置[1],如果此处不二次判断,那么线程二执行到这里的时候还会重新new
                    instance = new LogHelper(application, tag);
                }
            }
        }
        return instance;
    }


    public synchronized static LogHelper getInstance() {
        if (instance == null) {
            synchronized (LogHelper.class) {
                if (instance == null) {
                    throw new UnsupportedOperationException("Didn't finish the LogHelper init");
                }
            }
        }
        return instance;
    }


    private LogHelper(Application application, String tag) {
        if (application == null) {
            return;
        }
        this.application = application;
        logger = Logger.getLogger(tag);
        logger.setLevel(Level.FINE);
    }


    public synchronized Logger getLogger() {
        if (!isBindFileHandler) {
            isBindFileHandler = LogFileHandler.getInstance().bindLogger(logger);
        }
        return logger;
    }


    public synchronized static void v(String msg) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("]").append(msg);
        LogHelper.getInstance().getLogger().fine(toStringBuffer.toString());
    }


    public synchronized static void d(String msg) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ").append(msg);
        LogHelper.getInstance().getLogger().config(toStringBuffer.toString());
    }


    public synchronized static void i(String msg) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ").append(msg);
        LogHelper.getInstance().getLogger().info(toStringBuffer.toString());
    }


    public synchronized static void w(String msg) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ").append(msg);
        LogHelper.getInstance().getLogger().warning(toStringBuffer.toString());
    }


    public synchronized static void e(String msg) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ").append(msg);
        LogHelper.getInstance().getLogger().severe(toStringBuffer.toString());
    }




    public synchronized static void v(String...msgs) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ");
        if (msgs != null) {
            toStringBuffer.append("Log.v");
        }
        for (String msg : msgs) {
            toStringBuffer.append(String.format("===%s", msg));
        }
        LogHelper.getInstance().getLogger().fine(toStringBuffer.toString());
    }


    public synchronized static void d(String...msgs) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ");
        if (msgs != null) {
            toStringBuffer.append("Log.d");
        }
        for (String msg : msgs) {
            toStringBuffer.append(String.format("===%s", msg));
        }
        LogHelper.getInstance().getLogger().config(toStringBuffer.toString());
    }


    public synchronized static void i(String...msgs) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ");
        if (msgs != null) {
            toStringBuffer.append("Log.i");
        }
        for (String msg : msgs) {
            toStringBuffer.append(String.format("===%s", msg));
        }
        LogHelper.getInstance().getLogger().info(toStringBuffer.toString());
    }


    public synchronized static void w(String...msgs) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ");
        if (msgs != null) {
            toStringBuffer.append("Log.w");
        }
        for (String msg : msgs) {
            toStringBuffer.append(String.format("===%s", msg));
        }
        LogHelper.getInstance().getLogger().warning(toStringBuffer.toString());
    }


    public synchronized static void e(String...msgs) {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        StringBuffer toStringBuffer = new StringBuffer("[").append(traceElement.getFileName()).append(" | ")
                .append(traceElement.getLineNumber()).append(" | ").append(traceElement.getMethodName()).append("] ");
        if (msgs != null) {
            toStringBuffer.append("Log.e");
        }
        for (String msg : msgs) {
            toStringBuffer.append(String.format("===%s", msg));
        }
        LogHelper.getInstance().getLogger().severe(toStringBuffer.toString());
    }


    // 当前文件名
    public static String __FILE__() {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        return traceElement.getFileName();
    }


    // 当前方法名
    public static String __FUNC__() {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        return traceElement.getMethodName();
    }


    // 当前行号
    public static int __LINE__() {
        StackTraceElement traceElement = ((new Exception()).getStackTrace())[1];
        return traceElement.getLineNumber();
    }


    // 当前时间
    public static String __TIME__() {
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        return sdf.format(now);
    }
}


关联关系



通过流的方式发送到终端



详细代码


FileHandler生成的log预览



SocketHandler发送到终端的log预览



原创粉丝点击