How do I implement logging in an enum singleton? - java

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;

Related

How to decorate Logger in logback-classic?

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?

Testing Log Messages Created by Slf4j Without Using Log4J

I want to test a method whether it's creating correct logs or not. My class looks like this:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
class EventLogHandler {
private final Logger logger = LoggerFactory.getLogger(EventLogHandler.class);
private final Marker eventMarker = MarkerFactory.getMarker("EVENT");
public void handle(final Event event) {
final String log = SomeOtherClass.createLog(event);
logger.info(eventMarker, log);
}
}
I've seen some examples/solutions for testing logs but all of them are using Log4j, which we are not using in the project. I can only use Log4j2 of spring-boot, Slf4j and Logback classic.
How can I test that handle(...) method with my existing dependencies?
With the current implementation you cannot test/verify the invocations on logger without engaging a logging system and asserting on its output e.g. introduce logback and configure it with a stdout appender and capture stdout and assert against it etc.
In order to test your class without doing any of that you have to get your hands on the logger instance in use in EventLogHandler. The current implementation makes this difficult by constructing the logger like this:
private final Logger logger = LoggerFactory.getLogger(EventLogHandler.class);
A common approach to testing in this scenario is to refactor the creation of logger in such a way that you can inject a mocked instance into EventLogHandler when running your tests. For example:
class EventLogHandler {
private final Marker eventMarker = MarkerFactory.getMarker("EVENT");
private final Logger logger;
public EventLogHandler() {
this(LoggerFactory.getLogger(EventLogHandler.class));
}
// probably only used by your test case
public EventLogHandler(Logger logger) {
this.logger = logger;
}
public void handle(final Event event) {
logger.info(eventMarker, log);
}
}
Then test it like so:
#Test
public void someTest() {
Logger logger = Mockito.mock(Logger.class);
EventLogHandler sut = new EventLogHandler(logger);
sut.handle(event);
// verify that the right state is extracted from the given event and that the correct marker is used
Mockito.verify(logger).info(..., ...);
}
A less common alternative would be to use Powermock to allow you to mock this call: LoggerFactory.getLogger(EventLogHandler.class); and then use Mockito to verify calls onto it in the same way as is shown above..

How to override LOGGER interface default implementation

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

May I create logger in Log4j2 per Level, not per class?

I was taking over an old java projcet. And the original designer has gone.
In this project, it uses Log4j in a static way, like this:
public class LogUtil {
private static final String FQCN = LogUtil.class.getName();
private static final Logger logger1 = Logger.getLogger("INFO");
private static final Logger logger2 = Logger.getLogger("ERROR");
public static void info(Object message) {
if (logger1.isInfoEnabled()) {
forcedLog(logger1, Level.INFO, message);
}
}
public static void error(Object message) {
forcedLog(logger2, Level.ERROR, message);
}
private static void forcedLog(Logger logger, Level level, Object message) {
logger.callAppenders(new LoggingEvent(FQCN, logger, level, message, null));
}
}
Then other class can easily write logs with LogUtil.error("") and LogUtil.info("") .
And the INFO logger and ERROR logger were configured to write in separate files.
But in Log4j2, the class LoggingEvent can not be found.
So the question is, if I want to use Log4j2, how should I modify this class?
(maybe use Log4j 1.x bridge provided in log4j2 packages?)
Is using logger in this way reasonable? It seems that it is easier than creating loggers per class. Is there any problems like performance or others?
So the question is, if I want to use Log4j2, how should I modify this class? (maybe use Log4j 1.x bridge provided in log4j2 packages?)
You could rewrite it to use the public API instead of relying on underlying implementation details. So, something like this:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LogUtil {
private static final Logger TRACE_LOGGER = LogManager.getLogger("TRACE");
private static final Logger DEBUG_LOGGER = LogManager.getLogger("DEBUG");
private static final Logger INFO_LOGGER = LogManager.getLogger("INFO");
private static final Logger WARN_LOGGER = LogManager.getLogger("WARN");
private static final Logger ERROR_LOGGER = LogManager.getLogger("ERROR");
private static final Logger FATAL_LOGGER = LogManager.getLogger("FATAL");
public static void trace(Object msg){
TRACE_LOGGER.trace(msg);
}
public static void debug(Object msg){
DEBUG_LOGGER.debug(msg);
}
public static void info(Object msg) {
INFO_LOGGER.info(msg);
}
public static void warn(Object msg){
WARN_LOGGER.warn(msg);
}
public static void error(Object msg) {
ERROR_LOGGER.error(msg);
}
public static void fatal(Object msg){
FATAL_LOGGER.fatal(msg);
}
...
}
Is using logger in this way reasonable? It seems that it is easier than creating loggers per class. Is there any problems like performance or others?
I don't see how adding 3 lines to a class is anything other than easy. All it takes to create a logger (in log4j2) is this:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
...
private static final Logger logger = LogManager.getLogger();
As for potential problems, with this implementation you give up a lot of the functionality of log4j.
You can't change the log level on a package or class basis because all of the classes share the same loggers. Perhaps you want to turn on TRACE logging for a new class you're working on, but you don't want any TRACE logs from other classes as it clutters the log file - you would not be able to make this kind of change easily.
You can't change the appenders on a package or class basis because once again all classes share the same loggers. Perhaps you want to send all of the logs from a new package that you're working on to a separate file so that you can more easily see what your new code is doing without having to filter the logs manually. Maybe you want to see all logs from a particular class or package in a single file during development so that you can follow the execution more easily without having to jump around to different files.

How to get the logger of a class in a static context?

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());

Categories