ULog remote log-making Android debugging more convenient and intuitive
After the U8SDK is released, if you use U8SDK for SDK access, debugging is difficult. This is not a problem with the U8SDK, but a common problem in Android development. For Android development, we usually use logcat for debugging. If it is a native application, sometimes we can Debug it directly through the Debug breakpoint. However, we generally use U3D for game development, after Cocos2dx game engine development, it is released to the corresponding mobile platform. Therefore, breakpoint debugging is not very easy to implement. Most of the time, we print some logs and locate the corresponding logs in logcat to find the error information.
Logcat has been used for the past, but there are too many logs in logcat, although the filtering function is available, but sometimes, some models, there are too many printed logs, you have not read clearly, the log is topped by various system logs.
In addition, many game developers may be able to make the game ready soon. They will not be able to get started with Android development until they connect to the channel sdks of various platforms. As a result, how to use logcat correctly cannot be found.
Therefore, I would like to simply add a remote log print based on the original log, and then output the log to the web page, so that the log can be easily searched without worrying about various messy system logs.
Then, the original logs are simply encapsulated in U8SDK to Allow Log printing to support remote printing. Remote Printing, as its name implies, requires a server to receive the logs and output them to the webpage.
The first thing that comes to mind is to write data in java and deploy it to tomcat. But think about this simple logic. Using java is a waste of resources and a little bloated. Finally, because the U8SDK packaging tool is written in python, you can use python to build a web server. You can use several frameworks to build a web server in python, finally, we chose the simplest web. py framework. It is no longer appropriate for us to implement this function.
We only need to implement two functional interfaces: one is the log reporting interface and the other is the log display interface. To report logs, we use the Http Post method to display logs and use the Http Get browser for direct access.
To differentiate different levels of log display, we provide different levels of logs with different colors.
The logs reported by the client are first stored in the memory. On the webpage, we start a piece of js Code, refresh automatically every second, and keep the scroll position at the end. In this way, we can simply collect and display logs on a web server. code snippet:
import jsonimport webweb.config.debug = Falseurls = ( '/','index' )localLogs = ""class index: def GET(self): htmlFormat = "
%s <script type=\"text/javascript\">function myrefresh(){window.location.reload();window.scrollTo(0,document.body.scrollHeight);}setTimeout('myrefresh()',1000); </script>
" global localLogs localLogs = localLogs.encode('gbk') return htmlFormat % localLogs def POST(self): inputs=web.input() content = inputs.get('log') if content.startswith('{') and content.endswith('}'): content = '[' + content + ']' logs = json.loads(content) for log in logs: 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 = '
%s %s: [%s] %s
' % (color, log['time'],log['level'], log['tag'], log['msg']) stacks = log['stack'].split('\n') strLog = strLog + ('
' % color) for s in stacks: strLog = strLog + ('
%s
' % (s.strip())) strLog = strLog + '
' global localLogs localLogs = localLogs + strLog return "" if __name__ == '__main__': app = web.application(urls, globals()) app.run()
The above code is all the code of our log server. We save this file as uconsole. py, then open the command terminal and run:
python uconsole.py
In this way, the server can be started. The default listening port is 8080. If this port is in use, it will fail to be started, so we can change the port
python uconsole.py 8082
In this way, we will listen to port 8082. You can open http: localhost: 8082/and you will find that the webpage is blank, but the webpage will be automatically refreshed every second, indicating that the current server has been started and is waiting for the log to arrive.
Next, we need to design the log framework of the client to let the logs report to our log server in the form of Http Post.
We plan to integrate the original Android log (com. android. util. log) for a simple encapsulation, because we only need to add a remote Log interface, so we do not want to make it too complicated.
We define an ILog interface to extract the log interface. In this way, we will have two implementations, one LocalLog and one RemoteLog. In LocalLog, we still call the Log that comes with Android to print the logs. In RemoteLog, we put the logs in a queue and report the stored logs to the Log server at a certain interval.
ILog interface:
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();}
ULocalLog implementation:
/***** Android local Log output **/public class ULocalLog implements ILog {@ Override public void d (String tag, String msg) {Log. d (tag, msg) ;}@ Override public void I (String tag, String msg) {Log. I (tag, msg) ;}@ Override public void w (String tag, String msg) {Log. w (tag, msg) ;}@ Override public void e (String tag, String msg) {Log. e (tag, msg) ;}@ Override public void w (String tag, String msg, Throwable e) {Log. w (tag, msg, e) ;}@ Override public void e (String tag, String msg, Throwable e) {Log. e (tag, msg, e) ;}@ Override public void destory (){}}
URemoteLog implementation:
public class URemoteLog implements ILog{ private URemoteLogPrinter printer; public URemoteLog(String url, int interval){ printer = new URemoteLogPrinter(url, 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, e)); } @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, e)); } @Override public void destory() { printer.stop(); }}
In remote log implementation, we use a uremotelouplinter to temporarily store logs and regularly upload them to the log server. This implementation is as follows:
Public class uremotelouplinter {private List
Logs; private String url; private int interval = 1000; // unit: millisecond private Timer timer; private boolean running; public uremotelouplinter () {} public uremotelouplinter (String remoteUrl, int interval) {this. logs = Collections. synchronizedList (new ArrayList
(); This. url = remoteUrl; this. interval = interval;} public void print (ULog log) {start (); synchronized (logs) {logs. add (log) ;}} public void printImmediate (String url, ULog log) {Map
Params = new HashMap
(); Params. put ("log", log. toJSON (); U8HttpUtils. httpPost (url, params);} public List
GetAndClear () {synchronized (logs) {List
All = new ArrayList
(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
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 ("]"); Map
Params = new HashMap
(); Params. put ("log", sb. toString (); U8HttpUtils. httpPost (url, params) ;}} catch (Exception e) {e. printStackTrace (); stop ();}}}}
In this way, we can encapsulate the entire log, but we need to set several parameters in remote logs, such as the time interval for remote printing and the address of the remote log server. These parameters are stored in meta-data in AndroidManifest. xml. At the same time, we need an interface called to call logs for the application. Therefore, we encapsulate a Log class:
Public class Log {private static Log instance = new Log (); private List
LogPrinters; private boolean isInited = false; private boolean enable = false; private String level = ULog. rochelle debug; private boolean local = true; private boolean remote = true; private int remoteInterval = 1000; private String remoteUrl = ""; private Log () {logPrinters = new ArrayList
();} Public static void d (String tag, String msg) {try {if (! ULog. rochelle debug.equalsignorecase (instance. level) {return;} for (ILog printer: instance. logPrinters) {printer. d (tag, msg) ;}} catch (Exception e) {e. printStackTrace () ;}} public static void I (String tag, String msg) {try {if (! ULog. L_DEBUG.equalsIgnoreCase (instance. level )&&! ULog. l_INFO.equalsIgnoreCase (instance. level) {return;} for (ILog printer: instance. logPrinters) {printer. I (tag, msg) ;}} catch (Exception e) {e. printStackTrace () ;}} public static void w (String tag, String msg) {try {if (ULog. l_error.w.signorecase (instance. level) {return;} for (ILog printer: instance. logPrinters) {printer. w (tag, msg) ;}} catch (Exception e) {e. printStackTrace () ;}} public stat Ic void w (String tag, String msg, Throwable e) {try {if (ULog. l_error.w.signorecase (instance. level) {return;} for (ILog printer: instance. logPrinters) {printer. w (tag, msg, e) ;}} catch (Exception e2) {e2.printStackTrace () ;}} public static void e (String tag, String msg) {try {for (ILog printer: instance. logPrinters) {printer. e (tag, msg) ;}} catch (Exception e) {e. printStackTrace () ;}} public s Tatic void e (String tag, String msg, Throwable e) {try {for (ILog printer: instance. logPrinters) {printer. e (tag, msg, e) ;}} catch (Exception e2) {e2.printStackTrace ();}} /*** call * @ param context */public static void init (Context context) {try {if (instance. isInited) {return;} instance. parseConfig (context); instance. logPrinters. clear (); if (! Instance. enable) {android. util. log. d ("ULOG", "the log is not enabled. "); return;} if (instance. local) {instance. logPrinters. add (new ULocalLog ();} if (instance. remote) {instance. logPrinters. add (new URemoteLog (instance. remoteUrl, instance. remoteInterval);} Thread. setDefaultUncaughtExceptionHandler (new UncaughtExceptionHandler () {@ Override public void uncaughtException (Thread t, final Throwable e) {New Thread (new Runnable () {@ Override public void run () {try {uremotelouplinter printer = new uremotelouplinter (); printer. printImmediate (instance. remoteUrl, new ULog (ULog. l_ERROR, "Crash", "Application Crashed !!! ", E);} catch (Exception e) {e. printStackTrace ();} finally {System. exit (0 );}}}). start (); try {Thread. sleep (500);} catch (InterruptedException e1) {e1.printStackTrace () ;}}); instance. isInited = true;} catch (Exception e) {e. printStackTrace () ;}}/*** destroy */public static void destory () {try {if (instance. logPrinters! = Null) {for (ILog printer: instance. logPrinters) {printer. destory () ;}} catch (Exception e) {e. printStackTrace () ;}} private void parseConfig (Context ctx) {try {ApplicationInfo appInfo = ctx. getPackageManager (). getApplicationInfo (ctx. getPackageName (), PackageManager. GET_META_DATA); if (appInfo! = Null & appInfo. metaData! = Null) {if (appInfo. metaData. containsKey ("ulog. enable ") {enable = appInfo. metaData. getBoolean ("ulog. enable ");} if (appInfo. metaData. containsKey ("ulog. level ") {level = appInfo. metaData. getString ("ulog. level ");} if (appInfo. metaData. containsKey ("ulog. local ") {local = appInfo. metaData. getBoolean ("ulog. local ");} if (appInfo. metaData. containsKey ("ulog. remote ") {remote = appInfo. metaData. getBoolean ("ulog. remote ");} if (appInfo. metaData. containsKey ("ulog. remote_interval ") {remoteInterval = appInfo. metaData. getInt ("ulog. remote_interval ");} if (appInfo. metaData. containsKey ("ulog. remote_url ") {remoteUrl = appInfo. metaData. getString ("ulog. remote_url ") ;}}} catch (Exception e) {e. printStackTrace ();}}}
In addition to several log printing interfaces, we have added two interfaces: init and destroy. Before calling Log printing, we need to call the init Method for initialization, because we need to read the configuration parameters and set the corresponding parameters according to the configuration. Similarly, when the application exits, we need to call destroy to recycle resources.
In general, init can be called in onCreate or attachBaseContext of Application; destroy can be called in onTerminate of Application
After everything is ready, we also need to add several log configuration parameters in AndroidManifest. xml of the application:
<code class=" hljs xml"> <meta-data android:name="ulog.enable" android:value="true"> <!--{cke_protected}{C}%3C!%2D%2D%E6%98%AF%E5%90%A6%E5%BC%80%E5%90%AF%E6%97%A5%E5%BF%97%EF%BC%8C%E5%85%B3%E9%97%AD%E4%B9%8B%E5%90%8E%EF%BC%8C%E4%B8%8D%E4%BC%9A%E8%BE%93%E5%87%BA%E5%88%B0logcat%E4%B9%9F%E4%B8%8D%E4%BC%9A%E8%BE%93%E5%87%BA%E5%88%B0%E8%BF%9C%E7%A8%8B%2D%2D%3E--> <meta-data android:name="ulog.level" android:value="DEBUG"> <!--{cke_protected}{C}%3C!%2D%2D%E6%97%A5%E5%BF%97%E7%BA%A7%E5%88%AB(DEBUG%7CINFO%7CWARNING%7CERROR)%2D%2D%3E--> <meta-data android:name="ulog.local" android:value="true"> <!--{cke_protected}{C}%3C!%2D%2D%E6%98%AF%E5%90%A6%E5%9C%A8logcat%E4%B8%AD%E6%89%93%E5%8D%B0%2D%2D%3E--> <meta-data android:name="ulog.remote" android:value="true"> <!--{cke_protected}{C}%3C!%2D%2D%E6%98%AF%E5%90%A6%E8%BF%9C%E7%A8%8B%E6%89%93%E5%8D%B0%2D%2D%3E--> <meta-data android:name="ulog.remote_interval" android:value="500"> <!--{cke_protected}{C}%3C!%2D%2D%E8%BF%9C%E7%A8%8B%E6%89%93%E5%8D%B0%E6%97%B6%EF%BC%8C%E6%97%A5%E5%BF%97%E4%B8%8A%E6%8A%A5%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E6%AF%AB%E7%A7%92%2D%2D%3E--> <meta-data android:name="ulog.remote_url" android:value="http://192.168.18.9:8080/"> <!--{cke_protected}{C}%3C!%2D%2D%E8%BF%9C%E7%A8%8B%E6%97%A5%E5%BF%97%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9C%B0%E5%9D%80%EF%BC%8C%E5%B0%B1%E6%98%AFuconsole%E7%9B%91%E5%90%AC%E7%9A%84%E5%9C%B0%E5%9D%80%2D%2D%3E--></meta-data></meta-data></meta-data></meta-data></meta-data></meta-data></code>