Java logger that automatically determines caller's class name - java
public static Logger getLogger() {
final Throwable t = new Throwable();
final StackTraceElement methodCaller = t.getStackTrace()[1];
final Logger logger = Logger.getLogger(methodCaller.getClassName());
logger.setLevel(ResourceManager.LOGLEVEL);
return logger;
}
This method would return a logger that knows the class it's logging for.
Any ideas against it?
Many years later: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.java
The MethodHandles class (as of Java 7) includes a Lookup class that, from a static context, can find and return the name of the current class. Consider the following example:
import java.lang.invoke.MethodHandles;
public class Main {
private static final Class clazz = MethodHandles.lookup().lookupClass();
private static final String CLASSNAME = clazz.getSimpleName();
public static void main( String args[] ) {
System.out.println( CLASSNAME );
}
}
When run this produces:
Main
For a logger, you could use:
private static Logger LOGGER =
Logger.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
Creating a stack trace is a relatively slow operation. Your caller already knows what class and method it is in, so the effort is wasted. This aspect of your solution is inefficient.
Even if you use static class information, you should not fetch the Logger again for each message. From the author of Log4j,Ceki Gülcü:
The most common error in wrapper classes is the invocation of the Logger.getLogger method on each log request. This is guaranteed to wreak havoc on your application's performance. Really!!!
This is the conventional, efficient idiom for getting a Logger is during class initialization:
private static final Logger log = Logger.getLogger(MyClass.class);
Note that this gives you a separate Logger for each type in a hierarchy. If you come up with a method that invokes getClass() on an instance, you will see messages logged by a base type showing up under the subtype's logger. Maybe this is desirable in some cases, but I find it confusing (and I tend to favor composition over inheritance anyway).
Obviously, using the dynamic type via getClass() will require you to obtain the logger at least once per instance, rather than once per class like the recommended idiom using static type information.
I guess it adds a lot of overhead for every class. Every class has to be 'looked up'. You create new Throwable objects to do that... These throwables don't come for free.
We actually have something quite similar in a LogUtils class. Yes, it's kind of icky, but the advantages are worth it as far as I'm concerned. We wanted to make sure we didn't have any overhead from it being repeatedly called though, so ours (somewhat hackily) ensures that it can ONLY be called from a static initializer context, a la:
private static final Logger LOG = LogUtils.loggerForThisClass();
It will fail if it's invoked from a normal method, or from an instance initializer (i.e. if the 'static' was left off above) to reduce the risk of performance overhead. The method is:
public static Logger loggerForThisClass() {
// We use the third stack element; second is this method, first is .getStackTrace()
StackTraceElement myCaller = Thread.currentThread().getStackTrace()[2];
Assert.equal("<clinit>", myCaller.getMethodName());
return Logger.getLogger(myCaller.getClassName());
}
Anyone who asks what advantage does this have over
= Logger.getLogger(MyClass.class);
has probably never had to deal with someone who copies and pastes that line from somewhere else and forgets to change the class name, leaving you dealing with a class which sends all its stuff to another logger.
Assuming you are keeping static refs to the loggers, here's a standalone static singleton:
public class LoggerUtils extends SecurityManager
{
public static Logger getLogger()
{
String className = new LoggerUtils().getClassName();
Logger logger = Logger.getLogger(className);
return logger;
}
private String getClassName()
{
return getClassContext()[2].getName();
}
}
Usage is nice and clean:
Logger logger = LoggerUtils.getLogger();
For every class that you use this with, you're going to have to look up the Logger anyway, so you might as well just use a static Logger in those classes.
private static final Logger logger = Logger.getLogger(MyClass.class.getName());
Then you just reference that logger when you need to do your log messages. Your method does the same thing that the static Log4J Logger does already so why reinvent the wheel?
A good alternative is to use (one of) the lombok logs annotations :
https://projectlombok.org/features/Log.html
It generate the corresponding log statement with the current class.
Then the best thing is mix of two .
public class LoggerUtil {
public static Level level=Level.ALL;
public static java.util.logging.Logger getLogger() {
final Throwable t = new Throwable();
final StackTraceElement methodCaller = t.getStackTrace()[1];
final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(methodCaller.getClassName());
logger.setLevel(level);
return logger;
}
}
And then in every class:
private static final Logger LOG = LoggerUtil.getLogger();
in code :
LOG.fine("debug that !...");
You get static logger that you can just copy&paste in every class and with no overhead ...
Alaa
From reading through all the other feedback on this site, I created the following for use with Log4j:
package com.edsdev.testapp.util;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;
public class Logger extends SecurityManager {
private static ConcurrentHashMap<String, org.apache.log4j.Logger> loggerMap = new ConcurrentHashMap<String, org.apache.log4j.Logger>();
public static org.apache.log4j.Logger getLog() {
String className = new Logger().getClassName();
if (!loggerMap.containsKey(className)) {
loggerMap.put(className, org.apache.log4j.Logger.getLogger(className));
}
return loggerMap.get(className);
}
public String getClassName() {
return getClassContext()[3].getName();
}
public static void trace(Object message) {
getLog().trace(message);
}
public static void trace(Object message, Throwable t) {
getLog().trace(message, t);
}
public static boolean isTraceEnabled() {
return getLog().isTraceEnabled();
}
public static void debug(Object message) {
getLog().debug(message);
}
public static void debug(Object message, Throwable t) {
getLog().debug(message, t);
}
public static void error(Object message) {
getLog().error(message);
}
public static void error(Object message, Throwable t) {
getLog().error(message, t);
}
public static void fatal(Object message) {
getLog().fatal(message);
}
public static void fatal(Object message, Throwable t) {
getLog().fatal(message, t);
}
public static void info(Object message) {
getLog().info(message);
}
public static void info(Object message, Throwable t) {
getLog().info(message, t);
}
public static boolean isDebugEnabled() {
return getLog().isDebugEnabled();
}
public static boolean isEnabledFor(Priority level) {
return getLog().isEnabledFor(level);
}
public static boolean isInfoEnabled() {
return getLog().isInfoEnabled();
}
public static void setLevel(Level level) {
getLog().setLevel(level);
}
public static void warn(Object message) {
getLog().warn(message);
}
public static void warn(Object message, Throwable t) {
getLog().warn(message, t);
}
}
Now in your code all you need is
Logger.debug("This is a test");
or
Logger.error("Look what happened Ma!", e);
If you need more exposure to log4j methods, just delegate them from the Logger class listed above.
You could of course just use Log4J with the appropriate pattern layout:
For example, for the class name "org.apache.xyz.SomeClass", the pattern %C{1} will output "SomeClass".
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
I prefer creating a (static) Logger for each class (with it's explicit class name). I than use the logger as is.
You don't need to create a new Throwable object. You can just call
Thread.currentThread().getStackTrace()[1]
I just have the following line at the beginning of most of my classes.
private static final Logger log =
LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());
yes there is some overhead the very first time an object of that class is created, but I work mostly in webapps, so adding microseconds onto a 20 second startup isn't really a problem.
Google Flogger logging API supports this e.g.
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
See https://github.com/google/flogger for more details.
A nice way to do this from Java 7 onwards:
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
The logger can be static and that fine.
Here its using the SLF4J API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
But in principal can be used with any logging framework. If the logger needs a string argument add toString()
Simple and trivial OLD SCHOOL:
Just create your own class and pass there class name, method name + comment (if class /method changed they're refactored automatically Shift+F6)
public class MyLogs {
public static void LOG(String theClass, String theMethod, String theComment) {
Log.d("MY_TAG", "class: " + theClass + " meth : " + theMethod + " comm : " + theComment);
}
}
and just use it anywhere in the app (no context required, no initialzation, no extra libs and no look up) - can be used for any programing language!
MyLogs.LOG("MainActivity", "onCreate", "Hello world");
this will print in your console:
MY_TAG class: MainActivity meth: onCreate comm: Hello world
Why not?
public static Logger getLogger(Object o) {
final Logger logger = Logger.getLogger(o.getClass());
logger.setLevel(ResourceManager.LOGLEVEL);
return logger;
}
And then when you need a logger for a class:
getLogger(this).debug("Some log message")
This mechanism puts in a lot of extra effort at runtime.
If you use Eclipse as your IDE, consider using Log4e. This handy plugin will generate logger declarations for you using your favourite logging framework. A fraction more effort at coding time, but much less work at runtime.
Unless you really need your Logger to be static, you could use
final Logger logger = LoggerFactory.getLogger(getClass());
Please see my static getLogger() implementation (use same "sun.*" magic on JDK 7 as default java Logger doit)
note static logging methods (with static import) without ugly log property...
import static my.pakg.Logger.*;
And their speed is equivalent to native Java implementation (checked with 1 million of log traces)
package my.pkg;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;
public class Logger {
static final int CLASS_NAME = 0;
static final int METHOD_NAME = 1;
// Private method to infer the caller's class and method names
protected static String[] getClassName() {
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
boolean lookingForLogger = true;
for (int i = 0; i < depth; i++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame = access.getStackTraceElement(throwable, i);
String cname = frame.getClassName();
boolean isLoggerImpl = isLoggerImplFrame(cname);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (isLoggerImpl) {
lookingForLogger = false;
}
} else {
if (!isLoggerImpl) {
// skip reflection call
if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
// We've found the relevant frame.
return new String[] {cname, frame.getMethodName()};
}
}
}
}
return new String[] {};
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
protected static String[] getClassNameJDK5() {
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
int ix = 0;
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (isLoggerImplFrame(cname)) {
break;
}
ix++;
}
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (isLoggerImplFrame(cname)) {
// We've found the relevant frame.
return new String[] {cname, frame.getMethodName()};
}
ix++;
}
return new String[] {};
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
private static boolean isLoggerImplFrame(String cname) {
// the log record could be created for a platform logger
return (
cname.equals("my.package.Logger") ||
cname.equals("java.util.logging.Logger") ||
cname.startsWith("java.util.logging.LoggingProxyImpl") ||
cname.startsWith("sun.util.logging."));
}
protected static java.util.logging.Logger getLogger(String name) {
return java.util.logging.Logger.getLogger(name);
}
protected static boolean log(Level level, String msg, Object... args) {
return log(level, null, msg, args);
}
protected static boolean log(Level level, Throwable thrown, String msg, Object... args) {
String[] values = getClassName();
java.util.logging.Logger log = getLogger(values[CLASS_NAME]);
if (level != null && log.isLoggable(level)) {
if (msg != null) {
log.log(getRecord(level, thrown, values[CLASS_NAME], values[METHOD_NAME], msg, args));
}
return true;
}
return false;
}
protected static LogRecord getRecord(Level level, Throwable thrown, String className, String methodName, String msg, Object... args) {
LogRecord record = new LogRecord(level, format(msg, args));
record.setSourceClassName(className);
record.setSourceMethodName(methodName);
if (thrown != null) {
record.setThrown(thrown);
}
return record;
}
private static String format(String msg, Object... args) {
if (msg == null || args == null || args.length == 0) {
return msg;
} else if (msg.indexOf('%') >= 0) {
try {
return String.format(msg, args);
} catch (IllegalFormatException esc) {
// none
}
} else if (msg.indexOf('{') >= 0) {
try {
return MessageFormat.format(msg, args);
} catch (IllegalArgumentException exc) {
// none
}
}
if (args.length == 1) {
Object param = args[0];
if (param != null && param.getClass().isArray()) {
return msg + Arrays.toString((Object[]) param);
} else if (param instanceof Throwable){
return msg;
} else {
return msg + param;
}
} else {
return msg + Arrays.toString(args);
}
}
public static void severe(String msg, Object... args) {
log(Level.SEVERE, msg, args);
}
public static void warning(String msg, Object... args) {
log(Level.WARNING, msg, args);
}
public static void info(Throwable thrown, String format, Object... args) {
log(Level.INFO, thrown, format, args);
}
public static void warning(Throwable thrown, String format, Object... args) {
log(Level.WARNING, thrown, format, args);
}
public static void warning(Throwable thrown) {
log(Level.WARNING, thrown, thrown.getMessage());
}
public static void severe(Throwable thrown, String format, Object... args) {
log(Level.SEVERE, thrown, format, args);
}
public static void severe(Throwable thrown) {
log(Level.SEVERE, thrown, thrown.getMessage());
}
public static void info(String msg, Object... args) {
log(Level.INFO, msg, args);
}
public static void fine(String msg, Object... args) {
log(Level.FINE, msg, args);
}
public static void finer(String msg, Object... args) {
log(Level.FINER, msg, args);
}
public static void finest(String msg, Object... args) {
log(Level.FINEST, msg, args);
}
public static boolean isLoggableFinest() {
return isLoggable(Level.FINEST);
}
public static boolean isLoggableFiner() {
return isLoggable(Level.FINER);
}
public static boolean isLoggableFine() {
return isLoggable(Level.FINE);
}
public static boolean isLoggableInfo() {
return isLoggable(Level.INFO);
}
public static boolean isLoggableWarning() {
return isLoggable(Level.WARNING);
}
public static boolean isLoggableSevere() {
return isLoggable(Level.SEVERE);
}
private static boolean isLoggable(Level level) {
return log(level, null);
}
}
Take a look at Logger class from jcabi-log. It does exactly what you're looking for, providing a collection of static methods. You don't need to embed loggers into classes any more:
import com.jcabi.log.Logger;
class Foo {
public void bar() {
Logger.info(this, "doing something...");
}
}
Logger sends all logs to SLF4J, which you can redirect to any other logging facility, in runtime.
Related
Bytebuddy - Intercept java.lang.RuntimeException constructor
Iam trying to intercept the constructor RuntimeException(String). Iam trying to use Advice as mentioned here and shown here. But the methods onEnter(String message) or onExit(String message). My instrumenting class (inside a different jar): public class Instrumenting { private static final String CLASS_NAME = "java.lang.RuntimeException"; public static void instrument(Instrumentation instrumentation) throws Exception { System.out.println("[Instrumenting] starting to instrument '" + CLASS_NAME + "'"); instrumentation.appendToBootstrapClassLoaderSearch(new JarFile("C:\\Users\\Moritz\\Instrumenting\\dist\\Instrumenting.jar")); File temp = Files.createTempDirectory("tmp").toFile(); ClassInjector.UsingInstrumentation.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation).inject(Collections.singletonMap( new TypeDescription.ForLoadedType(RuntimeExceptionIntercept.class), ClassFileLocator.ForClassLoader.read(RuntimeExceptionIntercept.class))); new AgentBuilder.Default() .ignore(ElementMatchers.none()) .with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(instrumentation, temp)) .type(ElementMatchers.named(CLASS_NAME)) .transform((DynamicType.Builder<?> builder, TypeDescription td, ClassLoader cl, JavaModule jm) -> builder .visit(Advice.to(RuntimeExceptionIntercept.class) .on(ElementMatchers.isConstructor()) ) ).installOn(instrumentation); System.out.println("[Instrumenting] done"); } public static class RuntimeExceptionIntercept { #Advice.OnMethodEnter public static void onEnter(String message) throws Exception { System.err.println("onEnter: " + message); } #Advice.OnMethodExit public static void onExit(String message) throws Exception { System.err.println("onExit: " + message); } } } How its called: public class Main { public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { Instrumenting.instrument(instrumentation); } public static void main(String[] args) throws MalformedURLException, IOException { new RuntimeException("message"); } } Output: [Instrumenting] starting to instrument 'java.lang.RuntimeException' [Instrumenting] done What am I doing wrong?
The class is already loaded when your agent is running and you have not specified for example RedefinitionStrategy.RETRANSFORM. Therefore, your agent will not reconsider already loaded classes. Note that you should set a more specific ignore matcher then that. By the way, advice is inlined in the target class, your injection is not required.
NullpointerException in enum Logger
I have a class ValueRepository class and want to use a logger in it. The following code always throws a NullPointerException. public enum ValueRepository { INSTANCE; private static final Logger LOGGER = LoggerFactory.getLogger(ValueRepository.class.getName()); private Map<String, Set<String>> key2value; private Map value2key; ValueRepository() { key2value = new HashMap(); value2key = new HashMap(); load("keyValue.txt"); } public int size() { return key2value.size(); } public boolean hasKey(String key) { return key2value.containsKey(key); } public boolean hasValue(String value) { return value2key.containsKey(value); } public Set<String> getValues(String key) { return key2value.get(key); } public String getKey(String value) { return (String) value2key.get(value); } private void load(String filePath) { BufferedReader br; try { br = IOUtils.fileReaderAsResource(filePath); String line = null; while ((line = br.readLine()) != null) { LOGGER.info("test"); line = line.trim(); } br.close(); } catch (IOException io) { LOGGER.error("Can't load the file: " + filePath); io.printStackTrace(); } } public static void main(String[] args) { // do nothing } Shouldn't this work? This question is very different from the link. It's specifically about why it doesn't work in logger in enum. EDIT: SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/Users/congmi/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.6.2/log4j-slf4j-impl-2.6.2.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/Users/congmi/.m2/repository/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.NullPointerException at com.qe.repository.ValueRepository.load(ValueRepository.java:56) at com.qe.repository.ValueRepository.<init>(ValueRepository.java:26) at com.qe.repository.ValueRepository.<clinit>(ValueRepository.java:14) Process finished with exit code 1
Because you designed your ValueRepository singleton as an enum you got this initialization sequence of static constants: INSTANCE (implicitly calling the constructor) LOGGER You need to change this initialization sequence to: LOGGER INSTANCE You can't reach this sequence with enum, because the Java syntax insists that the enum instances are the very first. That means, you need to implement your singleton as a normal class, not as an enum. public class ValueRepository { private static final Logger LOGGER = LoggerFactory.getLogger(ValueRepository.class.getName()); public static final ValueRepository INSTANCE = new ValueRepository(); // remaining code unchanged }
Short Answer: In your enum the static LOGGER variable is null until the constructor finishes. There are a few options for you. Make the logger an instance variable (remove static), Don't reference LOGGER during construction (throw an exception instead). Load the data into a static field in a static initializer block, rather during the constructor. Long Answer: In general, enum static fields will not resolve until the instances are built, whether or not you've got a singleton enum, or multiple enum entries. That means any time you reference a private static final field that requires another piece of code running (aka - not a string or primitive) during the instance construction, it will fail with a NullPointerException. See this example in action here import java.util.Date; public class HelloWorld{ public static void main(String []args){ System.out.println("Hello World"); final WierdEnum instance = WierdEnum.INSTANCE; instance.printStuff(); } static enum WierdEnum { INSTANCE; private static final String test = "hello"; private static final Date date = new Date(); WierdEnum() { printStuff(); } void printStuff() { System.out.println("Static string: " + test); System.out.println("Static date: " + date); } } } Resolves to: $java -Xmx128M -Xms16M HelloWorld Hello World Static string: hello Static date: null Static string: hello Static date: Mon Apr 16 18:57:27 UTC 2018 The problem is that the class loading order is causing you to ending up with null class variables during the instance construction. This is a different order for an enum than for a class I suspect you'd like to keep this an enum since you're using it as a singleton pattern and you probably have this pattern elsewhere in your code. You have a few options: 1. Change the logger to an instance rather than class variable by removing static. We don't normally use an instance variable for a logger because it would have a logger for each instance of the object in the system. In this case, there's only one! So it's no problem. private final Logger LOGGER = LoggerFactory.getLogger(ValueRepository.class); 2. OR Don't use the logger during construction. Instead of logging, throw a new exception if there is a problem reading the file. I suspect that's preferable, since it doesn't look like you can recover from a failed file read. } catch (IOException io) { throw new RuntimeException("Can't load the file: " + filePath, io); } 3. Load the data into static fields in a static initializer block. private static Map<String, Set<String>> key2value = new HashMap<>(); private static Map value2key = new HashMap(); static { load("keyValue.txt"); } private static void load(String filePath) { ...
Use reflection to create classes at runtime
I have to create a list of objects, which are configured according to the name of some classes received as input. For each object I have to call a method, which add an operation that is created dynamically. However I don't know exactly ho to resolve the problem. Please see an example below. String className; // this is an input parameter final Class<?> classType = Class.forName(className); // here I would like to use classType instead of "?" but it gives me an error. Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null))); tasks.put(task, null); task.addOperation(new Operation<classType>() { // this gives an error #Override public void onNewInput(classType input) { // this gives an error System.out.println(input) } });
As you can see from the comments, the surrounding infrastructure and the intention are not entirely clear. However, you can achieve a certain degree of type-safety with a "helper" method that captures the type of the given Task, and allows you to work with this type internally: public class RuntimeType { public static void main(String[] args) throws Exception { String className = ""; final Class<?> classType = Class.forName(className); Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null)); addOperation(task); } private static <T> void addOperation(Task<T> task) { task.addOperation(new Operation<T>() { #Override public void onNewInput(T input) { System.out.println(input); } }); } } class TaskFactory { public static Task<?> createTask(String string) { return null; } } class Task<T> { public void addOperation(Operation<T> operation) { } } interface Operation<T> { void onNewInput(T input); }
java util logging multiple handlers causing repetitive log messages
I have a situation where multiple handlers are being set into a single logger. Each Handler replaces sensitive information from being logged. Please see the below SSCCE import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; import java.util.logging.LogRecord; import java.util.logging.Logger; public class TestLogging { public static void main(String[] args) { Logger logger=Logger.getLogger(A.class.getName()); logger.addHandler(new ConsoleHandler(){ { setFormatter(new Formatter() { #Override public String format(LogRecord record) { return record.getMessage().replaceAll("method","replacing method"); } }); } }); logger.addHandler(new ConsoleHandler(){ { setFormatter(new Formatter() { #Override public String format(LogRecord record) { return record.getMessage().replaceAll("Logging","replacing logging"); } }); } }); logger.setUseParentHandlers(false); A a =new A(); a.method(); } public static class A{ private static final Logger LOGGER=Logger.getLogger(A.class.getName()); public void method(){ LOGGER.info("\nLogging from inside method"); } } } I would like the output to be "replacing logging from inside replacing method", but instead I get an output like this Logging from inside replacing method replacing logging from inside method How do I merge both these handlers into one, if I find another has already been set?
By design handlers are not supposed to be chained or merged, as their main purpose is to perform the final LogRecord handling, like writing into file. I suggest to change the approach a bit and merge formatters instead of handlers. Please take a look at the following example: public class TestLogging { public static void main(String[] args) { Logger logger = Logger.getLogger(A.class.getName()); FormatHandler handler = new FormatHandler(); logger.addHandler(handler); handler.addFormatter(new Formatter() { #Override public String format(LogRecord record) { return record.getMessage().replaceAll("method", "replacing method"); } }); handler.addFormatter(new Formatter() { #Override public String format(LogRecord record) { return record.getMessage().replaceAll("Logging", "replacing logging"); } }); logger.setUseParentHandlers(false); A a = new A(); a.method(); } public static class FormatHandler extends ConsoleHandler { private List<Formatter> formatters = new ArrayList<>(); public FormatHandler() { setFormatter(new CompositeFormatter()); } public void addFormatter(Formatter f) { formatters.add(f); } class CompositeFormatter extends Formatter { #Override public synchronized String format(LogRecord record) { String modifiedMessage; for(Formatter formatter : formatters){ modifiedMessage = formatter.format(record); record.setMessage(modifiedMessage); } return record.getMessage(); } } } public static class A { private static final Logger LOGGER = Logger.getLogger(A.class.getName()); public void method() { LOGGER.info("\nLogging from inside method"); } } } It performs the output you want.
Flexible enable/disable logging in Android app
For performance wise, some people suggest use the following method, e.g. public class MyActivity extends Activity { private static final String TAG = "MyApp"; private static final boolean D = true; #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(D) Log.e(TAG, "MyActivity.onCreate debug message"); } But this is non-senese when are working on a large project, because when you debug, you need to update many files for the debug flag, are there any better method?
You can check the DEBUG boolean in your BuildConfig: if (BuildConfig.DEBUG) { // Do what you need } Or else, you can have a debug variable, but instead or keeping it in every activity, declare it in you Application class, and check it's value whenever you need. If your purpose of that variable is for logging, is a good practice to wrap your loggings into another class, which checks the DEBUG variable: public class LogUtils { public static void LOGD(final String tag, String message) { if (BuildConfig.DEBUG) { Log.d(tag, message); } } public static void LOGV(final String tag, String message) { if (BuildConfig.DEBUG) { Log.v(tag, message); } } public static void LOGI(final String tag, String message) { if (BuildConfig.DEBUG) { Log.i(tag, message); } } public static void LOGW(final String tag, String message) { if (BuildConfig.DEBUG) { Log.w(tag, message); } } public static void LOGE(final String tag, String message) { if (BuildConfig.DEBUG) { Log.e(tag, message); } } } Then, make log calls to this class: LogUtils.LOGD(TAG, "MyActivity.onCreate debug message");
I strongly recommend what Google guys developed at their open source app iosched. Among other reasons it keeps in mind BuildConfig and checks to see whether or not a log for the specified tag is loggable at the specified level with isLoggable. It's a must for my projects. /* * Copyright 2012 Google Inc. * * 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.google.android.apps.iosched.util; import com.google.android.apps.iosched.BuildConfig; import android.util.Log; /** * Helper methods that make logging more consistent throughout the app. */ public class LogUtils { private static final String LOG_PREFIX = "iosched_"; private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length(); private static final int MAX_LOG_TAG_LENGTH = 23; public static String makeLogTag(String str) { if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) { return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1); } return LOG_PREFIX + str; } /** * WARNING: Don't use this when obfuscating class names with Proguard! */ public static String makeLogTag(Class cls) { return makeLogTag(cls.getSimpleName()); } public static void LOGD(final String tag, String message) { if (Log.isLoggable(tag, Log.DEBUG)) { Log.d(tag, message); } } public static void LOGD(final String tag, String message, Throwable cause) { if (Log.isLoggable(tag, Log.DEBUG)) { Log.d(tag, message, cause); } } public static void LOGV(final String tag, String message) { //noinspection PointlessBooleanExpression,ConstantConditions if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) { Log.v(tag, message); } } public static void LOGV(final String tag, String message, Throwable cause) { //noinspection PointlessBooleanExpression,ConstantConditions if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) { Log.v(tag, message, cause); } } public static void LOGI(final String tag, String message) { Log.i(tag, message); } public static void LOGI(final String tag, String message, Throwable cause) { Log.i(tag, message, cause); } public static void LOGW(final String tag, String message) { Log.w(tag, message); } public static void LOGW(final String tag, String message, Throwable cause) { Log.w(tag, message, cause); } public static void LOGE(final String tag, String message) { Log.e(tag, message); } public static void LOGE(final String tag, String message, Throwable cause) { Log.e(tag, message, cause); } private LogUtils() { } }
Another solution is found in one of the answers to this somewhat related question. You can override the Log class like this: public class Log { static final boolean LOG = false; public static void i(String tag, String string) { if (LOG) android.util.Log.i(tag, string); } public static void e(String tag, String string) { if (LOG) android.util.Log.e(tag, string); } public static void d(String tag, String string) { if (LOG) android.util.Log.d(tag, string); } public static void v(String tag, String string) { if (LOG) android.util.Log.v(tag, string); } public static void w(String tag, String string) { if (LOG) android.util.Log.w(tag, string); } } This way, you don't need the if statement every time you use log. Just change the boolean in your overridden Log class. When you're ready to publish, you can use a tool like ProGuard to strip all the references to Log for performance.
Strip out Log.v and Log.d messages using ProGuard An alternative approach, with less code, is to have these stripped out for the final release app using ProGuard. Basically, in the app\proguard-rules.pro file, define the methods of the android.util.Log class that you want stripped out. The following addition to the proguard-rules.pro file will cause the v (verbose) and d (debug) methods to be stripped out at build time: # This tell Proguard to assume Log.v and Log.d have no side effects (even # though they do since they write to the logs) and thus can be removed # during optimization: -assumenosideeffects class android.util.Log { public static int v(...); public static int d(...); } This avoids the need for if (BuildConfig.DEBUG)-style checks peppered throughout the code. Also see: Disable LogCat Output COMPLETELY in release Android app?
I've written a LogWrapper class which is simple and looks something like this: public class LogWrapper { private static final String DEBUG_TAG = "some-tag" private static boolean logsEnabled; public static void e(String msg) { if (logsEnabled) { Log.e(DEBUG_TAG, msg); } } // other Log methods } You can use it instead of Log class, modifying the boolean variable as you wish in one place. Hope this helps.
I had the same problem recently, and I don't think that stripping off the classes with Proguard is a good idea to disable logs. So I ended up writing a simple drop-in replacement for the standard Android Log class https://github.com/zserge/log It allows you to control the log levels. It also gives you a lot of "sugar" for logging multiple values, for log tags and even more, and it all comes with only 200 lines of code available on Maven Central/JCenter.