I am using final Logger LOGGER = LoggerFactory.getLogger(clazz); to get the LOGGER object of org.slf4j. I am facing difficulty in overriding any default implemented method of this class.
I am trying to override non-static method of the conrete class. Lets say, Class MyLogger { Logger LOGGER = LoggerFactory.getLogger(clazz); LOGGER.debug("Some message"); } Now debug method is the non-static method declared in the LOGGER class for which some concrete implementation has been provided. The problem here is I am seeing lots of implementation classes when I try to search for the references. So in order to override debug method what should I do
Updated the original class like this:
public class MyLogger implements Logger {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);
public static org.slf4j.Logger init(Class clazz) {
final Logger loggerOut = LoggerFactory.getLogger(clazz);
setContainerId();
LOGGER.debug("Logger is initialized for [{}].", clazz.getName());
return loggerOut;
}
public void debug(String msg, Object arg1)
{
LOGGER.debug("My message",arg1);
}
}
Still not able to get result. Please suggest what am I missing here?
Static methods cannot be overriden. If you see here the functions of LoggerFactory in documentation
Related
I want to decorate logger like below:
public class CustomLogger implements org.slf4j.Logger {
private final Logger logger;
public CustomLogger(Class clazz) {
logger = getLoggerInLogback(clazz);
}
...
}
When I call org.slf4j.LoggerFactory.getLogger(clazz), I want this method to return a instance of CustomLogger. In this way, I can add additional behavior to logger without changing code.
But how can I make the method to return a instance of CustomLogger?
I'm trying to use JMockit to test that a certain logging operation takes place:
public class LogClass1 {
public void doLog() {
Logger logger = LogManager.getLogger(LogClass1.class);
logger.info("This is a log message for {}", "arg1");
}
}
public class LogClass1Test {
#Mocked
private Logger logger;
#Tested
private LogClass1 x;
#Before
public void setup() {
x = new LogClass1();
}
#Test
public void testDoLog() {
new Expectations() {
{
logger.info("This is a log message for {}", "arg1");
}
};
x.doLog();
}
}
But this results in a "missing 1 invocation to org.apache.logging.log4j.Logger#info" error.
I've done similar mocking with log4j 1.x in the past, and I haven't had this problem. I'm wondering if there's some issue because log4j 2.x seems to have many more overloads of its info() methods.
I tried changing "arg1" to (Object)"arg1" in the unit test to see if I could get it to match the signature. This didn't help.
Any thoughts on how I can get this to work?
Note that Logger is an interface, and that LogClass1 obtains an instance of it through the LogManager.getLogger static factory method. So, obviously, it creates an instance of some Logger implementation class. And said class is not being mocked in the test.
What the test needs to do is to mock LogManager, so it returns the #Mocked Logger instance. That is, add a #Mocked LogManager field to the test class.
(Also, no need for that setup method since #Tested creates an instance automatically.)
When I try to inject my Logger producer in an enum, I get a NPE. How can I inject a Logger in an enum?
Example:
public enum MyEnum {
HI("Hi there!"),
HELLO("Hello mister!");
#Inject
private Logger log;
private final String greeting;
private MyEnum(String greeting) {
this.greeting = greeting;
// this.log = LoggerFactory.getLogger(this.getClass());
}
public String getGreeting() {
log.debug("Method getGreeting called");
return this.greeting;
}
}
This class gives me a NPE on the log.debug() line. When I remove the #Inject and uncomment the this.log line it works.
Testcase looks like this:
#RunWith(Arquillian.class)
public class CoverKindTest {
#Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.addClass(MyEnum.class)
.addClass(LoggerProducer.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testEnum() {
MyEnum myEnum = MyEnum.HI;
String greeting = myEnum.getGreeting();
assertThat("Should give the greeting.", greeting, is("Hi there!"));
}
}
Complete testable project for this question can be found here, MyEnum.class is the original question, MyEnum1.class is the solution without injection (working, but not what I am looking for) and MyEnum2.class is a suggested answer.
Edit: Updated the GitHub repo with a working solution.
https://github.com/martijnburger/how-to-inject-a-logger-in-an-enum
This direct injection won't work since enum is static.
You can either create a new logger in your enum class
private static final Logger log = Logger.getLogger(Myenum.class.getName());
Found a solution that works!
I created a helper class as follows:
public class LoggerHelper {
private static Logger logger;
private void injectLogger(#Observes #Initialized(ApplicationScoped.class) Object context,
Logger logger) {
LoggerHelper.logger = logger;
}
public static Logger getLogger() {
return logger;
}
}
Now I can inject the logger in the Enum using:
private final Logger log = LoggerHelper.getLogger();
Enums cannot be injected, as they're static.
If you still want injection (instead of just creating the Logger), then you need to create a static class inside your enum that has either setter or constructor injection. When the setter or constructor gets called by the DI framework, take the value it gives you and assign it yourself to a static value in the enum.
The enum can access it now as needed. Beware though, the value will be null if your class was not injected yet.
Something like this:
public enum MyEnum {
HI("Hi there!"),
HELLO("Hello mister!");
private static Logger log;
private final String greeting;
private MyEnum(String greeting) {
this.greeting = greeting;
}
public String getGreeting() {
log.debug("Method getGreeting called");
return this.greeting;
}
#Component
public static class InjectionHelper {
#Inject
public InjectionHelper(Logger log) {
MyEnum.log = log;
}
}
}
You can create a Singleton Service class (created by your respective DI Framework - say Spring), Inject this log inside that class and use it in your enum.
Here is a sample code which works. (Replace the Service annotation with a bean tag for this class in XML, if you're using XML way of doing it. Also you can neglect the lombok #Getter annotation and replace it with a static getter)
// Service which is actually a Utility class but have DI Managed Beans.
#Service("staticService")
public class StaticService {
#Getter
private static Logger log;
#Inject
StaticService(Logger log) {
StaticService.log = log;
}
}
Now in your corresponding Enum:
public String getGreeting() {
StaticService.getLog().debug("Method getGreeting called");
return this.greeting;
}
I used a similar pattern for one of my classes (In Spring) and the injection worked.
Logic:
We first create an instance of the class (singleton instance) and inject the needed variable in the constructor.
Since this gets called during spring initialization (During Application startup), the log is initialized and we manually assign the static variable of log with the injected object in the constructor.
Note:
Don't forget the Inject annotation in the constructor Args.
Better to not provide a setter method to this log object. As its static and shared, we don't want people to replace this value post-construction. (Making it final is not an option as its static).
I'm trying to recieve the logger for my class:
public static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(this);
But using "this" there causes "Cannot use this in a static context" error.
Anyone know how to fix this?
EDIT: I must be able to access the logger from all classes in my program, so it must be public.
Notice I changed modifier from public to private:
public class FooBar {
private static final Logger log = Logger.getLogger(FooBar.class);
//or (if you are using java.util.logging):
private static final Logger log = Logger.getLogger(FooBar.class.getName());
}
For org.appache.log4j:
private static final Logger LOG = Logger.getLogger(MyClass.class);
For java.util.Logging
private static final Logger LOG = Logger.getLogger(MyClass.class.getName());
I'm using an enum singleton, but implementing logging is troublesome. This:
public enum Foo {
INSTANCE;
private final Logger log = Logger.getLogger(Foo.class.getName());
...
}
The logger is instantiated in the way that I would instantiate a logger for a normal Java class, but of course I get the following error:
Foo.java: illegal reference to static field from initializer
Is there an equivalent way to log in enum singletons?
In answer to your question, just make the logger static...
BTW, I think its standard practice to use a static logger even for object instances. In other words, the logger is on the class; all objects use the static logger references.
See
http://logging.apache.org/log4j/1.2/manual.html
Most of the examples of using a logger in there have the logger as a static property...
Log dynamically:
Logger.getLogger(Foo.class.getName()).info("log info");
A bit shorter: use a method: logger().debug(...)
private static Logger logger()
{
if(logger == null)
{
logger = Logger.getLogger(AnEnum.class);
}
return logger;
}
/** Logger **/
private static Logger logger;