This tutorial is part of AndEngine basics.
<< List of all tutorials
If you have been working as a software developer, you probably use logging daily. But for beginning game developers, the whole concept of logging might be quite new. So what exactly is logging? - Getting Started - How to set up your environment to work with AndEngine
- Extensions and Examples - AndEngine has very little documentation but great examples
- Using Git and GitHub - Alternative way how to download AndEngine sources
- Android Logging - Effective use of Android logging
<< List of all tutorials
When you want to print a message from your program, you have usually two options. Print it on screen or print it to some log. A log is simply a list of messages. It can be later printed in a console, a file, or anywhere else.
In games, where you usually work with fullscreen graphical interface, it's not very practical to print anything on screen. You can use good-old Java System.out.println() method, but using logging is more convenient.
Android comes with a log facility called LogCat. You can view LogCat output through adb or if you are using Eclipse, go to Window -> Show View -> and search for LogCat (the one with rainbow tail is the one). If your device is connected you will immediately see some messages.
The log messages have the following attributes:
- Log Level - this is the severity of the message. It can be verbose, debug, info, warning, error or assert (a.k.a.WTF - what a terrible failure)
- Time - Timestamp when the message added
- PID - process ID of the app that printed the message
- TID - Thread ID
- Application - identified by the package name
- Tag - your custom identifier
- Text - your custom message
How to use logging in code
You can use standard Android logging or if you are using AndEngine, then it's log wrapper. Few examples:
Log.v("kulis", "This is a very detailed message");
Debug.v("This is a very detailed message");
// notice missing tag above, "AndEngine" will be used
Log.i("kulis", "Info level message");
Debug.i("kulis", "Info level message");
try {
doSomethingDangerous();
} catch (Exception e) {
// never swallow exceptions, at least print them to log!
Log.e("kulis", "Oops!", e);
Debug.e("Oops!", e);
}
Debug.setDebugLevel(DebugLevel.ERROR);
Log.i("kulis", "This message still will be printed");
Debug.i("kulis", "This will not be printed, current level is ERROR");
Log.wtf("kulis", "This should never happen");
Notice that when using the Android class Log, you have to specify tag, message and optionally a throwable object (exception). AndEngine Debug class adds methods without the tag. In that case "AndEngine" tag will be used. Also notice the method setDebugLevel - this will limit AndEngine logging to the set level and higher. AndEngine doesn't implement the wtf() method. It should be used for things that should never happen - so in case they happen, you will know ;)
Best practices
Always log exceptions. Never use emtpy catch block. If you are expecting the exception to happen, at least print a debug message that it happened - the code above might change later and you will be surprised if you don't print anything. Also try to use specific exception handling (don't simply catch Exception, but the Exception that you expect, e.g. NullPointerException) and use different messages for different types.- Verbose - used for low level debugging. Print stuff like position of your character when you are debugging why the hell it's going off screen etc.
- Debug - Messages that could be useful to determine where something went wrong. E.g. put a debug level message to each method beginning and end.
- Info - These messages can be useful in production as well. Log important events in info level, e.g. Engine created
- Warning - something not quite right, but it's not a bug. E.g. you can't connect to Facebook to submit Highscore - not nice, but you can live with it.
- Error - Log exceptions and errors that you will need to analyze later and solve.
- Assert - this is the WTF level. Simply log things that should never happen, e.g. a case in switch statement, that you expect never to be called.
Log.d("kulis", "Position: " + x + ", " + y);
Java will create a StringBuilder object, use three method calls and then throw the StringBuilder away. This can have serious impact on performace! Imagine doing this in your game loop, that is being called 60 times per second...What you should do is one of the three following options:
if (BuildConfig.DEBUG) {
Log.d("kulis", "Position: " + x + ", " + y);
}
if (MainActivity.DEBUG) {
Debug("Position: " + x + ", " + y);
}
if (Debug.getDebugLevel().isSameOrLessThan(DebugLevel.DEBUG)) {
Debug("Position: " + x + ", " + y);
}
The first option, BuildConfig.DEBUG, is Android's built-in property that should be set to false during export with production certificate. But it doesn't work very well, so I can't recommend it.The second option is your own boolean property - highly recommended, because you will probably want to use it elsewhere too.
And finally the third option is AndEngine's built-in mechanism to test for the current Debug level (see above). It's seems too complicated, but still can be useful.
When using any of these (except the first one in some cases ;)), the concatenation will not be called if the condition is false.
Also if you are using AndEngine Debug class set debug level to NONE for production to avoid printing anything.
Conclusion
Use debugging whenever you need to pass a message from you application and be careful not to use too much of the concatenation. If you need it, wrap it in the if statement. Leverage the Debug class if you are already using AndEngine.You might have noticed, that the LogCat view icon in Eclipse is droid flying with rainbow tail. This is certainly an easter egg. Another easter egg in Ice Cream Sandwich is the actual nyandroid swarm. Go to Settings -> About and start tapping on Version (that should say 4.0.x). After a while a droid dressed in ice cream sandwich appears. Tap and hold on it. Enjoy...