Logging in spring MVC - java

I'm currently working on a web application using Spring MVC, and I use the #ExceptionHandler annotation in every controllers of the application.
So basically I got a method like this:
#ExceptionHandler(RuntimeException.class)
public String handleException(RuntimeException ex) {
injectedService.notifyAndLogException(ex.getMessage());
return ("error_page");
}
My idea is to log and send an email to an application administrator in the injected service.
For now, I've tried to read some documentation about logging in spring application, and all the things I've seen is setting a static logger in each controller.
Like this:
private final Logger log = LoggerFactory.getLogger(Controller.class);
#ExceptionHandler(RuntimeException.class)
public String handleException(RuntimeException ex) {
log.info("Logging error");
injectedService.notifyException(ex.getMessage());
return ("error_page");
}
I'd like to know what is the point to use a logger in each controller instead of using it in one point only (the service)?

I'd like to know what is the point to use a logger in each controller instead of using it in one point only
If you use a single logger for the whole application, then every log message will be logged as coming from the same component. By using a logger per class or component, then your log files will contain information about which component logged the message.
For example, when you do:
Logger log = LoggerFactory.getLogger(Controller.class);
This creates a logger with the name of the Controller class, which will generally be displayed in the log file, e.g.
2012-03-07:12:59:00 com.x.y.Controller Hello!
This is just a convention, but it's a good one that I advise you follow.

a logger in each of your class files enables you get 'debug' or 'info' level when you are in production, or not able to connect a debugger.
Since you can limit via package or even class file name, what is logged, you can pin point to find errors, or to see what is happening under different load situations (concurrency problems, resources used ). If you use one generic logger, then you may flood your log file.
With the logger in the class that received the exception, you may be able to get at class variables that are not being passed into your exception handler.
I would also recommend that you do not do
injectedService.notifyAndLogException(ex.getMessage());
but pass the exception into your notify. While stack traces can be notorious verbose, the messages usually are not very help full ( NullPointerException without a stacktrace ? ). In your notify service you can set the subject to ex.getMessage() and the body have the entire stack trace.

Your controllers can extend an abstract class that declares a logger like that
protected Logger logger = LoggerFactory.getLogger( getClass() );
This logger can be used in all controller and it will prefix the log message with the controller class name.

Related

What is the Best way to group log messages in a MVC app

I want to add a logging system to our spring MVC application with minimum refactoring. I want to group messages per request and attach some id to it. One approach I thought of is, creating some logger object with an id in controller and pass to service, DAO layers. All the layers keep adding messages to that logger object. Finally, I print them at the end of request processing. Does Spring or log4j frameworks provide any better way to achieve this?
Spring has a Request scope, so you can create an request-scoped ID object generated when a request is received. You can then inject this object in the classes you need to log with regular dependency injection, so you don't have to pass anything around.
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestId requestScopedBean() {
return new RequestId();
}
As for the Log, the common practice is to initialise a Log per class. Then you'd have all the elements you want in every class.
private static final Logger logger = Logger.getLogger(YourClass.java)
Hope it helps!
Spring Security has a feature to add SessionId to the logger automatically .
Check this One
Posting the same answer here :
RequestContextHolder.currentRequestAttributes().getSessionId();
This relies on Spring's RequestContextHolder, so it should be used with Spring MVC's DispatcherServlet or you should have a RequestContextListener declared. Also session will be created if not exists.

Send log messages as JSON

I am using spring boot in a project and currently exploring the logging behaviour. For that I'm using the zipkin service.
I have exported my logs to a json file using proper logback.xml:
{"#timestamp":"2018-07-12T17:53:44.613+05:30","#version":"1","message":"in meth3","logger_name":"com.example.demo.Controller.Controller","thread_name":"http-nio-8089-exec-3","level":"INFO","level_value":20000,"traceId":"62bcfafad3a37a09","spanId":"62bcfafad3a37a09","spanExportable":"true","X-Span-Export":"true","X-B3-SpanId":"62bcfafad3a37a09","X-B3-TraceId":"62bcfafad3a37a09","caller_class_name":"com.example.demo.Controller.Controller","caller_method_name":"meth3","caller_file_name":"Controller.java","caller_line_number":43,"appname":"pom.artifactId_IS_UNDEFINED","version":"pom.version_IS_UNDEFINED"}
Is there a way so that I could insert a jsonObject in my message part of the log. Something like:
logger.info(<some_json_object>)
I have tried searching a way extensively but to no avail. Is it even possible?
The slf4j API only takes String as the input to the info, debug, warn, error messages.
What you could do is create your own JsonLogger wrapper, which takes a normal Logger (maybe wraps around it), which you could include at the top of your classes like:
private static final JsonLogger logger = new JsonLogger(LoggerFactory.getLogger(MyClass.class));
You can then use Jackson, GSON or your favourite object to JSON mapper inside your JsonLogger so that you could do what you want. It can then offer the info, debug, warn, error methods like a normal logger.
You can also create your own JsonLoggerFactory which encapsulates this for you so that the line to include in each class is more concise.

