How to setup a log4j2 custom appender with Spring Boot? - spring-boot

I am facing challenges on creating a custom appender for my spring boot application. The following exception is thrown:
2019-11-08 10:33:27,897 main ERROR Unable to locate plugin type for
mycustomhandlerclass
2019-11-08 10:33:27,961 main ERROR Unable to locate plugin for
mycustomhandlerclass
2019-11-08 10:33:27,961 main ERROR Unable to invoke factory method in
class org.apache.logging.log4j.core.config.AppendersPlugin for element
Appenders: java.lang.NullPointerException java.lang.NullPointerException
Spring boot version : 2.1.7
Maven plugin version: 3.1.1
Mycustomhandlerclass.java:
import java.io.Serializable;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#Plugin(category = "Core", name = "Mycustomhandlerclass", elementType = "appender")
public class Mycustomhandlerclass extends AbstractAppender {
private final Logger logger = LogManager.getLogger(getClass().getName());
#SuppressWarnings("deprecation")
protected AmtAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions) {
super(name, filter, layout,ignoreExceptions);
// TODO Auto-generated constructor stub
}
#PluginFactory
public static Mycustomhandlerclass createAppender(#PluginAttribute("name") String name, #PluginAttribute("ignoreExceptions") boolean ignore, #PluginElement("Filter") Filter filter, #PluginElement("Layout") Layout<? extends Serializable> layout) {
return new Mycustomhandlerclass(name, filter, layout, ignore);
}
#Override
public void append(LogEvent event) {
// TODO Auto-generated method stub
logger.info("Entered CustomAppender");
}
}
log4j2.properties:
name=PropertiesConfig
appenders = file, errorFile, customAppender
property.filename = D:/logdestinationfolder
appender.file.type = RollingFile
appender.file.name = OUTFILE
appender.file.fileName = ${filename}/console.log
appender.file.filePattern = ${filename}/console%d{yyyy-MM-dd}.log
appender.file.layout.type = PatternLayout
appender.file.layout.pattern = %-5p %d{dd-MM-yyyy HH:mm:ss} %t %c{1} %m%n
appender.file.policies.type = Policies
appender.file.policies.time.type = TimeBasedTriggeringPolicy
appender.file.policies.time.interval = 1
appender.file.strategy.type=DefaultRolloverStrategy
appender.file.strategy.max=5
appender.errorFile.type = RollingFile
appender.errorFile.name = ERROROUTFILE
appender.errorFile.fileName = ${filename}/error.log
appender.errorFile.filePattern = ${filename}/error%d{yyyy-MM-dd}.log
appender.errorFile.layout.type = PatternLayout
appender.errorFile.layout.pattern = %-5p %d{dd-MM-yyyy HH:mm:ss} %t %c{1} %M() :: %L %m%n
appender.errorFile.policies.type = Policies
appender.errorFile.policies.time.type = TimeBasedTriggeringPolicy
appender.errorFile.policies.time.interval = 1
appender.errorFile.strategy.type=DefaultRolloverStrategy
appender.errorFile.strategy.max=5
appender.errorFile.filter.1.type=ThresholdFilter
appender.errorFile.filter.1.onMatch=ACCEPT
appender.errorFile.filter.1.level=ERROR
appender.customAppender.type = Mycustomhandlerclass
appender.customAppender.name = CUSTOMAPPENDER
appender.customAppender.filter.1.type=ThresholdFilter
appender.customAppender.filter.1.onMatch=ACCEPT
appender.customAppender.filter.1.level=WARN
rootLogger.level = ERROR
loggers=appLogger
logger.appLogger.name = com.solartis
logger.appLogger.level = DEBUG
logger.appLogger.appenderRefs=errorFile,file,customAppender
logger.appLogger.appenderRef.errorFile.ref = ERROROUTFILE
logger.appLogger.appenderRef.file.ref = OUTFILE
logger.appLogger.appenderRef.customAppender.ref = CUSTOMAPPENDER
Note: On removing the appender references in log4j2.properties, the logs are written to files in the mentioned path in log4j2.properties. So, the problem is with customappender integration.

You have to add the package of your appender class in log4j.properties e.g.
packages = org.home.appenders

