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
Related
I have to implement a logging mechnism in Java.
So I use JEE and use RequestFilter and Interceptors.
I put all that in a package called logging.
My classes are: RequestFilter, ResponseFilter and Constants
So my question. Is that ok to leave out logging in the class name because it is present in the package already ?
Or should I rather do. package logging and duplicate logging: RequestFilterLogging, ResponseFilterLogging, ConstantsLogging
It really depends on a few questions:
1- Is the purpose of RequestFilter to always log only?
2- Is there a plan for you to modify it to add further logic? For example customizing headers of requests, etc?
3- Do you plan on having more than 1 RequestFilter and specifying priorities?
If your design is to have multiple request filters, each doing their own custom logic, with priortization, then I would do it as such:
com.app.requestfilters
--> LoggingRequestFilter
--> HeadersRequestFilter
If your design is to have 1 RequestFilter that does a lot of things, not only logging:
com.app.requestfilters
--> CustomRequestFilter
In my opinion, it is best to not reuse class names that exists in libs/frameworks that you are using:
In Apache Tomcat, there is a RequestFilter, and if you are extending this class, then yours should be a different name
https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/filters/RequestFilter.html
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.
Our application is deployed as a servlet war to multiple tomcat servers under multiple customer contexts:
customer#application.war
We're using log4j2 as our logging and alert email mechanism. So far, everything is working great and our fatal errors are being sent. However, as we deploy to new contexts, it's becoming less clear which customer is generating the error.
So far, it appears that the subject value is static and set in the config file and the system variables are loaded when the logger is built:
subject="[${applicationname}] Fatal Error ${hostname}:${sys:pwd}"
While it appears that there is a way to ascertain the name of our deployed context via the servlet API, we have yet to determine how to introduce this value in the email subject programmatically and dynamically at run time.
This would greatly reduce the time it takes to research an error. Any tips?
So far we've considered the following:
Custom war file with custom log4j2 config for each customer context (very hackish)
Update all log.fatal calls to include the context info from the servlet (horrid)
Custom SmtpAppender (final and protected so that's out)
Custom SmtpManager and override the subject in the MimeMessage object (seems workable but the documentation does not show how to implement)
TIA!!
Piko
This is actually a known issue in Log4j2 as of 2.9.1. The problem is that a MimeMessage is cached and the subject becomes a fixed value. A simple solution would be to stop caching.
There is an open ticket to address this: Log4j2-1450. (Related: Log4j2-1192, which implemented pattern lookups but didn’t fix that MimeMessages are cached.)
If you can provide a patch or a pull request it would greatly increase the chances of this being addressed speedily.
Update: looking my old comment in that ticket:
Looks like subject already supports $$ runtime lookups. The following
attributes are used for each email that is sent, and it should be
possible to support runtime lookups for these attributes:
* from
* replyto
* to
* cc
* bcc
* subject (already a runtime lookup)
It should be possible to configure the subject to be a system properties lookup like this:
subject = "$${sys:email.subject}"
Then you set system property email.subject to a different subject and send an email with a different subject. Can you try this?
Update 2:
If system properties are not suitable, you can also create a custom lookup, this is only a few lines of code.
Let's say that I have a REST API with endpoint /user/{user_id}/foo.
Now when it is called I would like that all logs that come from handling this request contain information about {user_id}. Is it possible to achieve that without passing {user_id} to every method?
I'm using SLF4j for logging, my application is based on Spring Boot.
You could also use MDC for this, see here. It's essentially a map, you just put your contextual information in it (e.g. user id) and then you can use it in your log layout. Be aware that this only works with certain underlying frameworks like logback, where a sample layout pattern would look like this:
<Pattern>%X{user_id} %m%n</Pattern>
Check the logback manual for more details on this.
You can use Logback's Mapped Diagnotic Context to propagate the {user_id} to every log message.
There are two parts to this:
Push your {user_id} into MDC e.g. MDC.put("user_id", "Pawel");
Include the MDC entry in your log statements. You do this by specifying it in your logging pattern. So, if you store the user id in a MDC entry named "user_id" the you would set logging.pattern.level=user_id:%X{user_id} %5p to include the value of that entry in every log event.
More details in the docs
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.