how to use log4j2 properties inside mulesoft expression component

we use logging heavily in most of the application using the log component inside mule flows. But when I use expression component and manipulate payload according to destination system sometimes I need to validate data for that always I need to write system.out.println inside expression component. Is there any way we can invoke or use log4j2 properties like we do inside java component.
Here is the sample code I am looking for
//mulesoft payload
additionalfields = payload.additionalfields;
if(org.apache.commons.collections.MapUtils.isEmpty(additionalfields))
{
//System.out.println("we have no data "+additionalfields);
}
You can use groovy log like log.info("we have no data "+additionalfields)
or use logger like
import java.util.logging.Logger
Logger logger = Logger.getLogger("")
logger.info ("we have no data "+additionalfields)
logger.debug("we have no data "+additionalfields)
Hope this helps.

Log4j: Route logs to logger based on method-stack

i would like to know if it is possible to configure log4j in such a manner that all log messages that are issued from a given class will be written in an extra file, regardless wether they are issued directly by this class or indirectly (through method calls of other classes).
Let's say we have parts of our buisiness logic in class foo.bar.BL. Log4j is configured to output all logs of package foo.bar to a file. We have an interface to a customer realized in class foo.bar.interfaces.CustomerIF. To process the requests from the customer, foo.bar.interfaces.CustomerIF calls some methods from foo.bar.BL. Now we want to seperate the logs originated by a customer-request to an extra log file, including the log messages issued in foo.bar.BL.
Any ideas how we can achieve the desired behaviour?
Thanks in advance,
takki

Logger not functioning properly

I am using java.util.logging.Logger Class for logging in my application. I have added FileHandler so that the application log is stored directly in log.txt file.
But for some reason, after the application is terminated the log is far from complete. On cmd, I can see all the statements but they are never appended to the file.
I have set FileHandler to the Logger by:
private void setLogger() {
try {
FileHandler hand = new FileHandler("log/log.txt", true);
hand.setFormatter(new SimpleFormatter());
Logger log = Logger.getLogger(ImageRename.MAIN_LOG);
//log.setUseParentHandlers(false);
log.addHandler(hand);
log.setLevel(Level.ALL);
} catch (IOException e) {
System.out.println("Could Not set logger");
}
}
Any problem with flushing? How to solve it? Thanks.
PS: On debugging, I have noticed that in between
Logger.getLogger(ImageRename.MAIN_LOG).getHandlers().length
returns 0. Where as it should return 1. Initially it was printing 1, but somewhere down the line it becomes zero.
The problem is ... garbage collection.
What is happening is likely the following:
You call Logger.getLogger(ImageRename.MAIN_LOG);
You setup the logger.
Java notices it is unreferenced, and discards it.
You call Logger.getLogger(ImageRename.MAIN_LOG); and expect to get the same logger.
A fresh logger is set up with default configuration.
You can avoid this by two measures:
Use a configuration file logging.properties for configuration. When creating the logger, the Java logging API will consult the configuration, and thus recreate it appropriately.
Use static references. This is a best practise anyway. Equip each class with a logger:
private final static Logger LOG =
Logger.getLogger(ExampleClass.class.getName());
While the class is loaded it then should not be garbage collected AFAICT.
See e.g. http://www.massapi.com/class/fi/FileHandler.html for an example (found via Google)
Note the following line, which may be your problem:
fileHandler.setLevel(Level.ALL);
(Note: this is the level of the Handler, not of the Logger or message.)
For debugging, first try to get messages at an ERROR level logged. Messages at level INFO and below are often supressed by default.
Also try setting the logging level as soon as possible. In my experience, the most reliable way of configuring Java logging is by using a properties file, and invoking Java with:
-Djava.util.logging.config.file=path/to/file/logging.properties
The reason is that the settings you do sometimes are not applied to loggers created before you loaded the settings, once some changes have been made to the logging.

Categories

Resources