I have multiple services and each one of them interacting with each other. There is common.log file where I am writing the common log in to this file. logback.xml entry for this common.log is present in to another service lets name it as Service1. I want to write the some common things in to this existing common.log file using slf4j.
I tried to write a utility class to write the log from Service2 application to common.log and using the java.util.logging package as below ,
public class LogUtil {
private static LogUtil instance = new LogUtil();
public static LogUtil getInstance() {
return instance;
}
private static final Logger logger = Logger.getLogger(LogUtil.class.getName());
static {
try {
String logFile = System.getProperty("home") + "/logs/common.log";
FileHandler fh = new FileHandler(logFile, true);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.addHandler(fh);
} catch (Exception e) {
e.printStackTrace();
}
}
public void error(String msg) {
logger.log(Level.SEVERE,"[Error Common] :"+msg);
}
}
I want to do it using the slf4j api. Please suggest me some approach to write the utility class using slf4j.
Related
I have written a common class for logger and used this class in another class(Server_imp) to create logs. But no data is written in the text file instead it is shown on console.
What are problems with my code?
actually i have to create three log files for three different servers and log the functions performed by server managers .
and how i can stop data to be written on console?
public class MyLogging {
static Logger logger;
public Handler fileHandler;
SimpleFormatter plainText;
public MyLogging() throws IOException{
//instance the logger
logger = Logger.getLogger(MyLogging.class.getName());
//instance the filehandler
fileHandler = new FileHandler("D:/JAVA/assignment1/Montreallog.txt",true);
//instance formatter, set formatting, and handler
plainText = new SimpleFormatter();
fileHandler.setFormatter(plainText);
logger.addHandler(fileHandler);
}
}
//I have not copied the whole code
public class Server_imp extends UnicastRemoteObject implements ServerInterface
{
Logger logger = Logger.getLogger(MyLogging.class.getName());
public void createTRecord(String f_name,String l_name,String addr,String number,String spec,String loc)
{
logger.info("Teacher record created");
}
I'm trying to use a component to load up my configuration yml file.
However, it throws a Null Pointer exception and System.out shows 'null' for application.
However, when the same pattern is used to code up a #RestController, everything works fine. Why is #component not seeing my configurations??
#Component
#ConfigurationProperties(prefix = "myconf")
public class AppConfigJSON {
private String application;
private final String applicationConfigJSON;
Logger logger = LoggerFactory.getLogger(this.getClass());
private final ReadContext acRcJson;
public AppConfigJSON(){
String json = "";
try {
System.out.println("Application: " + this.application);
json = IOUtils.toString(this.getClass().getResourceAsStream("/myconfs/"+this.application+".json"));
} catch (IOException e) {
logger.error("Error reading JSON or app YML {}", e.getStackTrace());
}
this.applicationConfigJSON = json;
this.acRcJson = JsonPath.parse(this.getApplicationConfigJSON());
}
// These functions below set by configuration
public String getApplication()
{
return application;
}
public void setApplication(String application)
{
this.application = application;
}
}
In your constructor the application variable hasn't been initialized yet, in other words, Spring needs an instance first so it can apply it's magic. You need to move your constructor logic to a method annotated with #PostContruct so the application variable is set with the property value at that point.
I am trying to test something like this:
try {
logger.info("message");
//do something
} catch(Exception e) {
logger.error(errorMessage);
}
I know that it's not a good practice to catch an Exception, but there is some legacy code and there is no time for refactoring.
So, I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
Please notice that I can`t use:
final Logger logger = LogManager.getLogger(AnaliticsService.class);
final Appender mockAppender = mock(Appender.class);
logger.addAppender(mockAppender);
final ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class);
Log4jConfigHelper.getInstance().bufferConfiguration();
verify(mockAppender, times(x)).doAppend(captor.capture());
because I don`t know how many messages are logged when UT is running.
You should try to make a Mock for LoggerFactory.
First annotate your TestClass with:
#RunWith(PowerMockRunner.class)
#PrepareForTest({YourController.class, LoggerFactory.class})
Then make a test, which calls needed method and veryfies errors:
#Test
public void testErrorLogging() throws Exception {
mockStatic(LoggerFactory.class);
Logger logger = mock(Logger.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
YourController controller = new YourController();
controller.someMethod();
verify(logger).error(anyString());
}
Log messages are part of the user interface of your code. Code that does computations should not make assumptions about the manner in which log messages are made available to the user, the text and language of the log messages, or even whether messages are communicated as text (rather than, say, a graphical means). So computational code should delegate to an associated logger class (in the UI/presentation layer) that hides all those details.
If the computational code only requires that the associated logger conforms to an interface, and uses dependency injection for being associated with a logger class, it is easy to mock the logger to examine whether the computational code has requested logging.
So if the code to be tested is like this::
public class MyService
{
private final MyServiceLogger logger;
MyService(MyServiceLogger logger)
{
this.logger = Objects.requireNonNull(logger);
}
public void processFile(Path path) {
...
try{
...
} catch (EOFException e) {
logger.logUnexpectedEOF(path);
}
}
}
public interface MyServiceLogger
{
public logUnexpectedEOF(Path path);
}
public class MyServiceTextLogger implements MyServiceLogger
{
private final Logger textLogger = LogManager.getLogger(MyService.class);;
#Override
public logUnexpectedEOF(Path path) {
textLogger.error("unexpected EOF for file {}",path);
}
}
You can test it like this:
public class MyServiceTest
{
private static class MockMyServiceLogger implements MyServiceLogger
{
private Path path;
private int nCalls_logUnexpectedEOF;
#Override
public logUnexpectedEOF(Path path) {
++nCalls_logUnexpectedEOF;
this.path = path;
}
void assertCalled_logUnexpectedEOF(int nCalls, Path path) {
assertEquals("Called logUnexpectedEOF, nCalls", nCalls, nCalls_logUnexpectedEOF);
assertEquals("Called logUnexpectedEOF, path", path, this.path);
}
}
#Test
public void processFile_unexpectedEOF() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(1, testPath);
}
#Test
public void processFile_OK() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(0, null);
}
}
I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
You don't need to check for an exception this way. A test which throws an Exception fails.
} catch(Exception e) {
logger.error(errorMessage, e);
throw e; // report the error to the test
}
Note: when to throw an error to the testing framework it will log/print it so I suspect you don't need to be catching it in the first place.
I need to have an Custom Wrapper around Log4j2. The basic requirement is that. My application should only use My CustomLogger everywhere. instead of Log4j2 logger so in future if needed i can remove 3rd party library like log4j2 etc dependency easily.
How can i do this ??
Log4j2 comes with a tool for generating custom logger wrappers:
See http://logging.apache.org/log4j/2.0/manual/customloglevels.html#CustomLoggers
This tool was intended for use with custom log levels but you can also use it for your purpose. There are a few methods you may want to remove if you want to completely remove all references to the log4j2 api, but it will still save you a lot of work.
The Log interface
First, you requires an interface to be used in each class of application. e.g.:
public interface Log {
boolean isInfoEnabled();
void info(String str);
void info(String str, Throwable t);
}
The wrapper class
Create a class that implements that interface. It's the wrapper for log4j, e.g.:
class Log4jWrapper implements Log {
private static final String FQCN = Log4jWrapper.class.getName();
private ExtendedLoggerWrapper log;
public Log4jWrapper(Class<?> clazz) {
Logger logger = LogManager.getLogger(clazz);
log = new ExtendedLoggerWrapper((ExtendedLogger) logger,
logger.getName(), logger.getMessageFactory());
}
public boolean isInfoEnabled() {
return log.isInfoEnabled();
}
public void info(String str) {
log.logIfEnabled(FQCN, Level.INFO, null, new SimpleMessage(str), null);
}
public void info(String str, Throwable t) {
log.logIfEnabled(FQCN, Level.INFO, null, new SimpleMessage(str), t);
}
}
The LogFactory class.
To create each Log, use a factory. e.g.:
public class LogFactory {
public static Log getLog(Class<?> clazz) {
return new Log4jWrapper(clazz);
}
}
Usage example
Use this factory for each instance of Log into your application. e.g.:
public class Test {
private static final Log LOG = LogFactory.getLog(Test.class);
public static void main(String[] args) {
LOG.info("This is a test... :-)");
}
}
As disscussed in this link : How to create a own Appender in log4j?
For creating a custom appender in log4j 1.x we have to extend the AppenderSkeleton class and implements its append method.
Similarly How we can create a custom appender in log4j2 as we dont have AppenderSkelton class to extend and all other appender extend AppenderBase class .
This works quite differently in log4j2 than in log4j-1.2.
In log4j2, you would create a plugin for this. The manual has an explanation with an example for a custom appender here: http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders
It may be convenient to extend org.apache.logging.log4j.core.appender.AbstractAppender, but this is not required.
When you annotate your custom Appender class with #Plugin(name="MyCustomAppender", ...., the plugin name becomes the configuration element name, so a configuration with your custom appender would then look like this:
<Configuration packages="com.yourcompany.yourcustomappenderpackage">
<Appenders>
<MyCustomAppender name="ABC" otherAttribute="...">
...
</Appenders>
<Loggers><Root><AppenderRef ref="ABC" /></Root></Loggers>
</Configuration>
Note that the packages attribute on the configuration is a comma-separated list of all the packages with custom log4j2 plugins. Log4j2 will search these packages in the classpath for classes annotated with #Plugin.
Here is a sample custom appender that prints to the console:
package com.yourcompany.yourcustomappenderpackage;
import java.io.Serializable;
import java.util.concurrent.locks.*;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.config.plugins.*;
import org.apache.logging.log4j.core.layout.PatternLayout;
// note: class name need not match the #Plugin name.
#Plugin(name="MyCustomAppender", category="Core", elementType="appender", printObject=true)
public final class MyCustomAppenderImpl extends AbstractAppender {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
protected MyCustomAppenderImpl(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
#Override
public void append(LogEvent event) {
readLock.lock();
try {
final byte[] bytes = getLayout().toByteArray(event);
System.out.write(bytes);
} catch (Exception ex) {
if (!ignoreExceptions()) {
throw new AppenderLoggingException(ex);
}
} finally {
readLock.unlock();
}
}
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#PluginFactory
public static MyCustomAppenderImpl createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for MyCustomAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new MyCustomAppenderImpl(name, filter, layout, true);
}
}
For more details on plugins:
http://logging.apache.org/log4j/2.x/manual/plugins.html
If the manual is not enough, it may be useful to look at the source code for the built-in appenders in log4j-core.
As you pointed out AppenderSkeleton is not available anymore so the solutions in How to create my own Appender in log4j? will not work.
Using Mockito, or similar library to create an Appender with an ArgumentCaptor will not work if you're expecting multiple logging messages because the MutableLogEvent is reused over multiple log messages.
The most generic solution I found for log4j2 is to provide a mock implementation that records all the messages. It does not require any additional libraries like Mockito or JMockit.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AbstractAppender;
private static MockedAppender mockedAppender;
private static Logger logger;
#Before
public void setup() {
mockedAppender.message.clear();
}
/**
* For some reason mvn test will not work if this is #Before, but in eclipse it works! As a
* result, we use #BeforeClass.
*/
#BeforeClass
public static void setupClass() {
mockedAppender = new MockedAppender();
logger = (Logger)LogManager.getLogger(ClassWithLoggingToTest.class);
logger.addAppender(mockedAppender);
logger.setLevel(Level.INFO);
}
#AfterClass
public static void teardown() {
logger.removeAppender(mockedAppender);
}
#Test
public void test() {
// do something that causes logs
for (String e : mockedAppender.message) {
// add asserts for the log messages
}
}
private static class MockedAppender extends AbstractAppender {
List<String> message = new ArrayList<>();
protected MockedAppender() {
super("MockedAppender", null, null);
}
#Override
public void append(LogEvent event) {
message.add(event.getMessage().getFormattedMessage());
}
}
It looks like plugin appenders are scanned at startup and cannot be added during runtime. Is that true?
to add new appender while running you can use monitorInterval property to update log configuration i.e. every 60 sec:
<Configuration monitorInterval="60">