开始
最近在公司的项目中需要收集安卓的运行日志,最开始的时候我把日志写入到设备本地文件中存
储,使用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/javascriptamp;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);
}
}