dynamically choose log4j appender depending on the parameter received - java

I have log4j appenders (appendersB, appendersC) and a specific class like below
class A { A{Parent a}}
where Parent is an interface with two implementations as below
class B implements Parent {..}
class C implements Parent {..}
now I want to tell log4j that whenever class A is instantiated with paramater B it should use appendersB and if it receives C then it should use appendersC.
Is this possible?

Choice of appender is done in the Log4j configuration file, based on the logger name, so to be able to configure it so logging entries go to different appenders, your code should use different loggers.
Normally, the logger name is the fully qualified class name, and the logger is created as a static field of the class. This is done for performance, low memory footprint, and convenience of naming.
You can however make the logger field non-static, and assign it with a dynamically generated name.
As an example, your A class would normally do this:
package org.example;
public class A {
private static final Logger log = LogManager.getLogger(A.class);
// rest of code
}
This will create a logger named org.example.A, which can be configured to write any any appender(s) of choice.
To base logger, and hence potentially the appender, on the actual class of object given as parameter to constructor, you could do this:
package org.example;
public class A {
private final Logger log; // not static
public A(Parent p) {
this.log = LogManager.getLogger(getClass().getName() + "." +
p.getClass().getSimpleName());
}
}
This will create a logger for each instance of A, and the name derives from constructor parameter, e.g.
new A(new B()) // Logger name: org.example.A.B
new A(new C()) // Logger name: org.example.A.C
You can now configure Log4j to direct logger org.example.A.B to appenderB, and logger org.example.A.C to appenderC.
You can of course build the dynamic logger name any way you want to. Logger names don't have to be based on class names, e.g. you could name them bravo.foo and charlie.foo.

Related

How to get loggers in java.util.logging so i can change their log level at runtime?

I am using java.util.logging in different classes. The Log Level's default value is INFO. For example in one class Class1 it is setup this way:
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Class1 {
private static final Logger LOGGER = Logger.getLogger(Class1.class.getName());
static {
LOGGER.setLevel(Level.INFO);
for (Handler handler : LOGGER.getHandlers()) {
handler.setLevel(Level.INFO);
}
}
...
}
The above is the same way it is setup in different classes.
Now the Log Level could be changed at runtime. For example suppose it is changed to FINEST at runtime. In this case I want to get all the loggers which have been created so far and then change their Log Level to FINEST. How can I do that? I was thinking about creating another class say LogRepository which has a java.util.List and whenever a LOGGER is created, I add it into the java.util.List of LogRepository. But I think there may be another better way.
I believe this is just a matter of setting the level of the parent logger that all of the instances inherit from.
Logger.getLogger("").setLevel(Level.FINEST);

log class and method name in Log4j logger

I need to get class and method names to Log4j logger. What is the best way of doing that? Also I would like to create one instance of logger for whole application - it is quite boring to declare logger in each class. What is the way to solve these problems.
You can use %C and %M placeholders in your PatternLayout. Please be advised, their use is not recommended for performance reasons.
There are several ideas on how to avoid declaring loggers for each class. For example, if creating a common base class is a viable option, you can declare a protected final logger like this:
abstract class Base {
protected final Logger logger = Logger.getLogger(getClass());
}
class Concrete extends Base {
public void testLogger() {
logger.info("It works!");
}
}
Or, you may try injecting loggers with a dependency injection framework such as Weld.
Actually, creating one Logger for whole application, is not a good idea at all. It's easy to mess up loggers, levels of logging, configuration, etc.
But, if you still want it, you could create a logger instance in main method, and pass it for each of the class (e.g. using setLogger() in every class you use or passing as argument to class constructor). But again, is 100% bad idea to do this.
make your Logger Instance static
Every class has its own Logger => Logger.getLogger(xyz.class)
Use 2. than you can configure log4j

How to inject logger using Google Guice

