Need to perform RequestBasedLogging - logback

I have a need to implement request based logging.
Based on header - log-level-header.
In my code, I am using JAX-RS and have implemented ContainerRequestFilter.
#Override
public void filter(final ContainerRequestContext context) throws IOException {
String log_level = context.getHeaderString("log-level-header");
//translate to actual log level
Logger root = (Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
root.setLevel(logLevelToSet);
}
I am using Logback and slf4j API.
The problem is that i am setting the log level to the RootLogger which is a singleton and hence ends up modifying the log level across application.
Instead I intend to change the log level for a particular thread (RequestBasedLogging). Is it achievable and how?

Yes, this is achievable via TurboFilters and MDC. The code in MDCFilter should be helpful as well.
The key is to understand MDC.

Related

Retrieve Log's Thread in Logback Filter

Using ch.qos.logback:logback-core:1.2.3 I've added TurboFilter to the LoggerContext sniffing for all the logs that go through the loggers:
loggerContext.addTurboFilter(filter);
But I can't find a way to figure out the log Thread. It actually doesn't seem to be passed in to the TurboFilter function:
public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
}
Any suggestions how to get the whole info from the loggers including the Threads?
It seems it wasn't so difficult at all. I figured it out that the decide method is called on the same Thread where the log has occurred, so Thread.currentThread().getName() gives the information I need.

How to get back MDC "inheritance" with modern logback?

After going back to an older project and getting around to update its dependencies I had to realize that logback does not anymore propagate MDCs to children since version 1.1.5: https://github.com/qos-ch/logback/commit/aa7d584ecdb1638bfc4c7223f4a5ff92d5ee6273
This change makes most of the logs nigh useless.
While I can understand the arguments given in the linked issues, I can not understand why this change could not have been made in a more backwards compatible manner (as is generally usual in java..).
Q: What is the now correct way to achieve the same behaviour other than having to subclass everything from Runnables to Threads?
I see no straightforward way to change this back. Two alternatives that come to mind are:
Way #1: Wrap all Runnables
Introduce an abstract class that will copy MDC from the original Thread to a new Thread and use it instead of Runnable
public abstract class MdcAwareRunnable implements Runnable
{
private Map<String, String> originalMdc;
public MdcAwareRunnable()
{
this.originalMdc = MDC.getCopyOfContextMap();
}
#Override
public void run()
{
MDC.setContextMap(originalMdc);
runImpl();
}
protected abstract void runImpl();
/**
* In case some Runnable comes from external API and we can't change that code we can wrap it anyway.
*/
public static MdcAwareRunnable wrap(Runnable runnable)
{
return new MdcAwareRunnable()
{
#Override
protected void runImpl()
{
runnable.run();
}
};
}
}
If some Runnable comes from an external API that you can't change that code, use wrap helper method.
Drawback: need to analyze and change whole code.
Way #2: Mess with slf4j internals
Resurrect the original LogbackMDCAdapter implementation that uses InheritableThreadLocal from before that commit and put it somewhere in your code under some other name. Then somewhere around startup use reflection to override MDC.mdcAdapter property with and instance of that custom implementation. This is obviously a dirty hack but it saves a lot of troubles comparing to #1.
Note: for performance reasons it makes to inherit your resurrected version from existing LogbackMDCAdapter and just override all the methods with old implementation. See LoggingEvent.java and LogbackMDCAdapter.getPropertyMap internal method for some details.
Way #3: Mess with logback jar (even stranger alternative)
This sounds to me as a quite bad plan but for completness here it is.
Again resurrect the original LogbackMDCAdapter but this time don't rename, compile it and override that .class file inside logback.jar.
Or resurrect the original LogbackMDCAdapter with renaming, remove .class file for org.slf4j.impl.StaticMDCBinder from logback.jar and add your own class that will return resurrected version of LogbackMDCAdapter either to logback.jar or to your code. MDC seems to be bound to that class by name to create an implementation of MDCAdapter to use.
Or you can achieve similar result by using custom ClassLoader that would map org.slf4j.impl.StaticMDCBinder to your class instead of the one inside logback.jar. Note: this is probably impossible to achieve inside a Web-container that will add its own custom ClassLoaders.
Way 4: Misuse TurboFilter
ch.qos.logback.classic.Logger passes the logging event to a filter before passing it along to the appenders.
Way 5: Modify log Encoder / provider
Although this would mean the logging event is not updated, but the log output will be.

How to hook interceptor for ILogger

