Android Logger Refactor - Primitive Extermination

Android Logger Refactor - Primitive Extermination

Previously in our Logger Refactor adventure, we extracted a class for logging to System.out.
As mentioned; the focus of this part will be performing a similar operation for logging to android.util.Log. (Note: This focus totally didn't happen...)

We can revisit the current state of as a reminder of what it currently looks like.

The big take away I see with this is that there's just the one logger. If we're planning on pulling new logger functionality into this... heh... Small steps. We gotta make SMALL steps. It's a hard bit to REALLY do.
What I was planning on doing was to encapsulate all the functionality of Logger into an SystemLogger inner class; then create a AndroidLogger inner class containing all that functionality; then pull out all common into the Logger class. That's what I ended up with when I did this before. I'll probably end up with it again. ...

As I'm sitting here pondering for a few moments. I'm looking at the logger and I see final int level, final int loglevel. I have 2 ints; both representing a logging level. Then below I switch on on that level to return a value in getLevelTag. Since switches are bad; let's clean that guy up before we expect to drag more code into this.

I'm going to create a new class LogLevel. Seems fitting given the fairly repetitive naming of the parameters.

public class LogLevel {}

Since our use of the log level is an int and we switch on it to get a String, let's create a constructor with those.

public LogLevel(final int level, final String tag){
    this.level = level;
    this.tag = tag;

Much like the Logger SystemOut; this is a stateless item that will be used repeatedly; a singleton makes sense.
The declaration for one of these looks like

public final static LogLevel VERBOSE = new LogLevel(Log.VERBOSE, "V");

and this will be done for each log level. These will get shown later.

Run Tests

The new classes aren't used; so it's really good that the tests still run.

To make use of the new LogLevel objects; we need to update FyzLog's log methods to pass the appropriate LogLevel into the println method.

Once we make that change it looks like

Not very exciting...
Run Tests .... DOESN'T COMPILE...
Oh, right... Then update the Logger to take this new parameter. The IDE tried to help me there.

We'll update the log level of the logging call. This makes it so we can no longer use getLevelTag to get the tag; we'll need to expose that functionality on the LogLevel.

public String tag(){
    return tag;

Now that we've updated FyzLog, we have made changes to the code; the tests should be run.
Run Tests
Now we can migrate the other logging level param for println. As we make that change in FyzLog we cause a compile error (between the '**')

private static void log(final int level, final String msgFormat, final Object... args) {
        if (**level >= logLevel** && msgFormat != null) {

We need to enable LogLevel to compare. We want to create a method to let us know if the level can log at the logLevel.
if (level.logAt(logLevel) && msgFormat != null) {

For the short term; we'll turn the switch in the log method into an if-else so we can get be compilable again.
This also requires the android log branch to pass in the LogLevel. I was hoping to hold off on that; but it's an Obvious Implementation (ish) and all sins are acceptable to get to green.

Whoa - We're having to update the test code with the change to the FyzLog#logLevel variable.
Which is actually going to cause quite a bit of updating. My tests are refactored into a streamlined form that relies on the int nature of the log levels.

We'll hold off updating the tests for the next post. For this; we didn't get our objective of extracting a AndroidLog logger; but we created the LogLevel class to encapsulate the logging levels and associated tag.

Our tests are broken since we've changed how the logging level is represented. If it was encapsulated earlier; we wouldn't have been able to write the tests that rely on the log level being an int.
The code compiles, the tests need love. We'll refactor the tests next week.

Part 1 - Part 2 - [Part 3] - Part 4 - Part 5

Show Comments