Suppress warn logs for exception in Spring Boot - java

I have a custom exception like this
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public class AccessException extends RuntimeException {
}
But when I throw it from a #RestController, it's logging this:
2018-04-17 11:44:58.239 WARN 17928 --- [nio-8080-exec-3] .w.s.m.a.ResponseStatusExceptionResolver : Resolved exception caused by Handler execution: site.user.AccessException
I tried checking the source code but could not find where this is getting logged. I don't want to turn off logging because it may log other things that I am interested in. I just don't think that a custom exception like this should be logged every time.

You may set log level for class org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver to ERROR. That way you will only loose WARN, DEBUG and TRACE logs for this class only.
If there are some WARN-level exceptions you want to see from this class I see two options:
copy org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver to your codebase, change the logging. Make sure that your copy is on the top of the classpath and Spring is below. This is kind of hack. ResponseStatusExceptionResolver is implementation detail of Spring. It may be changed/moved/deleted in future release.
write your own implementation of org.springframework.web.servlet.HandlerExceptionResolver, register it, make sure that ResponseStatusExceptionResolver is not registered.

In Spring you can add this line into your application.properties to specify logging level for certain class:
logging.level.site.user.AccessException=ERROR
or
logging.level.site.user.AccessException=OFF

Related

Is there a way to get list of loaded properties file in SpringBoot application?

I'm facing an issue on only one platform when I'm trying to execute mvn clean install. As part of the build we compile multiple component and last we execute functional testing using wiremock. It is supposed to pick specific configuration from function testing profile and default properties should be picked from application.properties file. But for some reason same code isn't able to find the properties mentioned in these file. So, just wondering if somehow, if I can get the list of properties files being loaded during wiremock ? This will give some clue on why isn't expected properties files are being picked ?
All properties files are located inside :
src/main/resources
And, following from test class.
#ContextConfiguration(classes = SampleFTConfiguration.class)
public class SampleControllerTest{
//test method
}
#ComponentScan("com.xxx.xxx.xxx.ft")
#PropertySource("classpath:application-test.properties")
public class SampleFTConfiguration{
}
Note : I'm not expecting anyone to fix the issue, all I wanted to know, if we can get the name of loaded property files ?
After searching and trying out for a while, looks like ConfigurableEnvironment is what you're trying to find.
The code is pretty simple. However I think it's better to debug and check the configurableEnvironment value directly, so you can adjust the code to your needs (remove filter name, etc).
#Autowired
private ConfigurableEnvironment configurableEnvironment;
#Test
public void getProperties() {
Map<String, Object> mapOfProperties = configurableEnvironment.getPropertySources()
.stream()
.filter(propertySource -> propertySource.getName()
.contains("application-test.properties"))
.collect(Collectors.toMap(PropertySource::getName, PropertySource::getSource));
mapOfProperties.values()
.forEach(System.out::println);
}
the code will printed out
{properties-one=value-for-properties-one,
properties-two=value-for-properties-two}
with my application-test.properties value
properties-one=value-for-properties-one
properties-two=value-for-properties-two
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/ConfigurableEnvironment.html
Ok, following the test definition please make sure that:
You should run the test with spring runner (spring extension if you're on JUnit5). So you should place the annotation #RunWith(SpringRunner.class) (or #ExtendsWith(SpringExtension.class) for junit 5)
The property source you're using is application-test.properties. You've said that the properties file is located in src/main/resources but the file name probably implies that it should reside in src/test/resources
To troubleshoot context configuration on app startup (for those who don't have an access to app sources) you can add
logging:
level:
org.springframework.boot.context.config: trace
to your application.yml to get filenames:
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-app.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-ppe.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
Adding org.springframework.boot.context.properties: trace doesn't help much though. Some user-defined properties get logged, however others don't.

Changing level of logging dynamically

I want to change the level of logging for all (about) of my web application controller classes dynamically. Each class that does logging contains this code:
private static final Logger logger = LoggerFactory
.getLogger(HomeController.class);
I learned that slf4j doesn't offer the functionality of setting a log level, so that one has to use the underlying log4j:
public static void setLogLevel(String level) {
org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getRootLogger();
logger4j.setLevel(org.apache.log4j.Level.toLevel(level.toUpperCase()));
logger.info("Sample Info After setLevel");
logger.debug("Sample Debug After setLevel");
logger.error("Sample Error After setLevel");
logger.trace("Sample Trace After setLevel");
logger.warn("Sample Warn After setLevel");
}
My idea is to have a controller method which changes the logLevel and maybe stores it in the database. My question is: How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method? Also note that the logger code is static, while my database access is not static.
How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method?
You use a single controller method, taking the logger name and desired log level as a query parameters, and call the string version of LogManager.getLogger(String name).
Also saving to the database and re-loading on restart is of course extra work to be done by you.
Alternatively, the controller can update the Log4j configuration file used at startup, and force Log4j to reload it whenever it's changed, e.g. by calling PropertyConfigurator.configure(String configFilename).
You can use Slf4j and externalize your application.properties file . You can configure the logging level in this file as per your needs.
logging.level.com.test=DEBUG
logging.level.org=INFO
I am not sure why you need to store this logging level in the Database.

How to write a test case to verify log4j logs are generated in proper format

I want to write a testng test case to verify that the logs (log4j) generated are following exactly the given conversion pattern from the log4j.properties file. As an example if I have conversion pattern : [%d] %-5p [%t]: %m%n a sample log would looks like [2015-07-07 16:42:09,937] DEBUG [main]: Message 1 I want to make sure that the log follows the exact pattern.
So for now what I'm doing is first read all the logs in to a String array and loop that array to find whether the expected log without the date i.e. DEBUG [main]: Message 1 contains in the recorded.
Is this way of testing log records is correct ?
If you have any idea about a good way to test logs rather than this please point out.
There are any solutions. The first in my mind is:
Configure your log4j to write on System.out and use the SystemOutRule.
public void MyTest {
#Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
#Test
public void writesTextToSystemOut() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
See: https://stefanbirkner.github.io/system-rules/index.html
This solution is logger implementation independent if the logger writes to System.out and/or System.err.
More log4J aware:
Use a mock framework - #slartidan says - and mock the appender implementation.
Based on your preferred mock framework and log4J 1.x or log4J 2.x the solution is quite different.
Log4J 1.2
Create a mock logger appender and add this appender on logger:
Logger.getLogger("...").addAppender( mockedAppender );
See: https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html#addAppender%28org.apache.log4j.Appender%29
Log4J 2.x
Extending ConfigurationFactory and Configuration (only in test artefact) and add the mocked appender.
The easiest way to create a custom Configuration is to extend one of the standard Configuration classes (XMLConfiguration, JSONConfiguration) and then create a new ConfigurationFactory for the extended class. After the standard configuration completes the custom configuration can be added to it.
http://logging.apache.org/log4j/2.x/manual/customconfig.html (includes complete example)

RunTime Retention policy java annotations doesn't work in Weblogic 12C

We are planning to upgrade our product to Web-logic 12.C and WebSphere 8 stack ( Earlier it was WLC 10.3.5 and WAS 7). But issue in one of the web service component causing entire application failed to deploy in web logic. It works perfectly fine with WebSphere 8.
When deploying the EAR, Application sever throws 'Exception [EclipseLink-59] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DescriptorException' . After more analysis, I found below code in one of the WebServce dependant class causing the problem,
#ExcludeAttribute
public Map getOperations(){
Map map = new HashMap();
//some operation
return map;
}
#ExcludeAttribute describes Runtime retention policies, which is defined as shown below
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ExcludeAttribute {
}
getOperations method returns java.util.Map which does not work with RunTime retention annotations, but works with any other data types such as (Integer, Customer etc) . I have changed to java.uitl.HashMap and did not work.
I was able to fix this (rather I would call work around) by using following annotation,
#XmlTransient
I have no other clue why does it not working with java.uitl.Map. Any thoughts would really give thumbs up!! I have posted to Oracle support, even they have not came back yet. Is there any know issues with java.util.Map/Collection class with combination of WEblogic12c/Annotations.
[EDIT - 1]
To answer Doughan question, methods which return non collection data type does not throw any exception, for eg:
#ExcludeAttribute
public Integer getOperations(){
return 1;
}
Where #ExcludeAttribute is custom annotation defines '#Retention(RetentionPolicy.RUNTIME)', and I do not need to define #XmlTransient to ignore.
I am bit confused to with usage of retention run time annotation , and not sure if I need to keep it or should use XMLTransient annotation.
[Edit 2 ,Based on #Doughan's answer]
I understand that we need to explicitly annotate getter methods ( as #XMLTransient) if they are not to be mapped from Weblogic 12C, and this is no way related to RuntTime Retention annotations. So any stack upgrade to 12C should update code base with this annotation if there unmapped public getter methods. I think is pretty much answers my concerns.
Correct me if I am wrong.
The existing code base already has annotated with Runtime annotation, and I thought its the one causing issue.
Detailed stack trace follows
weblogic.application.ModuleException: [HTTP:101216]Servlet:
"com.chordiant.component.cxradecisions.decision.impl.internal.AssessmentDecisionInterfaceWebServiceWrapper"
failed to preload on startup in Web application: "/ra".
com.sun.xml.ws.spi.db.DatabindingException: Descriptor Exceptions:
Exception [EclipseLink-59] (Eclipse Persistence Services -
2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DescriptorException Exception
Description: The instance variable [responseButtons] is not defined in
the domain class [com.chordiant.dm.ra.bean.Assessment], or it is not
accessible. Internal Exception: java.lang.NoSuchFieldException:
responseButtons Mapping:
org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping[responseButtons]
Descriptor: XMLDescriptor(com.chordiant.dm.ra.bean.Assessment --> [])
Runtime Exceptions:
at com.sun.xml.ws.db.toplink.JAXBContextFactory.newContext(JAXBContextFactory.java:185)
at com.sun.xml.ws.spi.db.BindingContextFactory.create(BindingContextFactory.java:179)
at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:211)
at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:185)
And I have a method getResponseButtons() defined in Assessment class
#ExcludeAttribute
public Map getResponseButtons() {
Map map = new HashMap();
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
In WebLogic 12.1.1 you will need to annotate that property with #XmlTransient:
#ExcludeAttribute
public Map getOperations(){
Map map = new HashMap();
//some operation
return map;
}
#ExcludeAttribute is custom annotation created by us, which uses
#Retention(RetentionPolicy.RUNTIME), ( I have provided snippet of this
annotation)
Custom annotations do not affect how MOXy produces its mapping metadata. There is no way that it could, just because the annotation is called #ExcludeAttribute MOXy couldn't assume it should be treated like #XmlTransient.
But issue in one of the web service component causing entire
application failed to deploy in web logic. It works perfectly fine
with WebSphere 8.
EclipseLink MOXy is the default JAXB provider in WebLogic as of version 12.1.1. You may be hitting an issue where previously MOXy treated all properties with only a getmethod as write only properties. New versions of MOXy will ignore these properties unless they are explicitly annotated. This may have caused it to appear to you that the #ExcludeAttribute annotation was having an effect.
I am bit confused to with usage of retention run time annotation
This setting is related to whether or not you can access this annotation via reflection at runtime. Are you creating your own annotation for your own purposes?
When deploying the EAR, Application sever throws 'Exception
[EclipseLink-59] (Eclipse Persistence Services -
2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DescriptorException'
If the contents of that property are meant to be mapped could you share the complete stack trace?

java logging properties : Log a certain class to a specific log file

I use a property file for java.util.logging and want to log all classes under package:
aaa.bbb.ccc.*
the normal way (i.e. info, fine, finer) but class
aaa.bbb.ccc.ddd.MyClass
in its own logging file "My Class.log" with level finer.
The config should be done only via a properties file. How would this look like?
I tried various ways (e.g. different handlers) but not any succeeded: It never worked that both log files are written to.
To make the problem more concrete - the config I tried:
handler.performance.class=com.logging.handler.FileHandler
handler.performance.file=${LOGGING_ROOT}/performance.log
handler.performance.level=FINE
handler.fine.class=com.logging.handler.FileHandler
handler.fine.file=${LOGGING_ROOT}/finer.log
handler.fine.level=FINE
handler.async.class=com.logging.handler.AsyncBufferHandler
handler.async.level=ALL
handler.async.targets=fine
handler.asyncperf.class=com.logging.handler.AsyncBufferHandler
handler.asyncperf.level=ALL
handler.asyncperf.targets=performance
com.myapp.handlers=async,console
com.myapp.useParentHandlers=false
com.myapp.common.logging.handlers=asyncperf
com.myapp.common.logging.useParentHandlers=false
The class I want to log to this separate performance log is located beneath com.myapp.common.logging...
Found the solution - it was a wrong initialization:
The logger should be initialized with:
Logger.getLogger(MyClass.class.getName())
Then the config:
com.myapp.common.logging.MyClass.handlers=asyncperf
com.myapp.common.logging.MyClass.useParentHandlers=false
logs all logging messages of this class in the specified separate file as desired!
Define two File appenders for the two target files
Define one root logger to use the first appender
Define a second logger for the special class, to use the other appender
set additivity of the logger to false in order to make any message go to one but not both files
I think that you have problem because you can't configure 2 default FileHandlers, only one of them. So try to implement your personal subclass of FileHandler and configure it as a separate handler.
I don't remember if we can configure logger for separate class or only for package, so try also configure handlers to package of MyClass.

Categories