I'm using the LoggingFacility, and need to add interceptor for the ILogger instances, created by the facility.
So far I tried to modify the component model for ILogger, and this didn't work, as the loggers are not really resolved using the standard resolving mechanism (they are created by a factory, which use some wrappers).
I was thinking to override the logging subresolver, but kernel.Resolver does not allow replacing (or wrapping) the resolver added by the facility.
I was thinking about hooking to Kernel.DependencyResolving, but it appears I can not replace the dependency there.
What is the most appropriate place to put such a hook, so I can add Interceptor for the ILogger.
EDIT: After a lot of poking around, I came with somewhat "hackish" solution, which unfortunately depends on small reflection usage.
The real problem appears to be, that the way the loggers are constructed does not follow (for me) the castle spirit of doing things. I.e. the resolver does not use the already registered logger factory, so it's impossible to add interceptors to the factory itself.
There is a great article about that on CodeProject: Aspect Oriented Programming (AOP) in C# using Castle DynamicProxy from Linjith Kunnon. It shows you how to define a Interceptor
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
try
{
Console.WriteLine(string.Format("Entered Method:{0}, Arguments: {1}", methodName, string.Join(",", invocation.Arguments)));
invocation.Proceed();
Console.WriteLine(string.Format("Sucessfully executed method:{0}", methodName));
}
catch (Exception e)
{
Console.WriteLine(string.Format("Method:{0}, Exception:{1}", methodName, e.Message));
throw;
}
finally
{
Console.WriteLine(string.Format("Exiting Method:{0}", methodName));
}
}
}
And how to register it with Castle.Windsor
kernel.Register(
Component.For<LoggingInterceptor>()
.ImplementedBy<LoggingInterceptor>()
);
kernel.Register(
Component.For<IRocket>()
.ImplementedBy<Rocket>()
.Interceptors(InterceptorReference.ForType<LoggingInterceptor>()).Anywhere
);
Please note that there is more valuable content in the linked article and that the whole code provided here is from the article and not from me. All kudos goes to Linjith Kunnon.
You need to create your own logger factory (derived from default implementation matching your logging framework) and then you can setup facility to use this factory like this:
container.AddFacility<LoggingFacility>(f => f.UseLog4Net().LogUsing<MyFactory>());
See full example here

Mule: JUnit test case to call a service which is in middle of the Mule flow

I'm newbie for JUnit test case. Please help me on this issue. I have 2 mule flows- first flow having MQ as inbound and it has datamapper to transformer the xml. With the first flow input, i'm calling second flow where we are calling the existing service ( SOAP/HTTP) call. Please find my JUnit below. I'm able to get the success response. But my requirement
1. I need to see the transformer response coming out from the Transformer.( Like how we see via logger component in our flow)
2.Need to override the url (HTTP) through JUnit ( in order to test the error scenario)
public class Request_SuccessPath extends FunctionalTestCase {
#Test
public void BulkRequest () throws Exception {
MuleClient client = muleContext.getClient();
System.out.println("test");
String payload = " <root> <messageName>str1234</messageName><messageId>12345</messageId><DS>123</DS><</root>";
MuleMessage reply = client.send ("vm://test",payload ,null);}
#Override
protected String getConfigResources() {
// TODO Auto-generated method stub
return "src/main/app/project.xml";}
i thought the following snippet will override the url.But it is not
DefaultHttpClient client1 = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://localhost:7800/service);
assertNotNull(response);
3. How to take the control of the flow and see any response inbetween the flow.
Instead of WMQ, i have replaced VM as inbound end point for testing purposes.
4. Is there any chance like without replacing VM can we call directly with WMQ through JUnit TestCase. Kindly help me on this.
I'm using 3.4 version, not using maven as of now. Please help me. Thanks in advance.
1) What do you mean by "see". Would it work logging it? inspecting it while debugging?
2) You should parametrize your endpoint with a variable, something like
and configure a property placeholder as explained here: http://www.mulesoft.org/documentation/display/current/Using+Parameters+in+Your+Configuration+Files
Adding http.port, http.host and http.path variables to mule-app.properties
taking into account that you must set system-properties-mode="OVERRIDE" and then start your Mule server using bin/mule -M-Dhttp.host=your-host -M-Dhttp.port=your-port -M-Dhttp.path=your-path
3) Yes, WMQ has a Java API you can use to interact with it: http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp?topic=%2Fcom.ibm.mq.csqzaw.doc%2Fuj41013_.htm , you will probably found hundreds of examples by googling about it.
Regards.

session-specific info to comet's "onEvent" method

I have two questions about using the Comet feature with Glassfish. I'm
pretty new at this, so if there's an easy answer or some documentation
I should read please let me know! Thanks.
I'm building a system that has multiple microcontrollers sending data
to a central DB. Users view the data via their browsers ... in formats
(metric vs. English, say) of their own choosing. The display needs to
be updated without user action. It looks like Glassfish + Comet should
be perfect. And so I started with Oracle's "hidden_Comet" example, and
that works great.
So question #1 is this: how can one get session-specific information
into the "onEvent" method?
As context, here's the code; it’s straight from the Oracle example:
private class CounterHandler implements CometHandler<HttpServletResponse> {
private HttpServletResponse response;
public void onEvent(CometEvent event) throws IOException
{
if (CometEvent.NOTIFY == event.getType())
{
PrintWriter writer = response.getWriter();
writer.write("<script type='text/javascript'>");
[... etc. Here is where I need to pass some session-specific
info to the JavaScript]
event.getCometContext().resumeCometHandler(this);
}
}
It would seem that session attributes would be perfect, but it looks
like you can't get the 'session' variable from the "HttpServletResponse".
I thought about using cookies, but they seem to be accessible only with
HttpServletRequest, not "...Response", and, as above, only ‘response’
is available in the “onEvent” method.
So question #1 is: how do you do this?
Question #2 is: is this just the wrong way to attack this problem and is
there a better way?
I'm not sure I understand the data structures and control flow of Comet very well yet, but it seems that this works:
Add a constructor to "class CounterHandler" and pass in the 'session' variable from 'doGet()' where "new CounterHandler" is called. Specifically, change:
CometHandler handler = new CometHandler();
to
HttpSession session = request.getSession();
CometHandler handler = new CometHandler(session);
Have the constructor save the session variable in a class instance variable. And then the "onEvent()" method has access to session attributes. And Bob's your uncle.
(It seems straightforward enough ... well, now.)