Pass log event specific data with slf4j (and log4j) - java

Is there a way to pass further information which just applies to the current log event with slf4j? Request based information like user, ip address or application name can be stored and accessed via the MDC. Later I can access that information in a layout or a converter and dont have to parse the log message. If I use a JSON based format I have another field with "appName":myApp and if I log with log4j in plaint text I can access it via the %{appName} notation.
Is there a way to achieve this with information which just applies to one log event? For example I want to pass an exception id(even for exceptions I do not own). The best but still ugly solution is to pass the id to the MDC, log the exception and delete it afterwards. Does anyone know a better solution?

If you don't need to use slf4j and log4j is good enough - you can use custom messages with log4j: https://logging.apache.org/log4j/2.x/manual/messages.html
If you want slf4j - you could implement your own Logger adapter which would treat any extra parameters appropriately (just like http://www.slf4j.org/xref/org/slf4j/impl/JDK14LoggerAdapter.html)

Related

Differentiate logging informations of multiple instances of the same class

I'm trying to log the methods call chain of some classes instances of a Java program using Log4j2. Each instance will behave differently based on the inputs they will recive (obviously).
The problem I'm facing is differentiating the logging messages of each instances for easly reconstructing the methods call chains I was talking about.
In addition to logging the entering and the leaving of each method, which is the default logging schema I'm using, I've tried to:
add method call parameters to the log: too few informations and not very readable;
add more informations about the method behaviour: too verbose and not very readable;
adding instance hash code to all the logging messages, which will become something like LOG.trace("{} - Leaving constructor(index:{})", System.identityHashCode(this), index);: good solution, but I've to add some boilerplate code to all the logging methods, which makes the code a little less readable;
using per-instance loggers (so not the per-class/static one) and adding the instance hash code in the logger name (so the logger name will be Classname#hashcode): seems the best solution in clean code terms, but I didn't found any way for declaring logger settings (like logging threshold) for multiple loggere, i.e. for all the loggers which name starts with Classname.
Which one do you think will be the best solution, or do you have any other way to suggest?
For this requirement, you can easily use an nested thread context: Look at "Fish Tagging" in https://logging.apache.org/log4j/2.x/manual/thread-context.html .
Excerpt:
ThreadContext.push(UUID.randomUUID().toString()); // Add the fishtag;
logger.debug("Message 1");
.
.
.
logger.debug("Message 2");
.
.
ThreadContext.pop();

How to modify a message in Log4J 2?

I'm trying to MODIFY, not DENY, certain messages before being logged using Log4J 2. I'm currently trying to use a Filter, but I can't seem to be able to modify a message from any of it's methods.
Please be patient with me as I'm totally new to Log4j.
Log4j purposely does not let you modify the LogEvent as it might get passed on to other Filters and Appenders that expected the original event. However, the RewriteAppender will let you create a copy of the LogEvent that is modified and then pass that to a subordinate Appender. The RoutingAppender also supports a RewritePolicy that does the same thing.

Setting a custom user defined formatter in logger.properties file

I have been asked to format logger messages so that they display on a single line. The problem is that the logger has programatically defined handlers and I cannot change any of these. Is it possible to override the format of the log messages in the .properties file even though the syntax has been defined in the logger class within a logp method? I apologise for not being able to provide code. I have tried creating a custom formatter and pointing to this in the logger.properties file but the changes are not visible.
Thanks,
Daniel
I have been asked to format logger messages so that they display on a single line.
If they are all using the SimpleFormatter then read the example in the API docs and configure it through the system properties or logging.properties as described in the SimpleFormatter class level documentation.
Is it possible to override the format of the log messages in the .properties file even though the syntax has been defined in the logger class within a logp method?
The only formatting that the logger is doing is order of the parameterized message format arguments. If you want to change that formatting you should use localization and parameterized logging. Then you can swap out different formats by providing a different resource bundles (per build) which allow you to change the format for each type of message key. However, that is only formatting the LogRecord message and params. The rest is controlled in the formatters.
I have tried creating a custom formatter and pointing to this in the logger.properties file but the changes are not visible.
The logging.properties is executed before the programatically defined handlers are setup. Call LogManager.readConfiguration or after that code runs. Keep in mind that will undo programatically defined handlers that are being setup. However, that may not help you because if you are able to write code to trigger a re-configuration you could just write code to install your formatter on the programatically defined handlers.

Filtering a subclass in log4j

I have a message driven system with, say, com.example.BaseMessagingAgent class, which is a basic class for many message agents. This base class logs message events. There are many subclasses of this base class, implementing different specific agents of system. Let us com.example.MyAgent which extends com.example.BaseMessagingAgent is one of them.
I want to log messages only related to class MyAgent. But I cannot define logging as:
log4j.logger.com.example.MyAgent=DEBUG, APPENDER
because logging occurs in parent class com.example.BasicMessagingAgent - I will record nothing.
And I also do not want to set logging in base class:
log4j.logger.com.example.BaseMessagingAgent=DEBUG, APPENDER
because it will log events for all agents, and I will have a lot of unnecessary logging.
Does enyone know how to limit logging to only one subclass?
You should write a filter for Log4j since AFAIK there is no way to put such information on log4j.properties file. More details at http://books.google.it/books?id=vHvY008Zq-YC&lpg=PA95&ots=yi335bZU7z&dq=&pg=PA95#v=onepage&q&f=false
It's pretty simple, actually.
First, add the appender to the root logger. Really. It will make your life much more simple.
Now configure the whole thing:
log4j.rootLogger=DEBUG, APPENDER
log4j.logger.com=ERROR
log4j.logger.com.example.MyAgent=DEBUG
The default for all classes below "com.*" will be to log only errors. The sole exception is com.example.MyAgent which will log at debug level.
You need to set the root logger to DEBUG as well or it will throw away all the DEBUG log messages.
The next step is to use one logger per instance. To get that, simply remove the static in the line which you create your logger and replace BaseMessagingAgent with getClass()
I know, it looks like overkill but that's how log4j works. Also creating a logger per instance isn't very expensive (unless you create millions of MyAgent per second).
If you really want to add an appender to a single class, then don't forget to turn off additivity (...Class.additivity=false) or you will get all log messages twice.

what is the NDC logs and how we can use it in our application and what is the significance of that

what is the NDC logs and how we can use it in our application and what is the significance of that...
Nested Diagnostic Contexts are particular to a thread.
Common uses are for recording info per-session (if one thread is used for a session), so you can log the originating client, username etc. and other cross-cutting attributes without:
passing these attributes through the layers of your application
explicitly logging them in each log statement. Log4j will output the NDC if PatternLayout is suitably configured.
See also Log4j's Mapped Diagnostic Contexts.
NDC stands for "Nested Diagnostic Contexts", it's a feature of log4j. The most common usage of log4j is just to log stuff without any indication of what client request it was part of, with the result that, when your application runs in production with concurrent requests, all the log messages for all the requests are jumbled together in the log file and telling who did what is impossible. NDC allows you to mark log messages as belonging to particular clients so that you can distinguish who is doing what, without having separate loggers for each client.
Logger are usually statically defined in the code, which make log sometimes hard to understand.
The NDC allows the dynamically push a parameter that will be displayed in every subsequent log line issued by the thread, until it is popped.
Useful if you want a log like:
[request=x] a
[request=y] a
[request=x] b
[request=x] c
[request=y] b
[request=x] d
[request=y] c
[request=y] d
(Disclaimer: I don't remember the exact formatting)
With just a,b,c,d it's hard to understand which thread does what. If you push and pop the request id dynamically, then it's easier to follow. Can also be used for other kinds of contextual information.

Categories