In all of my requests, there is a header (request id) that I want to log, in case of anything - on any log level.
Is there a way to inject this into the sl4fj logging? So that the logger always tried to log the request id, in for example logging an exception, but the request.
Or do I always need to add this as a parameter to the logging?
This is not really related to Guice.
You have in slf4j a concept of MDC (Mapped Diagnostic Context). You can put variables in an MDC. These variables are local to the thread and are added to each log generated by this thread. The typical use case of an MDC is to add in every log the user associated with an HTTP request, or a session-id (ie, your use case).
See http://logback.qos.ch/manual/mdc.html
For a short example, you put a variable in MDC like this:
MDC.put("userId", currentUser);
and you can add in an appender format the variable with:
%X{userId}
Theoretically, it could be possible to implement this feature with Guice by injecting a request-scoped logger, but it's really more costly and less integrated with the logging framework. I didn't advise you to do this kind of things!
Related
Our app has 3rd party code that uses JUL to log. Our app has the jul-to-slf4j.jar on the classpath. I see the SLF4JBridgeHandler.install() method gets called (several times) as for whatever reason spring cleans up logging config a few times on based on certain ApplicationEvents. I created an event listener on ApplicationStartedEvent to again make sure the handler is installed as late as possible. I verify by checking the SLF4JBridgeHandler.isInstalled() method. Yet the 3rd party logs keep flowing to standard out. If i breakpoint the 3rd party logging class in IntelliJ and call SLF4JBridgeHandler.isInstalled() it returns false. If I then execute the below code the logs get picked up by slf4j and everyone is happy.
SLF4JBridgeHandler.install();
LogManager.getLogManager().getLogger("com.3rdparty").setUseParentHandlers(false);
Why does SLF4J keep getting uninstalled? How do I keep it installed? Are the 3rd party loggers getting created before we install slf4j and then not getting updated by the install() call?
Why does SLF4J keep getting uninstalled?
Most common reason is there is code calling LogManager.reset(). You can find the source by using a security manager.
If you user a logging.properties file then just add the bridge as a handler. That way reset is actually installing the bridge handler.
handlers= java.util.logging.ConsoleHandler, org.slf4j.bridge.SLF4JBridgeHandler
The only downside is that the bridge handler has to be visible to the system classloader. A very hacky trick you can try is to subclass SLF4JBridgeHandler and override equals to return false. LogManager.reset won't be able to remove this handler.
Logger.getLogger("").addHandler(new SLF4JBridgeHandler() {
public boolean equals(Object o) { return false;}
});
Are the 3rd party loggers getting created before we install slf4j and then not getting updated by the install() call?
You'll have to do some work to find out that answer. Use JConsole Mbeans or the system out debugging to walk the logger tree while debugging your application. The security manager tracing is your best bet for finding the exact stacktrace for who is responsible for removing the handler.
Can there be two root loggers somehow?
That can't happen under the standard LogManager. Tomcat has an implementation that is sensitive to the context classloader. That LogManager may return different instances of the same logger name based on context class loader. The only other way the handlers can be removed without going through the security manager is if the root logger was garbage collected. You could try to hold a strong reference to the root logger but that really shouldn't be needed. You should print the identity hash code of the root logger when the handler was known to be added and the identity hash code of the root logger after the handler was dropped. If the values are different the logger was garbage collected.
referring to quesition/answer in How to log MDC with Spring Sleuth?
I think this has/will change(d) with spring-cloud 2.0 as there is no SpanLogger or Slf4jSpanLogger anymore (or I don't find it)
Wouldn't it be nice if application properties spring.sleuth.baggage-keys and spring.sleuth.propagation-keys if set would also be put in MDC I think inside Slf4jCurrentTraceContext (as this class is currently final I cannot subclass it)
If not, how could I achieve this with spring-cloud 2.0 accordingly?
We don't want to put all entries in MDC (that really doesn't make a lot of sense). You can however either copy the Slf4jCurrentTraceContext and extend it in the way you want to (and register it as a bean) or maybe create your own implementation of CurrentTraceContext that would wrap the existing CurrentTraceContext via a Bean Post Processor and perform additional logic. I guess the first option is more preferable.
In version 2.1.0, Slf4jScopeDecorator was introduced and it will automatically add baggage values to MDC as long as they are whitelisted in the spring.sleuth.log.slf4j.whitelisted-mdc-keys configuration.
For example, if you have the following configuration:
spring.sleuth.baggage-keys=key1,key2
spring.sleuth.log.slf4j.whitelisted-mdc-keys=key2
Only the value of key2 will be automatically added MDC, but not the value of key1.
For more info, see: https://cloud.spring.io/spring-cloud-sleuth/reference/html/#prefixed-fields
How can I debug the transactions in my spring boot application ?
Is there a way to see how method calls get grouped in a transaction context or another ?
You have asked 2 broad questions here.
1. How to debug a transaction (assuming from your post title, you are looking for logging)
The simplest way to achieve is to use the bundled java.util.logging.Logger in SpringBoot.
Below code snippet should tell you how:
public class EmojiController {
private final static Logger logger = Logger
.getLogger(EmojiController.class.getName());
public ModelAndView getEmoji() {
logger.info("emoji: " + emojiId + " lookup initiated");
---do something---
}
By default, If you use the ‘Starters’, Logback will be used for logging. Appropriate Logback routing is also included to ensure that dependent libraries that use Java Util Logging, Commons Logging, Log4J or SLF4J will all work correctly.
2. Grouping the method calls related to one transaction:
This is a very wide topic and has even wider application when asked in the context of distributed application architecture like, micro-services. The point is, how do you relate various log entries with each other to group them for a wholistic view.
The solution for that lies in the concept called Distributed Tracing. You can read in detail about that in this wonderful post from Josh Long.
More detailed documentation on the discussed technologies can be found here -
Sleuth - http://cloud.spring.io/spring-cloud-sleuth/spring-cloud-sleuth.html
ZipKin - http://zipkin.io/
It should help you achieve what you want but in case you have more questions on their usage, please raise another question.
--- EDIT ---
There's a section about Logging in the Spring Reference.
It shows how to configure different logging frameworks, among them log4j
In your case the last line of the config would be:
log4j.logger.org.springframework.transaction=DEBUG
I have some perplexity about the SecurityContext propagation in Spring Integration.
Here is the point of the documentation:
http://docs.spring.io/spring-integration/reference/htmlsingle/#security-context-propagation
My perplexity are the following:
(1) To be sure that our interaction with the application is secure,
according to its security system rules, we should supply some security
context with an authentication (principal) object. The Spring
Security project provides a flexible, canonical mechanism to
authenticate our application clients over HTTP, WebSocket or SOAP
protocols (as can be done for any other integration protocol with a
simple Spring Security extension) and it provides a SecurityContext
for further authorization checks on the application objects, such as
message channels. By default, the SecurityContext is tied with the
current Thread's execution state using the
(ThreadLocalSecurityContextHolderStrategy). It is accessed by an AOP
interceptor on secured methods to check if that principal of the
invocation has sufficent permissions to call that method, for example.
This works well with the current thread, but often, processing logic
can be performed on another thread or even on several threads, or on
to some external system(s).
This means that the SecurityContext (normally) is accessible only for the current Thread. Right?
So, how to make it accessible for another thread of another application (integrated with Spring Integration) ?
(2) Standard thread-bound behavior is easy to configure if our application is built on the Spring Integration components and its
message channels. In this case, the secured objects may be any
service activator or transformer, secured with a
MethodSecurityInterceptor in their
(see Section 8.8, “Adding Behavior to Endpoints”) or even
MessageChannel (see Section D.2, “Securing channels” above). When
using DirectChannel communication, the SecurityContext is available
automatically, because the downstream flow runs on the current thread.
But in case of the QueueChannel, ExecutorChannel and
PublishSubscribeChannel with an Executor, messages are transferred
from one thread to another (or several) by the nature of those
channels. In order to support such scenarios, we can either transfer
an Authentication object within the message headers and extract and
authenticate it on the other side before secured object access.
Or, we can propagate the SecurityContext to the thread receiving the
transferred message.
This means that we have to extract the Principal manually? If yes, how?
Or it's enough to use the propagation aspect, from 4.2 version?
(3) Starting with version 4.2 SecurityContext propagation has been
introduced. It is implemented as a
SecurityContextPropagationChannelInterceptor, which can simply be
added to any MessageChannel or configured as a
#GlobalChannelInterceptor. The logic of this interceptor is based on
the SecurityContext extraction from the current thread from the
preSend() method, and its populating to another thread from the
postReceive() (beforeHandle()) method. Actually, this interceptor
is an extension of the more generic
ThreadStatePropagationChannelInterceptor, which wraps the
message-to-send together with the state-to-propagate in an internal
Message extension - MessageWithThreadState, - on one side and
extracts the original message back and state-to-propagate on another.
The ThreadStatePropagationChannelInterceptor can be extended for any
context propagation use-case and
SecurityContextPropagationChannelInterceptor is a good sample on the
matter.
"Starting with version 4.2 SecurityContext propagation has been introduced." => Ok, very well.
But: "It is implemented as a SecurityContextPropagationChannelInterceptor, which can simply be added to any MessageChannel or configured as a #GlobalChannelInterceptor."
What does it mean? I have to implement an interceptor that extends "SecurityContextPropagationChannelInterceptor" ?
What I have to "add" in my <int:channel> configuration?
And if I use <int:channel-interceptor> (the same of #GlobalChannelInterceptor), it's different from using <int:interceptors> ?
Other perplexity:
"The logic of this interceptor is based on the SecurityContext extraction from the current thread from the preSend() method, and its populating to another thread from the postReceive()
(beforeHandle()) method."
But why there are a "obtainPropagatingContext" method and a "populatePropagatedContext" method in the SecurityContextPropagationChannelInterceptor class?
Where is made the propagation? In the preSend() / postReceive() methods, or in those two methods?
Furthermore, I tried to propagate the SecurityContext to an external application, without success...
Any explanations about this argument would be appreciated.
You have a lot of questions here, but let me try to answer to them.
What does it mean? I have to implement an interceptor that extends "SecurityContextPropagationChannelInterceptor" ?
No, there is such an interceptor in the Framework out-of-the-box. What you have to do to understand how to add interceptor to MessageChannel: http://docs.spring.io/spring-integration/reference/html/messaging-channels-section.html#channel-configuration-interceptors.
Or like this:
#Bean
#GlobalChannelInterceptor(patterns = {
"#{'queueChannel'}",
"${security.channel:executorChannel}",
"publishSubscribeChannel" })
public ChannelInterceptor securityContextPropagationInterceptor() {
return new SecurityContextPropagationChannelInterceptor();
}
See their JavaDocs for more information.
But why there are a "obtainPropagatingContext" method and a "populatePropagatedContext" method in the SecurityContextPropagationChannelInterceptor class?
SecurityContextPropagationChannelInterceptor extends ThreadStatePropagationChannelInterceptor<Authentication> , where obtainPropagatingContext and populatePropagatedContext are just generic method to extract some current State in the preSend() (on Thread) and provide that State for population/manipulation in the postReceive(), which may happen in the different Thread.
Yes, SecurityContext is thread-bound in Spring Security and the logic to be sure that we can perform a secured function is fully based on the ThreadLocal variable. That's why we have to transfer it that way. The "propagation" is a process not state.
Not sure what you mean about "external application", but there is only one mechanism to do that: send credentials together with the request to that application.
In my spring-3 application I have an AuthenticationInterceptor (which is basically an interceptor) that checks for the privileges for a user. I am using a Spring's MultipartResolver to try an upload a file to the server.
The problem that I now face is that I wish to perform different actions based on user privileges, in case of a MaxUploadSizeExceededException.
However I see that this exception is occurring at the DispatcherServlet level and is caught by HandlerExceptionResolver
I want to be able to call my AuthenticationInterceptor before any of this happens?
Is there a straightforward way.
The problem is that the exception occurs BEFORE the request is dispatched to a controller and because of that, your interceptor also never fires. I guess you have that part figured out already.
Want to get around that...
For starters, I would move the authentication mechanism out IN FRONT of the servlet by using servlet filters. This being said, it makes little or no sense to roll your own solution in that space when a great product like Spring Security can do that for you.
Once you transition to Spring Security (or similar), the user's SecurityContext (roles, permissions, etc.) will have been resolved by the time the exception occurs and is caught.
Now, if I'm reading your question correctly, it seems you might like to respond to the exception differently based on the user's roles, permissions, etc. That should be possible at this point. You'd implement a custom HandlerExceptionResolver that inspects the SecurityContext to see if the user has a certain role or permission and then respond accordingly.
Hope that helps!
There are two basic ways to handle doing something in-stream before the Handler code gets called:
Implement the HandlerInterceptor interface, and code the code you want to run in the preHandle method
Create an Aspect using #Aspect and configure a pointcut to run #Before the method call
In either case, you could check the logged-in user's Roles using SecurityContextHolder.getContext().getAuthentication().getAuthorities() and then decide what to do based on Role membership.