One Tomcat two spring application (war) two seperate logging configurations - spring

As mentioned in the title I have two applications with two different logging configurations. As soon as I use springs logging.file setting I can not seperate the configurations of both apps.
The problem worsens because one app is using logback.xml and one app is using log4j.properties.
I tried to introduce a new configuration parameter in one application where I can set the path to the logback.xml but I am unable to make the new setting work for all logging in the application.
public static void main(String[] args) {
reconfigureLogging();
SpringApplication.run(IndexerApplication.class, args);
}
private static void reconfigureLogging() {
if (System.getProperty("IndexerLogging") != null && !System.getProperty("IndexerLogging").isEmpty()) {
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
// Call context.reset() to clear any previous configuration, e.g. default
// configuration. For multi-step configuration, omit calling context.reset().
System.out.println("SETTING: " + System.getProperty("IndexerLogging"));
System.out.println("SETTING: " + System.getProperty("INDEXER_LOG_FILE"));
context.reset();
configurator.doConfigure(System.getProperty("IndexerLogging"));
} catch (JoranException je) {
System.out.println("FEHLER IN CONFIG");
}
logger.info("Entering application.");
}
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
reconfigureLogging();
return application.sources(applicationClass);
}
The above code works somehow. But the only log entry which is written to the logfile specified in the configuration, which ${IndexerLogging} points to, is the entry from logger.info("Entering application."); :(
I don't really like to attach that code to every class which does some logging in the application.
The application has to be runnable as tomcat deployment but also as spring boot application with integrated tomcat use.
Any idea how I can set the path from ${IndexerLogging} as the path to read the configuration file when first configuring logging in that application?

Take a look at https://github.com/qos-ch/logback-extensions/wiki/Spring you can configure the logback config file to use.

Related

Spring sleuth Baggage key not getting propagated

I've a filter (OncePerRequestFilter) which basically intercepts incoming request and logs traceId, spanId etc. which works well,
this filter lies in a common module which is included in other projects to avoid including spring sleuth dependency in all of my micro-services, the reason why I've created it as a library because any changes to library will be common to all modules.
Now I've to add a new propagation key which need to be propagated to all services via http headers like trace and spanId for that I've extracted current span from HttpTracing and added a baggage key to it (as shown below)
Span span = httpTracing.tracing().tracer().currentSpan();
String corelationId =
StringUtils.isEmpty(request.getHeader(CORELATION_ID))
? "n/a"
: request.getHeader(CORELATION_ID);
ExtraFieldPropagation.set(CUSTOM_TRACE_ID_MDC_KEY_NAME, corelationId);
span.annotate("baggage_set");
span.tag(CUSTOM_TRACE_ID_MDC_KEY_NAME, corelationId);
I've added propagation-keys and whitelisted-mdc-keys to my application.yml (with my library) file like below
spring:
sleuth:
propagation-keys:
- x-corelationId
log:
slf4j:
whitelisted-mdc-keys:
- x-corelationId
After making this change in filter the corelationId is not available when I make a http call to another service with same app, basically keys are not getting propagated.
In your library you can implement ApplicationEnvironmentPreparedEvent listener and add the configuration you need there
Ex:
#Component
public class CustomApplicationListener implements ApplicationListener<ApplicationEvent> {
private static final Logger log = LoggerFactory.getLogger(LagortaApplicationListener.class);
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
log.debug("Custom ApplicationEnvironmentPreparedEvent Listener");
ApplicationEnvironmentPreparedEvent envEvent = (ApplicationEnvironmentPreparedEvent) event;
ConfigurableEnvironment env = envEvent.getEnvironment();
Properties props = new Properties();
props.put("spring.sleuth.propagation-keys", "x-corelationId");
props.put("log.slf4j.whitelisted-mdc-keys:", "x-corelationId");
env.getPropertySources().addFirst(new PropertiesPropertySource("custom", props));
}
}
}
Then in your microservice you will register this custom listener
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(MyApplication.class)
.listeners(new CustomApplicationListener()).run();
}
I've gone through documentation and seems like I need to add spring.sleuth.propagation-keys and whitelist them by using spring.sleuth.log.slf4j.whitelisted-mdc-keys
Yes you need to do this
is there another way to add these properties in common module so that I do not need to include them in each and every micro services.
Yes, you can use Spring Cloud Config server and a properties file called application.yml / application.properties that would set those properties for all microservices
The answer from Mahmoud works great when you want register the whitelisted-mdc-keys programatically.
An extra tip when you need these properties also in a test, then you can find the anwser in this post: How to register a ApplicationEnvironmentPreparedEvent in Spring Test

Share application.properties files in different project

Below showing the project structure
Core Project
|-config project
|
|-Service project
After building the core project we get Service.jar file.
While running the service.jar am passing spring.config.additional.location as command line argument.
java -jar Service-1.0.jar --spring.config.additional-location=C:/Users/Administrator/Desktop/Springboot/
above spring.config.additional.location path having application.property file and some xml files.
I can able to read application property file in service project ,following logic
Application.propertes
external.config=C:/Users/Administrator/Desktop/Springboot/config/
Mian Class
#ImportResource(locations = {
"${external.config}"+"/spring/service-config.xml",
"${external.config}"+"/spring/datasource-config.xml"
})
public class ServiceMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(ServiceMain.class)
.build()
.run(args);
for (String name : applicationContext.getBeanDefinitionNames()) {
}
}
}
Similar kind of logic applied in config project is given below,its not working
#Configuration
public class ConfigurationFactory
{
#Value("${external.config}")
public String extConfPath;
public String REQ_CONF = extConfPath+"/Configuration.xml";
public static final String FILTER_XML_CONF = extConfPath+"/DocFilter.xml";
}
Is there any better way to do this? How can i read external application.properties in config project
Do we have any better way to do this in spring boot
As you are cleary developing a distributed web system the best practice is to used externalised configuration used by your different services allowing you to update settings without redeployment. Take a look at Spring Cloud Config

How to live reload without restart on additional static resources in spring boot?

I'm attempting to set a new resource location on a spring boot project that is enabled for live reloads but not for application restarts. I'm able to add additional resources, but any change made in the new directory causes the application to restart.
The documentation on this seems light. I must be misinterpreting something.
My layout looks like this:
- src
- main
- java
- resources
- static
- web
- dist
And my application class looks like this:
#Bean
WebMvcConfigurer configurer () {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/dist/**")
.addResourceLocations(""file:web/dist/"")
.setCachePeriod(0);
}
};
}
public static void main(String[] args)
{
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new PropertyLogger());
Properties properties = new Properties();
properties.setProperty("spring.devtools.restart.additional-paths", "web/dist/");
properties.setProperty("spring.devtools.restart.additional-exclude", "/dist/**");
app.setDefaultProperties(properties);
app.run(args);
}
I've read through several similar questions and this seems to be the best I can do. Is it possible to enable live reload on dist without a full application restart?
By the way, my IDE is IntelliJ. I'm beginning to wonder if IntelliJ needs to exclude the dist directory. I'll followup if that's the case.
I've beaten this to death and have finally found a working solution.
Properties properties = new Properties();
properties.setProperty("spring.devtools.restart.additional-paths", "web/");
properties.setProperty("spring.devtools.restart.additional-exclude", "dist/**");
app.setDefaultProperties(properties);
Defining the web directory as an additional path combined with the pattern used for additional exclude does the trick.
I won't mark this as accepted unless there are upvotes to back my conclusion.

Spring Boot Devtools: Detect automatic restart and apply configuration

I want to set properties that apply only during the automatic restarts provided by Spring Boot developer tools. Is there a way to achieve this?
In other words, is there a way for some part of my code (maybe a configuration bean or a listener) to detect that a restart is under way?
In my specific use case I want to run some SQL scripts during the regular Spring Boot application startup, but not after Devtools has triggered a restart (so that my database state doesn't change during restarts).
Here's an idea:
It's confusing to explain, but you'll see with the code below. When Spring-Boot starts with devtools in its dependencies, it first starts and then immediately restarts a first time trough devtools. You could dynamically add command line argument to track the restarts and change the Spring profile being used when devtools is restarting:
#SpringBootApplication
public class App {
public static void main(String[] args)
throws ParseException, IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
String profile = "";
//(1) Very first time - Spring Boot doesn't really load, it only kick start devtool then restarts.
if (args.length == 0) {
args = new String[] { "spring-boot-loaded" };
profile = "no-devtools-yet";
}
//(2) The first time the application loads with devtools
else if (args.length == 1 && args[0].equals("spring-boot-loaded")) {
args = new String[] { "spring-boot-loaded", "devtools-loaded" };
profile = "devtools";
Field argsField = Restarter.class.getDeclaredField("args");
argsField.setAccessible(true);
argsField.set(Restarter.getInstance(), args);
}
//(3) This is the first restart - You don't want to re-initialized the database here
else {
profile = "devtools-reloaded";
}
new SpringApplicationBuilder() //
.sources(App.class)//
.profiles(profile) //
.run(args);
}
}
The sketchy part is that the Restarter keeps the original arguments (which would be "no-devtools-yet" in this example). So when devtools first starts, you need to replace the Restarter's internal args

Log4j2 in a spring web app with servlet 3.0

I need to change the default location of log4j2 configuration file. I followed the documentation here
https://logging.apache.org/log4j/2.x/manual/webapp.html
But the only file log4j2 can see is log4j2.xml in the classpath. otherwise I get "no log4j2 configuration file found"
I tried:
-1 setting context parameters
-2 setting system property Log4jContextSelector to "org.apache.logging.log4j.core.selector.JndiContextSelector". and using the JNDI selector
as described here
https://logging.apache.org/log4j/2.x/manual/webapp.html#ContextParams
-3 lookups: web, env, sys, ctx and bundle. the first 4 failed only bundle worked but you can only lookup inside the classpath.
-4 set isLog4jAutoInitializationDisabled to true, and I am not sure how to configure the filter in this case. If I include them in the web.xml the app will not deploy.
jar in the project
./WEB-INF/lib/log4j-jcl-2.4.1.jar
./WEB-INF/lib/log4j-core-2.4.1.jar
./WEB-INF/lib/log4j-slf4j-impl-2.4.1.jar
./WEB-INF/lib/log4j-api-2.4.1.jar
In my situation with .propeties file I use code shown below
#Plugin(name = "LogsConfigurationFactory", category = ConfigurationFactory.CATEGORY)
public class CustomLogsConfigurationFactory extends PropertiesConfigurationFactory {
#Override
public Configuration getConfiguration(String name, URI configLocation) {
File propFile = new File("/path_to/log4j2.properties");
return super.getConfiguration(name, propFile.toURI());
}
#Override
protected String[] getSupportedTypes() {
return new String[] {".properties", "*"};
}
}
I think you can change CustomLogsConfigurationFactory on XmlConfigurationFactory, and change return typse in getSupportedTypes method. I hope this will help you.

Resources