The error is in this code:-
protected AmtAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions) {
super(name, filter, layout,ignoreExceptions);
// TODO Auto-generated constructor stub
}
What is AmtAppender? The constructor name should be same as the class name. You need to provide the constructor in this manner:-
#SuppressWarnings("deprecation")
protected MyCustomAppender(String name, Filter filter)
{
super(name, filter, null);
}

Related

spring boot list all application properties of an app [duplicate]

There is already a question asking for logging the active configuration, there is a correct answer but the problem is that the configuration is logged only if all beans are correctly instantiated. I would like to log all properties even (mainly) if the application crash at startup. My question is more specific:
How to log all active properties of a spring boot application before the beans instantiation?
For doing this you need to register an ApplicationListener. The event to catch is the ApplicationPreparedEvent, according to the documentation:
ApplicationPreparedEvent is an event published when a SpringApplication is starting up and the
ApplicationContext is fully prepared but not refreshed. The bean
definitions will be loaded and the Environment is ready for use at
this stage.
The main method would look like this:
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MyApplication.class);
springApplication.addListeners(new PropertiesLogger());
springApplication.run(args);
}
I've reused the code of the answer cited in the current question but I've modified it because the context you get is not already refreshed and the structure of the environment is not exactly the same as after the startup of the application. I've also printed the properties by property sources: one for the the system environment, one for the system properties, one for the application configuration properties, etc... Note also that the ApplicationPreparedEvent can be triggered multiple times, and that properties are printed only the first time. See Spring Boot issue #8899 for details.
package com.toto.myapp.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import java.util.LinkedList;
import java.util.List;
public class PropertiesLogger implements ApplicationListener<ApplicationPreparedEvent> {
private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);
private ConfigurableEnvironment environment;
private boolean isFirstRun = true;
#Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
if (isFirstRun) {
environment = event.getApplicationContext().getEnvironment();
printProperties();
}
isFirstRun = false;
}
public void printProperties() {
for (EnumerablePropertySource propertySource : findPropertiesPropertySources()) {
log.info("******* " + propertySource.getName() + " *******");
String[] propertyNames = propertySource.getPropertyNames();
Arrays.sort(propertyNames);
for (String propertyName : propertyNames) {
String resolvedProperty = environment.getProperty(propertyName);
String sourceProperty = propertySource.getProperty(propertyName).toString();
if(resolvedProperty.equals(sourceProperty)) {
log.info("{}={}", propertyName, resolvedProperty);
}else {
log.info("{}={} OVERRIDDEN to {}", propertyName, sourceProperty, resolvedProperty);
}
}
}
}
private List<EnumerablePropertySource> findPropertiesPropertySources() {
List<EnumerablePropertySource> propertiesPropertySources = new LinkedList<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof EnumerablePropertySource) {
propertiesPropertySources.add((EnumerablePropertySource) propertySource);
}
}
return propertiesPropertySources;
}
}
📝 Show the Properties BEFORE application is ready
In my case, I needed to show the properties before the context is loaded. While debugging the app, I would like to log all the properties so that I know what's going on...
☕ Kotlin Implementation
As described at https://www.baeldung.com/spring-boot-environmentpostprocessor, the properties can be collected before the context is loaded through the use of EnvironmentPostProcessor, which is instantiated as part of Spring Factories from the call ConfigFileApplicationListener.loadPostProcessors(). At this point, you can collect all the properties and show in any specific way.
NOTE: While loading properties during this event, the context isn't ready. So, are the loggers. For this reason, the properties can be loaded before the App Banner (if any)
Also, the entry for the spring factory must be present, so create it first
org.springframework.boot.env.EnvironmentPostProcessor=\
cash.app.PropertiesLoggerEnvironmentPostProcessor
Then, create the logger
package cash.app
import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.EnumerablePropertySource
import java.util.*
/**
* This is to log the properties (config and system) before the app loads. This way, we know what will be loaded
* on the app.
* Note that we can't use the logger because the context hasn't built yet at the time it loads the properties twice.
*
* As an event consumer, the method ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent is called
* while the context is building. The process is described at https://www.baeldung.com/spring-boot-environmentpostprocessor
* and one important aspect is that this class is an EnvironmentPostProcessor, only loaded before the App is loaded
* with the assistance of the "src/main/resources/META-INF/spring.factories". It is loaded by the
* ConfigFileApplicationListener.loadPostProcessors(), which looks for the list of classses in the factories.
*
* https://www.baeldung.com/spring-boot-environmentpostprocessor explains how to create AutoConfiguration classes for
* shared libraries. For the case of config, the reload of properties is detailed and explained on the docs at
* https://www.baeldung.com/spring-reloading-properties
*
* TODO: We need to hide the secrets, if they are defined here.
*
* #author Marcello.DeSales#gmail.com
*/
#Order(Ordered.LOWEST_PRECEDENCE)
class PropertiesLoggerEnvironmentPostProcessor : EnvironmentPostProcessor {
companion object {
/**
* Sharing is started immediately and never stops.
*/
private var numberOfPasses: Int = 0
private var systemProperties: MutableMap<String, String> = mutableMapOf()
}
override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
for (propertySource in findPropertiesPropertySources(environment)) {
// Avoid printing the systemProperties twice
if (propertySource.name.equals("systemProperties")) {
numberOfPasses = numberOfPasses?.inc()
} else {
System.out.println("******* \" + ${propertySource.getName()} + \" *******" )
}
// Adaptation of https://stackoverflow.com/questions/48212761/how-to-log-all-active-properties-of-a-spring-boot-application-before-the-beans-i/48212783#48212783
val propertyNames = propertySource.propertyNames
Arrays.sort(propertyNames)
for (propertyName in propertyNames) {
val resolvedProperty = environment!!.getProperty(propertyName!!)
val sourceProperty = propertySource.getProperty(propertyName).toString()
if (resolvedProperty == sourceProperty) {
if (propertySource.name.equals("systemProperties")) {
systemProperties.put(propertyName, resolvedProperty)
} else {
System.out.println( "${propertyName}=${resolvedProperty}" )
}
} else {
if (propertySource.name.equals("systemProperties")) {
systemProperties.put(propertyName, resolvedProperty ?: "")
} else {
System.out.println( "${propertyName}=${sourceProperty} ----- OVERRIDDEN =>>>>>> ${propertyName}=${resolvedProperty}" )
}
}
}
}
// The system properties show up twice in the process... The class is called twice and we only print it in the end.
if (numberOfPasses == 2) {
System.out.println("******* \" System Properties \" *******")
val sysPropertyNames = systemProperties.keys.sorted()
for (sysPropertyName in sysPropertyNames) {
val sysPropertyValue = systemProperties!!.get(sysPropertyName!!)
System.out.println( "${sysPropertyName}=${sysPropertyValue}" )
}
}
}
private fun findPropertiesPropertySources(environment: ConfigurableEnvironment): List<EnumerablePropertySource<*>> {
val propertiesPropertySources: MutableList<EnumerablePropertySource<*>> = LinkedList()
for (propertySource in environment!!.propertySources) {
if (propertySource is EnumerablePropertySource<*>) {
if (propertySource.name.equals("systemProperties") || propertySource.name.contains("applicationConfig:")) {
propertiesPropertySources.add(propertySource)
}
}
}
return propertiesPropertySources.asReversed()
}
}
🔊 Example Logs
Here's the loggers during the bootstrap of one of my services
/Users/marcellodesales/.gradle/jdks/jdk-14.0.2+12/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always
....
....
2022-02-22T21:24:39 INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] o.s.b.devtools.restart.ChangeableUrls : The Class-Path manifest attribute in /Users/marcellodesales/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.2.7s-codec-1.11.jar
2022-02-22T21:24:39 INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
******* " + applicationConfig: [classpath:/application.yaml] + " *******
management.endpoint.health.show-details=always
management.endpoints.web.base-path=/actuator ==========>>>>>> OVERRIDDEN =========>>>>>> management.endpoints.web.base-path=/orchestrator/actuator
management.endpoints.web.exposure.include=*
management.metrics.web.server.request.autotime.enabled=true
spring.application.name=orchestrator-service
spring.boot.admin.client.enabled=false ==========>>>>>> OVERRIDDEN =========>>>>>> spring.boot.admin.client.enabled=true
spring.cloud.discovery.client.composite-indicator.enabled=false
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
******* " + applicationConfig: [classpath:/application-ppd_dev.yaml] + " *******
spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist=true ==========>>>>>> OVERRIDDEN
=========>>>>>> spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist\=true
spring.devtools.livereload.enabled=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.test-connection=true
******* " System Properties " *******
LOG_LEVEL_PATTERN=%5p [,%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]
PID=74720
com.sun.management.jmxremote=
file.encoding=UTF-8
ftion
java.vm.specification.version=14
java.vm.vendor=AdoptOpenJDK
java.vm.version=14.0.2+12
jboss.modules.system.pkgs=com.intellij.rt
jdk.debug=release
line.separator=
os.arch=x86_64
os.name=Mac OS X
os.version=10.16
user.name=marcellodesales
user.timezone=America/Los_Angeles
2022-02-22T21:25:16 DEBUG [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] o.s.b.c.c.ConfigFileApplicationListener : Activated activeProfiles observed,db,ppd_dev
_____ _____ _
/ ____| / ____| | |
| (___ _ _ _ __ ___ _ __| | __ _ ___| |__
2022-02-22T20:41:08 INFO [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74181 --- [ restartedMain]

Problem with #PropertySource and Map binding

I have this specific problem with my yml configuration file.
I have a multi-module maven project as follows:
app
|-- core
|-- web
|-- app
I have this configuration file in core project
#Configuration
#PropertySource("core-properties.yml")
public class CoreConfig {
}
And this mapping:
#Component
#ConfigurationProperties(prefix = "some.key.providers.by")
#Getter
#Setter
public class ProvidersByMarket {
private Map<String, List<String>> market;
}
Here are my core-properties.yml
some.key.providers:
p1: 'NAME1'
p2: 'NAME2'
some.key.providers.by.market:
de:
- ${some.key.providers.p1}
- ${some.key.providers.p2}
gb:
- ${some.key.providers.p1}
When I load the file via profile activation, for example, rename the file to application-core-properties.yml and then -Dspring.profiles.active=core-propertiesit does work however if when I try to load the file via #PropertySource("core-properties.yml") it does not and I get the following error:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-03-27 10:07:36.397 -ERROR 13474|| --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'some.key.providers.by.market' to java.util.Map<java.lang.String, java.util.List<java.lang.String>>:
Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<java.lang.String, java.util.List<java.lang.String>>]
Action:
Update your application's configuration
Process finished with exit code 1
Bacouse you don't have equivalent properties stracture,
example
spring:
profiles: test
name: test-YAML
environment: test
servers:
- www.abc.test.com
- www.xyz.test.com
---
spring:
profiles: prod
name: prod-YAML
environment: production
servers:
- www.abc.com
- www.xyz.com
And config class should be
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class YAMLConfig {
private String name;
private String environment;
private List<String> servers = new ArrayList<>();
// standard getters and setters
I have resolved the issue implementing the following PropertySourceFactory detailed described in here
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(#Nullable String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
I had a similar problem and found a workaround like this:
diacritic:
isEnabled: true
chars: -> I wanted this to be parsed to map but it didn't work
ą: a
ł: l
ę: e
And my solution so far:
diacritic:
isEnabled: true
chars[ą]: a -> these ones could be parsed to Map<String, String>
chars[ł]: l
chars[ę]: e

log4j2 Does Not Write to Log File from Spring Boot Controller in MVC

My apologies if this is a duplicate of another question, but I haven't been able to find a fix in any of the other answers I've found on here yet. The code excerpts below are not all-inclusive of all the code in the files but it has all of the relevant pieces included. I'm unable to completely copy/paste my entire files as this is for a corporate project.
I'm fairly new to spring boot (maybe 10 weeks into using it) and this is the first time I've ever tried using log4j2 (have to use this within our corporate environment.)
build.gradle excerpt:
dependencies {
....
// log4j2
compile("org.springframework.boot:spring-boot-starter-log4j2")
compile("org.apache.logging.log4j:log4j-api")
compile("org.apache.logging.log4j:log4j-core")
compile("org.apache.logging.log4j:log4j-web")
....
}
ApplicationController.java:
package com.test.controllers;
import ...
....
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#Controller
public class ApplicationController {
public static final Logger logger = LogManager.getLogger(Application.class);
}
IndexController.java:
package com.test.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class IndexController extends ApplicationController {
public IndexController() {
}
#RequestMapping("/")
public String index(Model model) {
// just a quick example of logging
logger.debug("--INDEX PAGE--");
return "index/index";
}
}
Application.java (my log file has 1 entry and it's from this class - the "Starting application..." line):
package com.test;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static final Logger logger = LogManager.getLogger(Application.class);
public static void main(String[] args) {
logger.debug("Starting application...");
SpringApplication.run(Application.class, args);
logger.debug("ending application...");
}
}
log4j2.properties:
status = debug
name = PropertiesConfig
property.filename = /var/tmp/logs/test.log
filters = threshold
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appenders = rolling
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = test1-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d %p %C{1.} [%t] %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=100MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20
loggers = rolling
logger.rolling.name = com.test
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
logger.rolling.name = com.test.controllers
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
rootLogger.level = debug
rootLogger.appenderRef.rolling.ref = RollingFile
With the above code, my application creates the /var/tmp/logs/test.log file and writes a single entry to it:
2017-09-28 13:54:56,141 DEBUG c.t.Application [main] Starting application...
However, I also need to access the logger from within my Controllers/etc to allow my entire app to write to this log file. I've attempted to call a logger in my ApplicationController.java and then use it in my IndexController.java. I see the entry from IndexController in my console but it does not write to the log file.
What am I doing wrong? Any help here would be appreciated.

Configure RollingFileAppender programatically Not able to write logs to the specific folder using logback in my spring boot application

I have this Loggerutils class which returns me the logger object.
public class Loggerutils {
public static Logger createLoggerFor(String string, String file) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
ple.setContext(lc);
ple.start();
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<ILoggingEvent>();
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setFileNamePattern("pilogs.%i.log.zip");
rollingPolicy.setMinIndex(1);
rollingPolicy.setMaxIndex(3);
rollingPolicy.setContext(lc);
rollingFileAppender.setFile(file);
rollingFileAppender.setEncoder(ple);
rollingFileAppender.setContext(lc);
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.start();
Logger logger = (Logger) LoggerFactory.getLogger(string);
logger.addAppender(rollingFileAppender);
logger.setLevel(Level.ALL);
logger.setAdditive(false); /* set to true if root should log too */
return logger;
}
}
I use below lines of code to instantiate logger
Logger logger = Loggerutils.createLoggerFor("UserController", "UserControllerFile");
logger.info("Inside User controller..........");
and in application.properties I configured
logging.path=E:/LOGS/WebAPI
but in the given location only one file is being created and that is spring.log. My requirement is I want my log files to be created at specified location.
I don't have logback.xml in my resources folder
You should mention the full path of the log file instead of just the filename. Try this
public class Loggerutils {
public static Logger createLoggerFor(String string, String file) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
ple.setContext(lc);
ple.start();
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<ILoggingEvent>();
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setFileNamePattern("pilogs.%i.log.zip");
rollingPolicy.setMinIndex(1);
rollingPolicy.setMaxIndex(3);
rollingPolicy.setContext(lc);
rollingPolicy.start();
rollingFileAppender.setFile("E:/LOGS/WebAPI/" + file);
rollingFileAppender.setEncoder(ple);
rollingFileAppender.setContext(lc);
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.start();
Logger logger = (Logger) LoggerFactory.getLogger(string);
logger.addAppender(rollingFileAppender);
logger.setLevel(Level.ALL);
logger.setAdditive(false); /* set to true if root should log too */
return logger;
}
}
Made some changes to createLoggerFor() method. And now it works.
#Component
public class Loggerutils {
private static String logDirectory;
#Value("${logging.path}")
public void setLogDirectory(String path){
logDirectory = path;
}
public static Logger createLoggerFor(String loggerName, String fileName) {
Logger rootLogger = (Logger) LoggerFactory.getLogger(loggerName);
LoggerContext loggerContext = rootLogger.getLoggerContext();
loggerContext.reset();
RollingFileAppender<ILoggingEvent> rfAppender = new RollingFileAppender<ILoggingEvent>();
rfAppender.setContext(loggerContext);
rfAppender.setFile(logDirectory+loggerName+"\\"+fileName);
FixedWindowRollingPolicy fwRollingPolicy = new FixedWindowRollingPolicy();
fwRollingPolicy.setContext(loggerContext);
fwRollingPolicy.setFileNamePattern(logDirectory+loggerName+"\\"+"pilogs.%i.log.zip");
fwRollingPolicy.setParent(rfAppender);
fwRollingPolicy.start();
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
triggeringPolicy.setMaxFileSize("10MB");
triggeringPolicy.start();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
encoder.start();
rfAppender.setEncoder(encoder);
rfAppender.setRollingPolicy(fwRollingPolicy);
rfAppender.setTriggeringPolicy(triggeringPolicy);
rfAppender.start();
rootLogger.addAppender(rfAppender);
return rootLogger;
}
}
and logging.path in my application.properties is,
logging.path=E:\\LOGS\\WebAPI\\
Hope this will help :)

