Last Updated: February 25, 2016
·
2.109K
· fdamilola

Cleaner Logging Interface for Android Development.

Admit it.

There's no way you have written android applications and not gotten frustrated with Log.x() being littered around in your source code.

Say you have seven (7) helper classes being used for connecting with different api endpoints. Pretty certain each one of the classes has a line that goes

Log.d(TAG_NAME, "Some message");

Now, let's say you want to push to production and find it bothersome to start poring over let's say 50 classes with at least 3 lines of that, what to do?

I recently came up with a simple but quite effective method.

First, write a class, I named mine Logger.java.

Here's a glimpse at the content :

public class Logger {

private static boolean canLog = false;

public Logger(boolean canLog){
    this.canLog = canLog;
}

public static void logString(Object s, String logdata){
    String TAG = s.getClass().getSimpleName();
    if(canLog) {
        Log.e(TAG, logdata);
    }
}
}

Note, you can add more methods with more Log levels (d, w, e ..) and the rest.

Now, inside my Application.java file,

I do this in the onCreate method.

@Override
public void onCreate() {
    super.onCreate();
    new Logger(true); //notice the true flag
}

Anywhere in the code I need to log data :

Logger.logString(FirstActivityFragment.this, "string message");

Now, let's say I want to disable logging for the app :

@Override
public void onCreate() {
    super.onCreate();
    new Logger(false); //notice the false flag toggles off logging.
}

There you go. Feel free to write more complicated solutions. Let me know when you ruzzle up stuff.
Feel free to drop comments and upvote. ;)
:D

Update

Following the comments section.
Here's an update to the Logger.java class. Courtesy of a conversation with @segunfamisa :

public class Logger {

private static String TAG = "APP_NAME";
private static boolean DEBUG_ON = false;

public enum LogState{
    D, I, W, E;
}

public static void log(LogState ls, String tag, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(tag, msg);
                break;
            case I:
                Log.i(tag, msg);
                break;
            case W:
                Log.w(tag, msg);
                break;
            case E:
                Log.e(tag, msg);
                break;
            default:
                break;
        }
    }
}

public static void log(Class<?> cls, LogState ls, String tag, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(tag, cls.getName() + " "+ msg);
                break;
            case I:
                Log.i(tag, cls.getName() + " "+ msg);
                break;
            case W:
                Log.w(tag, cls.getName() + " "+ msg);
                break;
            case E:
                Log.e(tag, cls.getName() + " "+ msg);
                break;
            default:
                break;
        }
    }
}

public static void log(Class<?> cls, LogState ls, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(TAG, cls.getName() + " "+ msg);
                break;
            case I:
                Log.i(TAG, cls.getName() + " "+ msg);
                break;
            case W:
                Log.w(TAG, cls.getName() + " "+ msg);
                break;
            case E:
                Log.e(TAG, cls.getName() + " "+ msg);
                break;
            default:
                break;
        }
    }
}
}

7 Responses
Add your response

Haha..awesome. This is very similar to what I do :) :)

public class Logger {

private static String TAG = "APP_NAME";
private static boolean DEBUG_ON = false;

public static void d(Class<?> cls, String msg){

    d(TAG, cls, msg);
}

public static void i(Class<?> cls, String msg){
    i(TAG, cls, msg);
}

public static void w(Class<?> cls, String msg){
    w(TAG, cls, msg);
}

public static void e(Class<?> cls, String msg){
    e(TAG, cls, msg);
}

public static void d(String tag, Class<?> cls, String msg){
    d(tag, cls.getName() + " " + msg);
}

public static void e(String tag, Class<?> cls, String msg){
    e(tag, cls.getName() + " " + msg);
}

public static void w(String tag, Class<?> cls, String msg){
    w(tag, cls.getName() + " " + msg);
}

public static void i(String tag, Class<?> cls, String msg){
    i(tag, cls.getName() + " " + msg);
}



public static void d(String tag, String msg){
    if(DEBUG_ON)
        Log.d(tag, msg);
}

public static void e(String tag, String msg){
    if(DEBUG_ON)
        Log.e(tag, msg);
}

public static void w(String tag, String msg){
    if(DEBUG_ON)
        Log.w(tag, msg);
}

public static void i(String tag, String msg){
    if(DEBUG_ON)
        Log.i(tag, msg);
}

}

over 1 year ago ·

@segunfamisa
Dope!!

I think this can be minimised though and made easily debuggable.
Here's what I would minimised to :

public class Logger {

private static String TAG = "APP_NAME";
private static boolean DEBUG_ON = false;

public enum LogState{
    D, I, W, E;
}

public static void log(LogState ls, String tag, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(tag, msg);
                break;
            case I:
                Log.i(tag, msg);
                break;
            case W:
                Log.w(tag, msg);
                break;
            case E:
                Log.e(tag, msg);
                break;
            default:
                break;
        }
    }
}

