/*
* 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;
}
}
}