9 minutes read

Errors in programming happen very often. Programs crash all the time. No matter how good your team of developers and testers are, there are use cases (ways in which a user interacts with the app) that the team didn't think of, and these use cases may cause your program to behave differently.

From a user's perspective, how many programs have crashed while you were using them? Have you seen the messages that usually pop up after the crash and ask for your permission to send the crash report (a log file) to the development team responsible for this software? In this topic, we will discuss what logging is, what it's used for, and how to use it in the Android framework.

What is logging

Logging is the process of recording various events while your application is running. This is done by outputting messages to a specific destination (databases, files, consoles, etc). These messages are intended to show the status of your app, what the user is doing at this moment, and how different components in your application are interacting internally. It is up to you as a developer to log what you expect to be useful while debugging or monitoring your application.

Why do logging in your apps

When your application crashes while you are testing it, you know exactly what caused it to crash. But when someone else is using your app and it crashes, it's out of your sight what they were doing, and what events led up to this crash.

Sometimes, an error can be obvious: once you see the stack trace for it, you will know the solution at once. But there are times when you try to identify the mysterious use case that caused your application to crash at a specific point, and looking only at the stack trace is not enough. In all situations, finding the use case (what exactly the user was doing) would be helpful for you and would let you address the problem in a more efficient way.

Also, what if there was a logic error? For example, you click on a button to perform a specific task, but your program does not execute the task fully or does nothing at all! An easy and fast way to debug your app is to print messages between your function steps to understand what part of the function has run and what part has been ignored. Instead of printing messages, a more professional and more optimized way is to use logging.

Logging APIs in Java

There are different APIs you can use in order to log the information you need. They differ in flexibility, speed, and the capabilities they provide. The logging APIs provided by the Java standard library are included in the java.util.logging package. However, most developers prefer to work with third-party libraries and frameworks like Logback, log4j, reload4j, or Apache Commons Logging. It is up to you to choose whatever suits your needs. Just so you know, an even more popular choice a lot of developers go for these days is SLF4J. SLF4J is not an implementation of a logging framework, however, it provides an abstraction layer that lets you interact with most logging frameworks written for Java. This means that you can migrate to any logging framework with minimal work, just by adding the dependency of the logging framework you want to use.

When it comes to the Android framework, its standard logging APIs are provided in its android.util.Log class, which would be sufficient for most of your applications unless you are opting for complex projects and prefer to go for third parties.

Logcat and android.util.Log

Logcat is the standard destination where all logs in the Android framework are output. Logs from all the running applications on your Android phone can be found in Logcat, in addition to logs from the Android system itself, such as when garbage collection starts.

To display the log messages for your app:

  1. Build and run your app.
  2. Click View > Tool Windows > Logcat. When the Logcat window opens, you will find your app selected in the window so that Logcat would filter out all other logs coming from other running apps.
  3. To further filter your app logs, you can use the search field to find specific logs that you are expecting. This can be done by always providing specific string values to your logging calls, often called TAGs.
private const val TAG = "MyActivity"

Log.v(TAG, "Download started")           // For verbose level logging:
                                         // for recording as much information as 
                                         // you want. You may need it or you may not —  
                                         // this is why it is called verbose.

private var n = 0                        // For some operations using the n variable.
Log.d(TAG, "Current value of n: $n")     // For debug level logging: 
                                         // to debug and test while 
                                         // developing your app.

Log.i(TAG, "Submit button was clicked")  // For info level logging: 
                                         // to collect the actions of the users. 

Log.w(TAG, "Object x is changing.")      // For warning level logging:
                                         // to highlight potential risks so that 
                                         // you may address them.

Log.e(TAG, "An error due to …")          // For error level logging: 
                                         // to log exceptions and errors.  
                                         // Usually used with try-catch blocks.

Logging levels determine how important a certain log type is. In the code snippet above, the least important level is at the top, going all the way down to the most important level at the bottom (Verbose < Debug < Info < Warning < Error).

There is an overloaded version of all the methods mentioned above. This overloaded method takes a third parameter of the Throwable type, which means you can use any method to log errors and exceptions as well:

} catch (e: IOException) {
    Log.e(TAG, "Cannot satisfy the request", e)
}

System.out and System.err in Android

You may wonder what would happen if you tried Java's System.out.println() and System.err.println() or their Kotlin counterparts. To begin with, System.err.println() has no counterparts in the Kotlin world. You have to call it directly by yourself.

While working with the Android framework, if configured properly, System.out and System.err will be a tool for communicating and printing to Logcat. So, System.out and System.err are just logging calls in Android, but they are limited to logging only on the info and the warning levels respectively, with fixed TAG values that you cannot change:

System.out.println("message!")  //identical
Log.i("System.out", "message!") //identical


System.err.println("message!")  //identical
Log.w("System.err", "message!") //identical

See also: Timber

Timber is a lightweight extensible Android library that provides logging APIs by building on top of the default android.util.Log class. In the development stage, your app would often contain many logging statements for different logging levels. Once your app is ready for release, many of these logging statements are no longer needed, like the verbose and the debug levels. Moreover, they are supposed to be removed: otherwise, they may cause latency in your app while it is running on users' devices. Timber makes disabling logging statements a lot easier without having to delete each logging statement by yourself. Timber also makes the whole logging experience less tiring by writing the TAGs automatically in your logging statements.

Conclusion

Logging is an important practice that is very helpful both while you are developing and debugging your application and after you are finished working on it. Logging is all about keeping a record of all that is going on in your application internally and externally in order to monitor it and watch out for the events that caused your application to behave differently or crash. There are various third-party logging frameworks in Java that developers prefer to use instead of the APIs provided by the Java standard library as they are faster, more efficient, and flexible. In Android, the standard logging class (android.util.Log) would be sufficient most of the time. It logs messages to Logcat and provides different levels of logging to indicate the importance of the logged data.

39 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo