Can I change the level of a message logged by Spring? - java

Our Spring-based microservice sometimes fails to connect to its eventing service for a couple of minutes. This is okay from an application point of view, as we have several retries and fallbacks in place that take care of this. Each failed connection attempt results in an ERROR log written by the class org.springframework.jms.listener.DefaultMessageListenerContainer. Is it possible to configure Spring such that this log message would be written with a lower log level, such as WARN?

As #Jens mentioned this is not possible since the level at which a message is logged is given by the actual function call.
So, if Spring (or any other library or even your own code, really) has something like:
public void connect() {
try(establishConnection()) {
// ...
} catch (Exception e) {
log.error("Failed to establish connection");
}
you'll notice that the logging is always done at the error level, since that is what is being invoked.
The only recourse you have in this case is to set the log level of the class to OFF if your given logging implementation supports it (the default, Logback, does). For example:
# application.yml
logging.level:
org.springframework.jms.listener.DefaultMessageListenerContainer: OFF
However, there's really no reason to do this unless you are getting absolutely swamped by these log messages (and even then, just filter them in your Dashboard). Error logs exist for a reason.
Alternatively, I suppose you could also do some hacky trickery using AspectJ to change the level something is logged at if you really, really needed to, but I doubt this applies in this case.
Lastly, you can fork the library and link your own Jar files into your application. But I doubt this maintenance burden is worth it in this case.

This is not possible because the level at which a message is logged is given by the actual function call. .
So, if Spring has something like:
public void connect() {
try(establishConnection()) {
// ...
} catch (Exception e) {
log.error("Failed to establish connection");
}
you'll notice that the logging is always done at the error level, since that is what is being invoked.
What you can do is to set the log level of the class to OFF :
# application.yml
logging.level:
org.springframework.jms.listener.DefaultMessageListenerContainer: OFF

Related

How do I change only the status code on a Spring MVC error with Boot?

I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}

How to handle exceptions during spring-boot start-up?

This isn't about how to handle exceptions in Spring MVC or anything. I specifically need to handle an exception that can happen while spring is starting, i.e. before the whole application context is even initialised.
For a bit of background, the application in question is an IoT node that allows remote access to electronic equipment. It has a little h2 database built in to persist some data. That data is nice to have at some moments, but not really essential for the application to work.
It so happens that the device the application is running on can get its power cut every once in a while, and if that happens while there was a write operation to the database going on, the file is corrupt and a JdbcSQLException will be thrown when the application tries to boot again.
Since the data is not really essential, the easiest way to make the application work again is to just delete the database and let h2 recreate it. But in order to do that, I have to catch the exception so I can react to it. The application does not have to continue starting, it will be booted up again by systemd. I really just need to identify the exception and delete the file, that's it.
There is one obvious way to do it, which is to put SpringApplication.run in a try-catch block. But it's also really ugly, because I get the exception I'm looking for nested inside a gazillion spring exceptions that were caused by h2 failing to start.
It was also suggested that I catch the exception in the bean that instantiates the database, but unfortunately there is no bean instantiating it. The DB serves as a Quartz job-store and as such is fully managed by spring. Its entire presence in the code are the following entries in the properties file:
spring.quartz.job-store-type=jdbc
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=900000
spring.datasource.name=h2
spring.datasource.url=jdbc:h2:file:${config.folder}controller
spring.datasource.driverClassName=org.h2.Driver
My question is, is there a way to register some kind of exception handler, or other means, to handle the exception directly when it happens, when I can identify it much more easily?
Depends how you've declared the bean. What's wrong with simply wrapping the bean like this?
#Configuration
class Conf {
#Bean
public DB foo() throws JdbcSQLException
{
try
{
return new DB();
}
catch(JdbcSQLException e)
{
deleteDatabase();
throw JdbcSQLException;
}
}
public static void deleteDatabase()
{
//...
}
}

Alternatives to SLF4J MDC

I'm trying to log the user name initiating each request in my JSF application, however apparently MDC on a web app server (thread pool) is risky.
I've already seen MDC leaking out into a new call when using the EJB #Asynchronous call which I wouldn't have expected.
What are the alternatives? I'd rather not have to rely on remembering to put the username on every log call. Do I wrap slf4j?
Clear your MDC put(..) with remove(..) in a try-finally block
MDC.put("system", "fedora");
try {
// your code here
} finally {
MDC.remove("system");
}
so that no state is kept after your code has run.

Is there a Spring Annotation for Error Handling that is not MVC related?

I have a bunch of Processors (basic data saving objects) that are currently in one big transaction. If one of them fails (duplicate data or whatever) I loose all the data.
I want to put the individual Processors into their own transactions so won't loose data. I can do that with annotations and all is fine. However, I have to catch the exceptions and only throw certain ones (like database down or some such). I want to eat the other errors (duplicates and bad messages and values and such) and just log them.
I found the ErrorHandler interface in Spring and the this thing: #ExceptionHandler(NullPointerException.class)
but I want something that is not tied to MVC. So I want something like this:
try {
<!--wrapped code (processor) -->
} catch (exception) {
<!--Exception handler code. -->
}
#ExceptionHandler(myexceptionHandler)
Also, I do understand that I can do this with AOP, but I would rather have a Spring stock class/annotation (I want someone else to do the work...)
It seems that Spring would have something like this built in, but I haven't found it. Any ideas?

Proper EJB Exception handling - ClassNotFoundException from client

I have some EJBs that use Hibernate to persist data to the database. I have a thick Swing client that talks to these EJBs. The client knows nothing about the database (no driver jar).
During one transaction a Hibernate ConstraintViolationException can be thrown. I catch all exceptions and wrap them in an EJBException like so:
catch(HibernateException e) {
e.printStackTrace();
throw new EJBException(e);
}
The problem I am getting is that when the exception is unmarshalled by the JBoss Invoker on the client side, a ClassNotFoundException is thrown (for PSQLException) since the client has no sql driver jar in the classpath.
I changed this application to always pass the caught exception to the ejbexception constructor like this so we could have a stack trace history. Now I am finding why the original developers didn't do this.
At this point I see two options - either include the postgres driver jar with the client, or remove passing the caught exception to the EJBException constructor. I am curious if anyone has any other suggestions and also how others handle exceptions in their EJBs?
My take is that the client, end user, doesn't need to know the technical details of the problem. Hence at various layer boundaries it's quite reasonble to convert a technical exception to a general "An error of nature XYZ ocurred".
A scheme I've seen used is for the server to allocate a unique error number at the point the exception is detected. It then writes diagnistics to its logs including that number. Messages reported to the client simply include the number. A support desk can then correlate the user's report of the issue via that specific error number.

Categories