Different Log4j file paths for each webapp

I'm trying to use different logs for each webapp. I've multiples applications in the same server and I need to load this directory from a properties file.
Each web app loads a property file with this keys/values (X it's the app's name, different for all):
app=APP.X
logPath=C://logs
I have added a log4j.properties file in each webapp project with all parameters that I need except the log's path (log4j.properties)
# Root logger option
log4j.rootLogger=INFO, stdout, file
# Redirect log messages to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%M:%L - %m%n
# Redirect log messages to a log file, support file rolling.
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.encoding=UTF-8
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%M:%L - %m%n
#log4j.appender.file.MaxBackupIndex=8
log4j.appender.file.DatePattern='.'yyyy-MM
I have created a Java class in my util jar that implements ApplicationListener and adds the key log4j.appender.file.File (Log4jContextListener.java)
public class Log4jContextListener implements ApplicationListener<ContextStartedEvent>
{
#Value("${rutaLogs}")
private String rutaLogs;
#Value("${app}")
private String app;
private Logger log = Logger.getLogger(getClass());
#Override
public void onApplicationEvent(ContextStartedEvent arg0)
{
Properties props = new Properties();
InputStream strm = null;
try
{
System.out.println("LOADING APP: " + app);
strm = Log4jContextListener.class.getClassLoader().getResourceAsStream("log4j.properties");
props.load(strm);
if (props != null)
log.info("PROP: " + props.getProperty("log4j.appender.file.layout"));
props.put("log4j.appender.file.File", logPath+ app + File.separator + app + ".log");
}
catch (IOException propsLoadIOE)
{
throw new Error("can't load logging config file", propsLoadIOE);
}
finally
{
try
{
strm.close();
}
catch (IOException configCloseIOE)
{
throw new Error("error closing logging config file", configCloseIOE);
}
}
// props.put("webAppRoot", event.getServletContext().getRealPath("/"));
PropertyConfigurator.configure(props);
}
}
And each web app adds a bean with this declaration in the applicationContext.xml
<!-- Listener to initialize LOG4J -->
<bean id="log4jConfigurationListener" class="com.framework.listeners.Log4jContextListener" />
The log's folders and files are creating right but the log traces are mixed, each webapp use only the latest location loaded.
How can I do diferent in each wepapp context?
PD: I have to do it reading from properties, I can't use environment vars.
Hi I manage to set log location per webapp context with the following:
package ...
import org.apache.log4j.Logger;
import org.apache.log4j.lf5.LogLevel;
import org.springframework.web.WebApplicationInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.InputStream;
import java.util.Properties;
import java.util.TimeZone;
public class SystemPropertyDefaultsInitializer implements WebApplicationInitializer {
private static final Logger LOG = Logger.getLogger(SystemPropertyDefaultsInitializer.class);
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
String warName = new File(servletContext.getRealPath("/")).getName();
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("log4j.properties");
Properties properties = PropertiesHelper.loadApplicationProperties(resourceAsStream);
String logLocation = "/var/log/tomcat7/" + warName + ".log";
File file = new File(logLocation);
if (!file.exists()) {
FileOutputStream fos = new FileOutputStream(file);
fos.close();
}
if (!file.canWrite()) {
throw new IOException("Write permission denied for file \"" + logLocation + "\"");
}
LOG.info("Log location set to: \"" + logLocation + "\"");
properties.setProperty("log4j.appender.FILE.File", logLocation);
PropertyConfigurator.configure(properties);
}
}

Resources