开始

最近在公司的项目中需要收集安卓的运行日志,最开始的时候我把日志写入到设备本地文件中存
储,使用Bmob的文件管理功能定时上传到服务器,在使用过程中有些地方不太方便,第一就是文
件上传过程中不能写入日志,不然会因为文件上传完成后的校验不通过,导致上传失败进而引发
程序崩溃。另外一个就是如果单纯的只为了收集日志的话引入Bmob的sdk有点过于浪费,Bmob
的SDK体积也并不小。
然后就开始自己动手解决这个问题。

服务端实现

既然是远程收集日志,我们需要一个服务端程序,服务端的主要作用就是接受客户端传来的数
据,写入到文件中。我们选择了Python+web.py来实现。

import json
import web
import time
import sys
reload(sys)
sys.setdefaultencoding('UTF-8')     #设置字符编码

web.config.debug = True             #调试模式打开

urls = (
        '/','index'
    )

localLogs = ""


class index:

    def GET(self):                  #以网页形式返回日志数据

        htmlFormat = "<html><head><title></title></head><body>%s   <script type=
amp;quot;text/javascript
amp;quot;>function myrefresh(){window.location.reload();window.scrollTo(0,document.body.scrollHeight);}setTimeout('myrefresh()',1000); </script></body></html>"
        global localLogs
        # localLogs = localLogs.encode('gbk')
        return htmlFormat % localLogs

    def POST(self):                 #接收请求保存日志到文件

        inputs=web.input()
        content = web.data()        #接收到日志数据

        if content.startswith('{') and content.endswith('}'):
            content = '[' + content + ']'           #以Json数组格式处理

        print content
        logs = json.loads(content)

        for log in logs:                            #处理数组中的log元素
            if 'stack' not in log:
                log['stack'] = " "

            color = '#808080'                       #根据日志级别不同,以不同颜色显示到网页
            if log['level'] == 'INFO':
                color = '#008000'
            elif log['level'] == 'WARNING':
                color = '#FFA500'
            elif log['level'] == 'ERROR':
                color = '#FF0000'

            strLog = '<div style="color:%s">%s  %s: [%s] %s </div>' % (color, log['time'],log['level'], log['tag'], log['msg'])#日志内容

            stacks = log['stack'].split('\n')
            strLog = strLog + ('<div color="%s">' % color)
            for s in stacks:
                strLog = strLog + ('<div>%s</div>' % (s.strip()))

            strLog = strLog + '</div>'

            fileName="logs/"+log['boxId']+"_"+time.strftime('%Y-%m-%d')+".html"
            f=open(fileName,'a')                        #以追加方式写文件
            f.write(strLog+"\n")
            f.close()
            global localLogs
            localLogs = localLogs + strLog

        return ""


if __name__ == '__main__':
    app = web.application(urls, globals())
    app.run()

安卓端实现

首先定义一个日志接口

public interface ILog {

    public void d(String tag, String msg);
    public void i(String tag, String msg);
    public void w(String tag, String msg);
    public void w(String tag, String msg, Throwable e);
    public void e(String tag, String msg);
    public void e(String tag, String msg, Throwable e);

    public void destory();
}

远程日志实现接口

public class URemoteLog implements ILog{


    private URemoteLogPrinter printer;

    public URemoteLog(int interval){
        printer = new URemoteLogPrinter(interval);
    }

    @Override
    public void d(String tag, String msg) {
        printer.print(new ULog(ULog.L_DEBUG, tag, msg));
    }

    @Override
    public void i(String tag, String msg) {
        printer.print(new ULog(ULog.L_INFO, tag, msg));
    }

    @Override
    public void w(String tag, String msg) {
        printer.print(new ULog(ULog.L_WARN, tag, msg));
    }

    @Override
    public void w(String tag, String msg, Throwable e) {
        printer.print(new ULog(ULog.L_WARN, tag, msg));
    }

    @Override
    public void e(String tag, String msg) {
        printer.print(new ULog(ULog.L_ERROR, tag, msg));
    }

    @Override
    public void e(String tag, String msg, Throwable e) {
        printer.print(new ULog(ULog.L_ERROR, tag, msg));
    }

    @Override
    public void destory() {
        printer.stop();
    }

}

日志打印类,主要将日志写入远程

public class URemoteLogPrinter {

    private List<ULog> logs;
    private int interval = 1000; //单位 毫秒

    private Timer timer;
    private boolean running;

    public URemoteLogPrinter(){
    }

    public URemoteLogPrinter(int interval){
        this.logs = Collections.synchronizedList(new ArrayList<ULog>());
        this.interval = interval;
    }

    public void print(ULog log){
        start();
        synchronized (logs) {
            logs.add(log);
        }
    }

    public void printImmediate(String log){
        HttpService.reportLog(log, new HttpCallback<String>() {
            @Override
            public void onSuccess(String data) {

            }
            @Override
            public void onFailure(int statusCode, Error error) {

            }
        });
    }

    public List<ULog> getAndClear(){
        synchronized (logs) {
            List<ULog> all = new ArrayList<ULog>(logs);
            logs.clear();
            return all;
        }
    }

    public void start(){
        if(running){
            return;
        }

        running = true;
        TimerTask task = new LogPrintTask();
        timer = new Timer(true);
        timer.scheduleAtFixedRate(task, 100, interval);
    }

    public void stop(){
        if(timer != null){
            timer.cancel();
        }
        running = false;
    }

    class LogPrintTask extends TimerTask{
        @Override
        public void run() {
            try{
                List<ULog> logs = getAndClear();
                if(logs.size() > 0){
                    StringBuilder sb = new StringBuilder();
                    sb.append("[");
                    for(ULog log : logs){
                        sb.append(log.toJson()).append(",");
                    }
                    sb.deleteCharAt(sb.length()-1).append("]");
                    HttpService.reportLog(sb.toString(), new HttpCallback<String>() {
                        @Override
                        public void onSuccess(String data) {

                        }
                        @Override
                        public void onFailure(int statusCode, Error error) {

                        }
                    });
                }
            }catch(Exception e){
                e.printStackTrace();
                stop();
            }
        }

    }
}

日志实体类

public class ULog {
    public static final String L_DEBUG="L_DEBUG";
    public static final String L_INFO="L_INFO";
    public static final String L_WARN="L_WARN";
    public static final String L_ERROR="L_ERROR";

    private String boxId;
    private String level;
    private String msg;
    private String tag;
    private String time;

    public ULog(String level,String tag,String msg) {
        this.boxId = MainApplication.getBoxId();
        this.level = level;
        this.msg = msg;
        this.tag = tag;
        Date date=new Date();
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time=format.format(date);
        this.time = time;
    }
    public String toJson(){
        Gson gson=new Gson();
        return gson.toJson(this);
    }
}