Usually I define logger like this:
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
But when using #Inject we must use non-static and non-final field, like:
#Inject
private Logger logger;
i.e. logger will be created in each instance of this class, also logger is mutable. May be exist some way to make logger static? Also how I can bind logger to certain class (I use send the class object when creating logger object from factory LoggerFactory.getLogger(MyClass.class);, how to create logger in same way using injecting ? )?
Please check the Custom Injections on Guice wiki, there is a complete Log4J example.
EDIT: You can use either a static field or a final field for your logger, but not a static final one. This is a Java limitation.
Also be wary that:
Injecting final fields is not recommended because the injected value may not be visible to other threads.
Haven't tested that but the code in the article should work fine for static fields, although you could improve it by getting rid of MembersInjector and doing all of it in the TypeListener (since a static field needs to be set only once).
Using requestStaticInjection() will force you to list all your classes in a module file - not a good idea, as you will soon forget to add one.
OTOH if you just want to support JUL you might be better of using the built-in support (as mentioned by Jeff, I assumed you didn't want a general answer, since you didn't mention JUL specifically in your question).
When designing your application for dependency injection, typically the best practice is to avoid static fields and methods as much as possible. This is so that your dependencies are clearer, and so it's easier to replace your real dependencies with other instances during tests and as your application evolves. The ideal behavior, therefore, is to avoid static methods and fields (including loggers) as much as possible.
Guice, however, does allow for marking fields static, and requesting injection of static fields when the Injector is created. The fields will need to remain mutable--final doesn't mean "final except for Guice".
public class YourClass {
#Inject static Logger logger;
/* ... */
}
public class YourModule extends AbstractModule {
#Override public void configure() {
/* YourClass.logger will work once you create your Injector. */
requestStaticInjection(YourClass.class);
}
}
Guice automatically provides java.util.logger.Logger instances for you with the class name embedded into it, but that's only because of a special case coded into Guice. If you want a special logger, as in this SO question, you'll need to investigate the Custom Injections to which Jakub linked--but if the whole goal is to centralize logger creation so you can control it in one place, you can just refactor that into a static factory outside of Guice too.
#LogToFile
public class YourClass {
private static final Logger logger = YourLoggerFactory.create(YourClass.class);
/* ... */
}
public class YourLoggerFactory {
private YourLoggerFactory { /* do not instantiate */ }
public Logger create(Class<?> clazz) {
if (clazz.getAnnotation(LogToFile.class) != null) {
return someImplementation(new File(...));
} else {
return someOtherImplementation();
}
}
}

Calling log4j logger in a java "Generic Class"

When trying to add log4j logger to a Generic class, it does not generate logs.
This is the code used.
public class GenericClass<K extends Serializable, V extends Serializable> extends ... {
public static Logger log = Logger.getLogger(GenericClass.class.getName());
I am guessing the reason this code fails is because the class is not instantiated till runtime and the plain name that i have given no more resolves to the newly generated class.
Is there a way that generic classes can be logged at all?
Generics have nothing to do with this.
At runtime, a logger by the name of x.y.z.GenericClass (x.y.z being the package in which GenericClass is located). If nothing is logged, then it's either because your application isn't logging anything, or your Log4J configuration is faulty.
To decide which one is true, add -Dlog4j.debug=true to your server initialization parameters and give it a try.

Logger related question in java

From java.util.logging.Logger:
Logger names can be arbitrary strings, but they should normally be based on the package name or class name of the logged component, such as java.net or javax.swing
Could anyone explain this sentence to me ?
Logger names can be arbitrary strings, but they should normally be based on the package name or class name of the logged component, such as java.net or javax.swing"
"Logger names can be arbitrary strings ...":
public class Foo {
private static final Logger logger = Logger.getLogger("specify logger name here - you can use any logger name you want, even supercalifragilisticexpialidocious");
}
Any code that wants the same logger instance just has to specify the same logger name (good luck spelling "supercalifragilisticexpialidocious" the same way twice).
However, you might want to share loggers more easily, get a handle to a specific class' logger for configuration, or have a hierachical relationship between loggers (e.g. organize loggers to into parents and children). Classes and packages are already organized hierarchically, so they recommend that logger names:
"... should normally be based on the package name or class name of the logged component ..."
package com.example.stackoverflow;
public class Foo {
private static final Logger logger = Logger.getLogger("com.example.stackoverflow.Foo");
}
Now I can easily get a handle to any logger for any class from anywhere (I just need to know its fully qualified class name). Also, now the the Logger framework can see which loggers are related to which, e.g. that the logger for "com.example.stackoverflow" is the parent of the logger for "com.example.stackoverflow.Foo".
But what if the package name changes or your class name changes? This code below does exactly the same thing as the code above, but in a less redundant and more maintainable manner:
package com.example.stackoverflow;
public class Foo {
private static final Logger logger = Logger.getLogger(Foo.class.getName());
}
Now if the package changes, the logger name is handled automatically. If the class is renamed in an IDE, the IDE will likely notice the Foo.class literal in the getLogger call above and update the Foo.class literal accordingly.
It is a way to create a hierarchical set of loggers that allows you to easily identify where each log entry came from. This is a typical scenario
public class MyClass
{
private static Logger sLog = Logger.getLogger(MyClass.class.getName());
}
I believe it relates the name you pass to the constructor when you initialise the logger. It is suggesting a structure name (string) being passed that relates to the actual class you are logging from. Generally you can get this from the class itself
For example
import java.util.logging.Logger;
...
private final static Logger LOGGER = Logger.getLogger(MyClass.class.getName());
Here, MyClass.class.getName() is returning the full class name to be used as the logger name.
A logger usually writes the "logger name" you give it, to the log file. In order to avoid having confusing logger names in one log file, each component should use a unique name. A unique name is the class name of the class doing the logging. This allows you to easily use tools later to extract the log messages for a single class, if desired.
We may think the name as "from which class or package the log is generated".

Categories