Wednesday, September 26, 2012

Logging in Android


Logging in Android
An important step in any application development - is logging. There are no serious projects that have been written without at least one bug. The log is used to keep track of all the moving and changing objects in the application. For example: requests and responses from the server, the values ​​of arrays and collections, the output error handlers, and more. Consider how to implement logging into the application so that it is comfortable.

In Android there is a class for working c logs: android.util.Log. Which we are going to use to display the information in LogCat DDMS. But we modify it a bit for easier use.
First, every time when we print some information in the log, spent little time. If the output of the log very often, this time can affect application performance. Therefore, before the publication of the application, you need to disable all of our logs. And if a lot of them? In this article we consider a class that allows you to disable all logging by changing just one parameter.
Second, the publication of the application is not possible if the application manifest parameter has set android:debuggable = "true", which we used to keep track of any part of the code in an application. In this article I will give an example, how depending on this option to display or not display the information in the log.
Third, it is sometimes necessary to override the output log is not in the console, and the other output. On the example in this article we will try to develop an application that will display our log to a file on SD-card.


Development


Create a new class for logging: LogSystem, one attribute of the class that will be responsible for the ability to output information to the log.
public final class LogSystem {
 private static boolean mLoggingEnabled = true;
private LogSystem() {
  
 }
 
 public static void setDebugLogging(boolean enabled) {
  mLoggingEnabled = enabled;
 }
}
Override all the output methods of the Log class:
public static int v(String tag, String msg) {
        int result = 0;
        if (mLoggingEnabled) {
         result = Log.v(tag, msg);
        }
        return result;
    }

 public static int v(String tag, String msg, Throwable tr) {
    int result = 0;
       if (mLoggingEnabled) {
        result = Log.v(tag, msg, tr);
       }
       return result;    
 }

     public static int d(String tag, String msg) {
      int result = 0;
         if (mLoggingEnabled) {
          result = Log.d(tag, msg);
         }
         return result;
    }

    public static int d(String tag, String msg, Throwable tr) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.d(tag, msg, tr);
        }
        return result;
    }

    public static int i(String tag, String msg) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.i(tag, msg);
        }
        return result;
    }

    public static int i(String tag, String msg, Throwable tr) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.i(tag, msg, tr);
        }
        return result;
    }

    public static int w(String tag, String msg) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.w(tag, msg);
        }
        return result;
    }

    public static int w(String tag, String msg, Throwable tr) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.w(tag, msg, tr);
        }
        return result;
    }

    public static int w(String tag, Throwable tr) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.w(tag, tr);
        }
        return result;
    }

    public static int e(String tag, String msg) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.e(tag, msg);
        }
        return result;
    }

    public static int e(String tag, String msg, Throwable tr) {
     int result = 0;
        if (mLoggingEnabled) {
         result = Log.e(tag, msg, tr);
        }
        return result;
    }
We have developed a a managed log. mLoggingEnabled attribute instructs whether to display a message or not.
In order to use our class, you just call any corresponding static method.
Consider the following example. We want to track the entire application life cycle.
public class MainActivity extends Activity {
    public static final String TAG = MainActivity.class.getSimpleName();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LogSystem.i(TAG, "onCreate()");
}

 @Override
 protected void onDestroy() {
  super.onDestroy();
  LogSystem.i(TAG, "onDestroy()");
 }

 @Override
 protected void onPause() {
  super.onPause();
  LogSystem.i(TAG, "onPause()");
 }

 @Override
 protected void onResume() {
  super.onResume();
  LogSystem.i(TAG, "onResume()");
 }

 @Override
 protected void onStart() {
  super.onStart();
  LogSystem.i(TAG, "onStart()");
 }

 @Override
 protected void onStop() {
  super.onStop();
  LogSystem.i(TAG, "onStop()");
 } 
    
}



Debuggable


In order to print or not to print the information in the log, depending on the value of attribute android:debuggable, necessary in the method onCreate() in the main Activity write:
boolean isDebuggable = (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));        
LogSystem.setDebugLogging(isDebuggable);


Log to file


For logging to a file on SD-card, you first need to add permission to the application manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Now the LogSystem class will modify, so we can show the log to a file.
private static final String PATH = "sdcard/log.dat";
public static int logInFile(String tag, String msg)
 {
  int result = 0;
  File file = new File(PATH);
  try {
   if (!file.exists()) {

    file.createNewFile();

   }
   String timeLog = new SimpleDateFormat("dd.MM.yy hh:mm:ss").format(new Date());
   BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
   bw.append(timeLog+" (" + tag + ")\t" + msg + "\n");
   bw.close();
   result = 1;
  } catch (IOException e) {
   e.printStackTrace();
  }
  return result;
 }

We have added a static attribute to specify the path to our log on SD-card. And developed a method to store the information in the file.
Upgrade our application with the new changes. Replace LogSystem.i(TAG, "onCreate()"); в методе onCreate() our Activity on LogSystem.logInFile(TAG, " onCreate ()");
Example record in the log will be the next:
19.09.12 14:12:01 (MainActivity)              onCreate ()


Possible improvements


  • Another way to output to a file 
If we want to use static methods v(), d(), e(), i(), w()to output directly to a file, you can modify the class LogSystem follows (the example of the method e()):
public static int logInFile(int priority, String tag, String msg)
 {
  int result = 0;
  File file = new File(PATH);
  try {
   if (!file.exists()) {

    file.createNewFile();

   }
   String timeLog = new SimpleDateFormat("dd.MM.yy hh:mm:ss").format(new Date());
   BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
   bw.append(priority + "\t" + timeLog + " (" + tag + ")\t" + msg + "\n");
   bw.close();
   result = 1;
  } catch (IOException e) {
   e.printStackTrace();
  }
  return result;
 }
public static int e(String tag, String msg) {
     int result = 0;
        if (mLoggingEnabled) {
         result = logInFile(Log.ERROR, tag, msg);
        }
        return result;
    }
  • Improvement output methods
It is also possible to improve the output methods, for example, add the parameterint placeForLog, which would show where the log output
public static int e(String tag, String msg, int placeForLog) {
     int result = 0;
        if (mLoggingEnabled) {
  switch (placeForLog) {
   case 0: 
           result = logInFile(Log.ERROR, tag, msg);
    break;
   case 1:
    result = Log.e(tag, msg);
    break;
        }
        return result;
    }


Links

The source codes of this project can be downloaded here: zip