/* * Copyright 2013 Prateek Srivastava (@f2prateek) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.f2prateek.xkcd.util; import android.app.Application; import android.content.pm.ApplicationInfo; import android.util.Log; import javax.inject.Inject; /** * Originally from RoboGuice: https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/Ln.java * * A more natural android logging facility. * * WARNING: CHECK OUT COMMON PITFALLS BELOW * * Unlike {@link android.util.Log}, Log provides sensible defaults. * Debug and Verbose logging is enabled for applications that * have "android:debuggable=true" in their AndroidManifest.xml. * For apps built using SDK Tools r8 or later, this means any debug * build. Release builds built with r8 or later will have verbose * and debug log messages turned off. * * The default tag is automatically set to your app's packagename, * and the current context (eg. activity, service, application, etc) * is appended as well. You can add an additional parameter to the * tag using {@link #Log(String)}. * * Log-levels can be programatically overridden for specific instances * using {@link #Log(String, boolean, boolean)}. * * Log messages may optionally use {@link String#format(String, Object...)} * formatting, which will not be evaluated unless the log statement is output. * Additional parameters to the logging statement are treated as varrgs parameters * to {@link String#format(String, Object...)} * * Also, the current file and line is automatically appended to the tag * (this is only done if debug is enabled for performance reasons). * * COMMON PITFALLS: * * Make sure you put the exception FIRST in the call. A common * mistake is to place it last as is the android.util.Log convention, * but then it will get treated as varargs parameter. * * vararg parameters are not appended to the log message! You must * insert them into the log message using %s or another similar * format parameter * * Usage Examples: * * Ln.v("hello there"); * Ln.d("%s %s", "hello", "there"); * Ln.e( exception, "Error during some operation"); * Ln.w( exception, "Error during %s operation", "some other"); */ @SuppressWarnings({ "ImplicitArrayToString" }) public class Ln { /** * config is initially set to BaseConfig() with sensible defaults, then replaced * by BaseConfig(ContextSingleton) during guice static injection pass. */ @Inject protected static BaseConfig config = new BaseConfig(); /** * print is initially set to Print(), then replaced by guice during * static injection pass. This allows overriding where the log message is delivered to. */ @Inject protected static Print print = new Print(); private Ln() { } public static int v(Throwable t) { return config.minimumLogLevel <= Log.VERBOSE ? print.println(Log.VERBOSE, Log.getStackTraceString(t)) : 0; } public static int v(Object s1, Object... args) { if (config.minimumLogLevel > Log.VERBOSE) return 0; final String s = Strings.toString(s1); final String message = args.length > 0 ? String.format(s, args) : s; return print.println(Log.VERBOSE, message); } public static int v(Throwable throwable, Object s1, Object... args) { if (config.minimumLogLevel > Log.VERBOSE) return 0; final String s = Strings.toString(s1); final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); return print.println(Log.VERBOSE, message); } public static int d(Throwable t) { return config.minimumLogLevel <= Log.DEBUG ? print.println(Log.DEBUG, Log.getStackTraceString(t)) : 0; } public static int d(Object s1, Object... args) { if (config.minimumLogLevel > Log.DEBUG) return 0; final String s = Strings.toString(s1); final String message = args.length > 0 ? String.format(s, args) : s; return print.println(Log.DEBUG, message); } public static int d(Throwable throwable, Object s1, Object... args) { if (config.minimumLogLevel > Log.DEBUG) return 0; final String s = Strings.toString(s1); final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); return print.println(Log.DEBUG, message); } public static int i(Throwable t) { return config.minimumLogLevel <= Log.INFO ? print.println(Log.INFO, Log.getStackTraceString(t)) : 0; } public static int i(Object s1, Object... args) { if (config.minimumLogLevel > Log.INFO) return 0; final String s = Strings.toString(s1); final String message = args.length > 0 ? String.format(s, args) : s; return print.println(Log.INFO, message); } public static int i(Throwable throwable, Object s1, Object... args) { if (config.minimumLogLevel > Log.INFO) return 0; final String s = Strings.toString(s1); final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); return print.println(Log.INFO, message); } public static int w(Throwable t) { return config.minimumLogLevel <= Log.WARN ? print.println(Log.WARN, Log.getStackTraceString(t)) : 0; } public static int w(Object s1, Object... args) { if (config.minimumLogLevel > Log.WARN) return 0; final String s = Strings.toString(s1); final String message = args.length > 0 ? String.format(s, args) : s; return print.println(Log.WARN, message); } public static int w(Throwable throwable, Object s1, Object... args) { if (config.minimumLogLevel > Log.WARN) return 0; final String s = Strings.toString(s1); final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); return print.println(Log.WARN, message); } public static int e(Throwable t) { return config.minimumLogLevel <= Log.ERROR ? print.println(Log.ERROR, Log.getStackTraceString(t)) : 0; } public static int e(Object s1, Object... args) { if (config.minimumLogLevel > Log.ERROR) return 0; final String s = Strings.toString(s1); final String message = args.length > 0 ? String.format(s, args) : s; return print.println(Log.ERROR, message); } public static int e(Throwable throwable, Object s1, Object... args) { if (config.minimumLogLevel > Log.ERROR) return 0; final String s = Strings.toString(s1); final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); return print.println(Log.ERROR, message); } public static boolean isDebugEnabled() { return config.minimumLogLevel <= Log.DEBUG; } public static boolean isVerboseEnabled() { return config.minimumLogLevel <= Log.VERBOSE; } public static Config getConfig() { return config; } public static interface Config { public int getLoggingLevel(); public void setLoggingLevel(int level); } public static class BaseConfig implements Config { protected int minimumLogLevel = Log.VERBOSE; protected String packageName = ""; protected String scope = ""; protected BaseConfig() { } @Inject public BaseConfig(Application context) { try { packageName = context.getPackageName(); final int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags; minimumLogLevel = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ? Log.VERBOSE : Log.INFO; scope = packageName.toUpperCase(); Ln.d("Configuring Logging, minimum log level is %s", logLevelToString(minimumLogLevel)); } catch (Exception e) { try { Log.e(packageName, "Error configuring logger", e); } catch (RuntimeException f) { // HACK ignore Stub! errors in mock objects during testing } } } public int getLoggingLevel() { return minimumLogLevel; } public void setLoggingLevel(int level) { minimumLogLevel = level; } } public static String logLevelToString(int loglevel) { switch (loglevel) { case Log.VERBOSE: return "VERBOSE"; case Log.DEBUG: return "DEBUG"; case Log.INFO: return "INFO"; case Log.WARN: return "WARN"; case Log.ERROR: return "ERROR"; case Log.ASSERT: return "ASSERT"; } return "UNKNOWN"; } /** Default implementation logs to android.util.Log */ public static class Print { public int println(int priority, String msg) { return Log.println(priority, getScope(5), processMessage(msg)); } protected String processMessage(String msg) { if (config.minimumLogLevel <= Log.DEBUG) { msg = String.format("%s %s", Thread.currentThread().getName(), msg); } return msg; } protected static String getScope(int skipDepth) { if (config.minimumLogLevel <= Log.DEBUG) { final StackTraceElement trace = Thread.currentThread().getStackTrace()[skipDepth]; return config.scope + "/" + trace.getFileName() + ":" + trace.getLineNumber(); } return config.scope; } } }