I have a spring boot application that uses spring logback for logging. My application is being deployed in an external tomcat. By default the path taken up by the log file resides in tomcat's directory rather than webapp roots sub directory.
Eg: instead of creating the logs inside tomcat -> logs -> app.log
I would like it to be under tomcat -> webapps -> spring-app -> logs -> app.log
Since the path is only known at runtime, I have declared a logback-configuration as app-logback-spring.xml used springProperty to populate the path.
public class LoggingInitializer implements ApplicationContextInitializer{
#Override
public void initialize(ConfigurableApplicationContext appContext) {
try {
URI uri = appContext.getResource("/").getURI();
File file = new File(uri);
Map<String, Object> properties = new HashMap<>();
properties.put("app-root.path", file.getAbsolutePath());
properties.put("app.log.path", file.getAbsolutePath()+"/logs");
System.out.println("props "+properties);
MapPropertySource mapPropSrc = new MapPropertySource("custom", properties);
appContext.getEnvironment().getPropertySources().addFirst(mapPropSrc);
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
File configFile = new File(file.getAbsolutePath()+File.separator+"WEB-INF"+File.separator
+"classes"+File.separator+"app-logback-spring.xml");
InputStream configStream = Files.newInputStream(configFile.toPath());
configurator.setContext(loggerContext);
configurator.doConfigure(configStream); // loads logback file
configStream.close();
} catch (IOException e) {
throw new CustomException("Could not initialize application", e);
} catch (JoranException e) {
throw new CustomException("Error configuring logger", e);
}
}
}
<configuration>
<springProperty scope="context" name="logpath" source="app.log.path"/>
<appender name="RollingFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logpath}/app.log</file>
---
---
</configuration>
However, during the initialization I suppose the logpath is not recognized. I infer it based on a folder that is created under tomcat with name logpath_IS_UNDEFINED and the app.log goes under it.
The app-root.path that I set is available for services with correct value. I know I can write to app-logback-spring.xml before reading it, but that looks like a convoluted way.
Is it because when the logger is loading, the spring property is not available is it?
Related
i am useing springboot create a project ,it's no error run idea,but, run app.jar file ,it's run Exception like this
java.io.FileNotFoundException: class path resource [templates/] cannot be resol
ed to absolute file path because it does not reside in the file system: jar:fil
:/E:/projects/help/target/zhx-help-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/templa
es/
at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:21
)
at org.springframework.core.io.AbstractFileResolvingResource.getFile(Ab
tractFileResolvingResource.java:52)
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.get
emplateLoaderForPath(FreeMarkerConfigurationFactory.java:338)
at org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.cre
teConfiguration(FreeMarkerConfigurationFactory.java:290)
at org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
afterPropertiesSet(FreeMarkerConfigurer.java:116)
at org.springframework.beans.factory.support.AbstractAutowireCapableBea
Factory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBea
Factory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
at org.springframework.beans.factory.support.AbstractAutowireCapableBea
Factory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBea
Factory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getO
ject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegist
y.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetB
an(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBea
(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory
springboot version :1.5.2
useing spring-data-jpa
I see you use freemarker. In spring boot you cannot use the normal File approach to get your templates because they are not accessible when you run an executable JAR (File cannot be loaded as a resource when inside the JAR)
Use the following approach to load your templates folder:
cfg.setTemplateLoader(new ClassTemplateLoader(getClass().getClassLoader(), "templates"));
Full example:
#Configuration
public class FreemarkerConfiguration {
#Bean
public freemarker.template.Configuration freemarkerConfig() throws IOException {
freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23);
cfg.setTemplateLoader(new ClassTemplateLoader(getClass().getClassLoader(), "templates"));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
return cfg;
}
}
If you are using jersey with spring boot, Jersey doesn't work well with Spring Boot. The reason for this error is, jersey is not able to auto discover all the rest resources. To fix this, register all the resources explicitly, registering package doesn't work. Hope this issue will be fixed in future versions of jersey.
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Resource1.class);
register(Resource2.class);
register(Resource3.class);
}
}
FreeMarkerConfigurationFactory.class
protected TemplateLoader getTemplateLoaderForPath(String templateLoaderPath) {
if(this.isPreferFileSystemAccess()) {
try {
Resource ex = this.getResourceLoader().getResource(templateLoaderPath);
File file = ex.getFile();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Template loader path [" + ex + "] resolved to file path [" + file.getAbsolutePath() + "]");
}
return new FileTemplateLoader(file);
} catch (IOException var4) {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Cannot resolve template loader path [" + templateLoaderPath + "] to [java.io.File]: using SpringTemplateLoader as fallback", var4);
}
return new SpringTemplateLoader(this.getResourceLoader(), templateLoaderPath);
}
} else {
this.logger.debug("File system access not preferred: using SpringTemplateLoader");
return new SpringTemplateLoader(this.getResourceLoader(), templateLoaderPath);
}
}
so make log lever info
<logger name="org.springframework.web" level="INFO"/>
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.
Using Apache Commons Configurations 1.9, how to avoid ConfigurationException upon loading a configuration file if the provided file cannot be found?
The Spring app context resembles:
<bean name="foo.config" class="org.apache.commons.configuration.PropertiesConfiguration" init-method="load">
<property name="fileName" value="foo.properties" />
</bean>
However my config file is optional, so I want to make sure the application starts correctly even the file doesn't exist.
How can I achieve this with Commons Configurations? A FactoryBean works, but is there another way?
if (!file.exists()) return new PropertiesConfiguration();
Or using try/catch syntax using an XML configuration:
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
public class Workspace {
private final XMLConfiguration mConfig;
public Workspace() {
final var configs = new Configurations();
XMLConfiguration config;
try {
config = configs.xml( "filename.xml" );
} catch( final Exception e ) {
config = new XMLConfiguration();
}
mConfig = config;
}
Using a regular properties configuration will work the same way.
I have a code like follows
public LocalFileStorage(String storageUrl, Resource storageDirectory) {
this.storageUrl = storageUrl;
try {
this.storageDirectory = storageDirectory.getFile();
this.storageDirectory.deleteOnExit();
this.storageDirectory.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
I call the class the follows.
private ResourceLoader resourceLoader; // from spring
LocalFileStorage pictureStorage = new LocalFileStorage(Url+ "/resources/", resourceLoader.getResource("/resources/"));
call to
resourceLoader.getResource("/resources/")
throws exception. I thought ResourceLoader loads directory also because after all directory is also a file.
My structure
Typically, only anything inside /WEB-INF/classes, /WEB-INF/lib, /WEB-INF/... will be added to the classpath and accessible through ClassLoader.getResource(). The folder you are trying to access is not in WEB-INF and will therefore not appear in the classpath.
Assuming you are using something similar to Maven, you should put resource files under /src/main/resources. When your project is built, those files will end up in WEB-INF/classes.
I have a hadoop job which includes some spring beans. Also, in the spring context file, there is a PropertyPlaceholderConfigurer named app.properties.
This app.properties is within the jar file, the idea is remove it from the jar file in order to change some properties without re compile.
I tried the -file option, the -jarlibs option but neither worked.
Any ideas?
What I did was:
Subclass the PropertyPlaceholderConfigurer
Override loadProperties method
If there is a custom System.getProperty("hdfs_path")
try {
Path pt = new Path(hdfsLocationPath);
FileSystem fs = FileSystem.get(new Configuration());
BufferedReader br = new BufferedReader(new InputStreamReader(fs.open(pt)));
props.load(br);
} catch (Exception e) {
LOG.error(e);
}
works like a charm ...
You can add this properties file to the distributed cache as follows :
...
String s3PropertiesFilePath = args[0];
DistributedCache.addCacheFile(new URI(s3PropertiesFilePath), conf);
...
Later, in configure() of your mapper/reducer, you can do the following:
...
Path s3PropertiesFilePath;
Properties prop = new Properties();
#Override
public void configure(JobConf job) {
s3PropertiesFilePath = DistributedCache.getLocalCacheFiles(job)[0];
//load the properties file
prop.load(new FileInputStream(s3PropertiesFilePath.toString()));
...
}
PS: If you are not running it on Amazon EMR, then you can keep this properties file in your hdfs and provide that path instead.