Abdullah Diab’s Blog

Log to a file on computer in Android

It’s been a long time since the last time I blogged about something, and many things have changed since then,

I graduated, got engaged and moved to Dubai.

I have plenty of things in my mind to blog about, many solutions and many tools that I developed that I’d like to share it with everyone, but I haven’t settled down, and my schedule is not stable yet, so I haven’t got enough time to sort things out and start blogging, anyway this post is simple and it doesn’t need much description so I thought I’d share it with you now.

Now let’s move to business, so I was working on an Android application and I needed to log some debug messages, normally I’d use Log.d(TAG, message); and this will send the message to logcat, but logcat is not useful for long messages, like for example if I needed to log the response I received from a web service, this response might be long and sending it to logcat will truncate it.

So usually in such situations we use files and log whatever we need inside them, but I’m working on an Android phone/emulator, and dealing with files and logging to them and reading them later from the computer will be a tedious thing to do, so I came up with a simple solution to log messages from Android to my computer.

The idea is to create a simple web server that accepts POST requests, these requests have only one parameter which is the data to be logged. I made it as simple as possible, I could have defined levels for logging and tags and other sorts of things, but I wanted a simple solution that wouldn’t take more than five minutes to write and get it working.

Simple web server

Since I’m a pythonist geek I couldn’t think of anything better than Python to implement the simple web server, and it was really great, since I didn’t have to write so much code!

I’ll be using HTTPServer and BaseHTTPRequestHandler from BaseHTTPServer module, some threading and cgi.

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

import threading
import cgi
import time

IP = '192.168.1.3'
PORT = 5500

LOG_FILE = r'C:\AndroidLogFile.log'

POST_PARAMETER = 'log'

I set the server to run on port 5500, don’t ask why I chose this number, I didn’t think of it 😛

I define a function that will execute a function in a separate thread as a daemon thread:

def startThread(fn):
    thread = threading.Thread(target=fn)
    thread.setDaemon(True)
    thread.start()
    return thread

I define the block of code to be executed when running the server:

if __name__ == '__main__':
    httpd = HTTPServer((IP, PORT), lambda *args, **kwargs: LogHandler(*args, **kwargs))
    serve_thread  = startThread(httpd.serve_forever)
    while serve_thread.isAlive():
        serve_thread.join(timeout=1)

Here I created an HTTP web server that is bound to the IP/PORT set at the beginning of the script, and I mapped the handler for this web server to a class called LogHandler.

This handler will handle the requests, and hence it’ll handle POST requests and save their contents into the specified file.

class LogHandler(BaseHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)

    def do_POST(self):
        length = int(self.headers.getheader('content-length'))
        postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1)
        f = open(LOG_FILE, 'a')
        f.write('%s:\n%s\n%s\n' % (time.strftime('%Y/%m/%d %H:%M:%S'), postvars[POST_PARAMETER][0], '*' * 20))
        f.close()

        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.send_header('Cache-Control', 'no-cache')
        self.end_headers()
        self.wfile.write('Saved!')

Pretty simple, not more than 35 lines of code!

Now let’s move to the Android/Java side.

Android/Java class

This class will be responsible for sending POST requests to the server, I’ll be using Apache classes to do the HTTP requests, and I’ll use some other classes from Java standard library, along with Android log class to log exceptions.

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

The class starts with defining some constants:

public class LogToFile {
	private static final String LOG_TO_FILE_ADDRESS = "http://192.168.1.3";
	private static final String LOG_TO_FILE_URL = "/";
	private static final String LOG_TO_FILE_LOG_PARAMETER = "log";
	private static final int LOG_TO_FILE_PORT = 5500;

Then I define a method that constructs an HTTP POST request to the specified url along with the specified parameters:

private static HttpPost Post(String url, HashMap<String, String> params, HashMap<String, ArrayList<String>> arrayParams) {
		try {
			if (!url.endsWith("/"))
				url += "/";
			List<NameValuePair> params_list = null;
			if (params != null) {
				params_list = new LinkedList<NameValuePair>();
				for (Entry<String, String> entry : params.entrySet())
					params_list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			if (arrayParams != null) {
				if (params_list == null)
					params_list = new LinkedList<NameValuePair>();
				for (Entry<String, ArrayList<String>> entry : arrayParams.entrySet())
					for (String value : entry.getValue())
						params_list.add(new BasicNameValuePair(entry.getKey(), value));
			}
			HttpPost request = new HttpPost(url);
			if (params != null)
				request.setEntity(new UrlEncodedFormEntity(params_list, "utf-8"));
			return request;
		} catch (Exception e) {
			Log.e("", e.getClass().getName() + "\n" + e.getMessage());
		}
		return null;
	}

I know this is a lot of code for such thing, but this method is used elsewhere in my application and that’s why it had to be this generic.

Another method will be used to execute the request, also it is generic to be used elsewhere, you can edit the code and make it more specific to your needs.

private static HttpResponse ExecuteRequest(HttpUriRequest request) {
		try {
			HttpClient httpclient = new DefaultHttpClient();
			HttpResponse response = httpclient.execute(request);
			return response;
		} catch (Exception e) {
			Log.e("", e.getClass().getName() + "\n" + e.getMessage());
		}
		return null;
	}

And now to the final piece of code here, this is the method that you’ll call from anywhere in your application to log messages to the file:

public static void LogToFile(String log) {
		try {
			HashMap<String, String> params = new HashMap<String, String>();
			params.put(LOG_TO_FILE_LOG_PARAMETER, log);
			HttpPost request = Post(String.format("%s:%d%s", LOG_TO_FILE_ADDRESS, LOG_TO_FILE_PORT, LOG_TO_FILE_URL), params, null);
			HttpResponse response = ExecuteRequest(request);
			response.getEntity().consumeContent();
		} catch (Exception e) {
			Log.e("", e.getClass().getName() + "\n" + e.getMessage());
		}
	}

Download

The source code is licensed under the GNU Public License (GPL).

Project on GitHub

You can download the source code from here:

Source code from GitHub