public static void log(Class<?> cls, LogState ls, String tag, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(tag, cls.getName() + " "+ msg);
                break;
            case I:
                Log.i(tag, cls.getName() + " "+ msg);
                break;
            case W:
                Log.w(tag, cls.getName() + " "+ msg);
                break;
            case E:
                Log.e(tag, cls.getName() + " "+ msg);
                break;
            default:
                break;
        }
    }
}

public static void log(Class<?> cls, LogState ls, String msg){
    if(DEBUG_ON){
        switch (ls){
            case D:
                Log.d(TAG, cls.getName() + " "+ msg);
                break;
            case I:
                Log.i(TAG, cls.getName() + " "+ msg);
                break;
            case W:
                Log.w(TAG, cls.getName() + " "+ msg);
                break;
            case E:
                Log.e(TAG, cls.getName() + " "+ msg);
                break;
            default:
                break;
        }
    }
}
}
over 1 year ago ·

We can do away with the excessive switch statements and make it easier to add more log states by taking advantage of some OOP thingys. Something like this:

package com.coderwall.logstate;
interface LogStateIF{
void log(String tag, String msg);
void log(Class<?> cls, String tag, String msg);
void log(Class<?> cls, String msg);
}

package com.coderwall.logstate;
class LogStateD implements LogStateIF{

    @Override
    public void log(String tag, String msg){
        Log.d(tag, msg);
    }

    @Override
    public void log(Class<?> cls, String tag, String msg){
        Log.d(tag, cls.getName() + " "+ msg);
    }

    @Override
    public void log(Class<?> cls, String msg){
        Log.d(TAG, cls.getName() + " "+ msg);
    }
}

package com.coderwall.logstate;
class LogStateW implements LogStateIF{

    @Override
    public void log(String tag, String msg){
        Log.w(tag, msg);
    }

    @Override
    public void log(Class<?> cls, String tag, String msg){
        Log.w(tag, cls.getName() + " "+ msg);
    }

    @Override
    public void log(Class<?> cls, String msg){
        Log.w(TAG, cls.getName() + " "+ msg);
    }
}

package com.coderwall.logstate;
class LogStateE implements LogStateIF{

    @Override
    public void log(String tag, String msg){
        Log.e(tag, msg);
    }

    @Override
    public void log(Class<?> cls, String tag, String msg){
        Log.e(tag, cls.getName() + " "+ msg);
    }

    @Override
    public void log(Class<?> cls, String msg){
        Log.e(TAG, cls.getName() + " "+ msg);
    }
}

package com.coderwall.logstate;
class LogStateI implements LogStateIF{

    @Override
    public void log(String tag, String msg){
        Log.i(tag, msg);
    }

    @Override
    public void log(Class<?> cls, String tag, String msg){
        Log.i(tag, cls.getName() + " "+ msg);
    }

    @Override
    public void log(Class<?> cls, String msg){
        Log.i(TAG, cls.getName() + " "+ msg);
    }
}

package com.coderwall.logstate;
public enum LogState{
    D(new LogStateD()),
    I(new LogStateI()),
    W(new LogStateW()),
    E(new LogStateE());

    private final LogStateIF implementation;
    private LogState(LogStateIF ls){
        this.implementation = ls;
    }

    public LogStateIF getImplementation(){
        return implementation;
    }
}


Sigh! Long code.. all in the name of OOP :)
Then an Adapter class that makes use of our work:

package com.coderwall.logstate;
public class LogStateAdapter{
public static void log(LogState ls, String tag, String msg){
    ls.getImplementation().log(tag, msg);
}

public static void log(Class<?> cls, LogState ls, String tag, String msg){
    ls.getImplementation().log(cls, tag, msg);
}

public static void log(Class<?> cls, LogState ls, String msg){
    ls.getImplementation().log(cls, msg);
}
}
over 1 year ago ·

Aha. Awesome :) Yes! Much shorter :)

over 1 year ago ·

@y0UngloRd remind me to almost never have to read your code. :(

over 1 year ago ·

Kikikiki... wicked laugh

over 1 year ago ·

It is worth noting that >= INFO level logs shouldn't be disabled, even in production (except of course if explicitly stated), so an all encapsulating if (DEBUG_ON) { ... } block might not exactly be an ideal solution.
If one really wishes to disable >= INFO logs, you could probably just have a setLogLevel method and handle the value appropriately in the corresponding Log.x. Ideally, all less than relevant logging should be done at <= DEBUG levels.

Meanwhile, I would suggest taking advantage of the BuildConfig.DEBUG field instead, and you automatically get a managed DEBUG switch

Personally, I would just create a wrapper class, also named Log, preserving the relevant signatures from the actual android.util.Log. That way, the only change I will be making will be the import android.util.Log lines

over 1 year ago ·