How do I programmatically tell Logback to Reload Configuration - java

I know there's a reloadDefaultConfiguration() jmx operation, but without getting an instance of MBean and invoking this operation, is there a Logback api to reload the default configuration (optionally specifying a log configuration file path)?

This is the source code of JMXConfigurator.reloadDefaultConfiguration():
public void reloadDefaultConfiguration() throws JoranException {
ContextInitializer ci = new ContextInitializer(loggerContext);
URL url = ci.findURLOfDefaultConfigurationFile(true);
loggerContext.reset();
ci.configureByResource(url);
}
What about just running this code wherever you need it?
The only problem is the loggerContext variable. You can obtain it using:
(LoggerContext)LoggerFactory.getILoggerFactory()
Unfortunately it doesn't look like there is well-factored API to do this, what about raising an issue? Also are you aware that Logback has a built-in auto-refreshing feature?

I used the following code for this purpose:
public static void reloadLogger() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ContextInitializer ci = new ContextInitializer(loggerContext);
URL url = ci.findURLOfDefaultConfigurationFile(true);
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
loggerContext.reset();
configurator.doConfigure(url);
} catch (JoranException je) {
// StatusPrinter will handle this
}
StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
}

Related

How to separate a Log4j configuration?

I've got a program that has a log4j configuration written in XML. I am trying to modify the original application, and attempting to improve upon the previous logger config.
Since I cannot modify the xml file itself, I want to be able to generate a new configuration through the ConfigurationBuilderFactory, and use it alongside the other config. The only issue is that, I am unable to accomplish this. It does not seem to want to work with both.
What can I do?
The following is my code, greatly simplified:
/**
* Internally uses {#link org.apache.logging.log4j.Logger}
*/
public final class MyLogger {
private static final LoggerContext context;
static {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
{
builder.setStatusLevel(WARN);
AppenderComponentBuilder console = builder.newAppender("SysOut", "Console");
console.addAttribute(...);
console.add(builder.newLayout(...).addAttribute(...));
builder.add(console);
// ... more configuration below
}
context = Configurator.initialize(builder.build()); // only works if no previous config exists, but will not replace an old config
}
}
// later on...
context.getLogger("MyLogger"); // uses the xml config, not the one written above
For creating loggers using the ConfigurationBuilder API programmatically you can refer below code.
It creates a logger in log4j2 environment added with some layout and appenders defined :
public class Log4j2Logger {
int counter = 0;
LoggerContext ctx;
Configuration config;
Logger logger;
String loggerName = "testLogger";
String appenderName = "myAppender";
static String testMessage = "This is a Test Message";
public void log() {
final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
final LoggerComponentBuilder loggerComp = builder.newLogger(loggerName, Level.ALL).addAttribute("additivity",
false);
builder.add(loggerComp);
config = builder.build();
ctx = Configurator.initialize(config);
config = ctx.getConfiguration();
ctx.start(config);
ctx.updateLoggers(config);
// To create/add the logger of the configuration specified above we can use the
// getLogger(..) method
logger = ctx.getLogger(loggerName);
// Now we need to attach an appender to the logger so that our messages could be
// logged
logger.addAppender(addConsoleAppender(ctx.getConfiguration(), appenderName));
while (counter < 10) {
logger.error(testMessage + counter);
counter++;
}
}
private Appender addConsoleAppender(Configuration config, String appenderName) {
Appender consoleAppender = ConsoleAppender.newBuilder().setConfiguration(config).setName(appenderName)
.withImmediateFlush(true).build();
consoleAppender.start();
return consoleAppender;
}
}
And for testing, you can have following in any test class:
Log4j2Logger testLogger = new Log4j2Logger();
testLogger.log();
This API helps you to handle logs in a powerful way.
You can :
Create multiple loggers with your configuration
Add multiple Appenders to it.
Configure them also.
Remove logger when the usage is over.
Create Asynchronous Loggers also.
PS : I have used log4j2 version 2.12.1.
I think you can create your own log4j.xml. You have to ensure that your XML will be loaded in your program. So just define the resource containing your XML in the Java Classpath before the resource containing the other XML.

Environment Specific application.properties file in Spring non-Boot application

I'd like to accomplish this: Environment Specific application.properties file in Spring Boot application
in a Spring non-Boot application. Any idea on how to do that? Now I am setting environment variables to tell the application which properties to use, would prefer to do it the "boot" way.
Help would be appreciated.
In order to represent the several environments use profiles. If you want to know more browse this site. and I think this is exactly what you are looking for.
Update 1:
Considering you have a fixed suffix of your property files and you have a set of property files for different environment, for example,
development-it_wroks.properties,
test-it_wroks.properties etc. etc.
etc.it_wroks
is the suffix
Determine the active enviourment from active_env.properties
profiles.active: development
#profiles.active: test
#profiles.active: stage
#profiles.active: production
Write a custom Property resolver
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
public class MyPropertyUtil {
public static String getValuesFromPerpertyFile(String filename,String key){
String value = null;
Configuration config = getConfiguration(filename);
value = config.getString(key);
return value;
}
public static Configuration getConfiguration(String file){
Configuration config = null;
try{
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration>
builder =new FileBasedConfigurationBuilder
<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties().setFileName(file));
config = builder.getConfiguration();
}catch(Exception ex){
ex.printStackTrace();
}finally{
}
return config;
}
}
Now your calling class
import org.apache.log4j.Logger;
public class MyCallingClass {
final static Logger logger = Logger.getLogger(this.getClass());
//Determine the active enviourment,You may determine this from os environment variable if you want
String activeEnvironment = MyPropertyUtil.
getValuesFromPerpertyFile("resource/active_env.properties"
,"profiles.active");
//Set the property file
String myEnvSpecificValue = MyPropertyUtil.
getValuesFromPerpertyFile("resource/"+activeEnvironment+"it_wroks.properties",
"my.property.string");
//Do what you want to
logger.info(myEnvSpecificValue);
}
You can add application-environment.properties as per environment. Spring boot should automatically detect the corresponding properties file based on active environment.

Location of AntiSamy policy file in web project

I'm trying to use AntiSamy to prevent XSS attacks on my site. I downloaded the following jars and added them to "/WEB-INF/lib"
antisamy-1.5.3.jar
nekohtml.jar
xercesImpl-2.5.0.jar
along with a policy file antisamy-slashdot-1.4.4.xml in "/WEB-INF".
I tried to implement a filter through web.xml. A snippet of the servlet I'm using is
public class AntiSamyFilter implements Filter {
private static final Logger LOG = Logger.getLogger(AntiSamyFilter.class);
private final AntiSamy antiSamy;
public AntiSamyFilter() {
try {
URL url = this.getClass().getClassLoader().getResource("antisamy-slashdot-1.4.4.xml");
LOG.info("After getResource");
Policy policy = Policy.getInstance(url.getFile()); //Deployment fails
LOG.info("After Policy");
antiSamy = new AntiSamy(policy);
LOG.info("After antiSamy");
} catch (PolicyException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
The deployment fails after Policy policy = Policy.getInstance(url.getFile());. It's probably because of the path of the policy file.
Can someone please tell me where the policy file should be kept?
The url.getFile part fails because it couldn't find the antisamy-slashdot-1.4.4.xml file. I created a package in src/my/package and changed
URL url = this.getClass().getClassLoader().getResource("antisamy-slashdot-1.4.4.xml");
to
URL url = this.getClass().getClassLoader().getResource("/my/package/antisamy-slashdot-1.4.4.xml");
I also added batik.jar along with the other jar files. It solved my problem

how to set loglevel from INFO to ERROR using log4j2 api at runtime?

logger.setLevel() method is not available in log4j2 API. So how to set log level at run time.
I'm not sure if this is the best way, but you set the level on org.apache.logging.log4j.core.config.LoggerConfig which you can get from the LoggerContext via the LogManager.
Once set, you can update the loggers with the new configuration.
As an example:
public static void main(String[] args) {
Logger log = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
log.error("An error");
log.debug("A debug");
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration conf = ctx.getConfiguration();
conf.getLoggerConfig(LogManager.ROOT_LOGGER_NAME).setLevel(Level.DEBUG);
ctx.updateLoggers(conf);
log.error("Another error");
log.debug("Another debug");
}
Yields:
14:03:41.346 [main] ERROR - An error
14:03:41.348 [main] ERROR - Another error
14:03:41.348 [main] DEBUG - Another debug
Credit to amcintosh, I wrapped their answer in a function:
/** Override the logging level of a given logger, return the previous level */
public static Level setLevel(Logger log, Level level) {
LoggerContext ctx = (LoggerContext)LogManager.getContext(false);
Configuration conf = ctx.getConfiguration();
LoggerConfig lconf = conf.getLoggerConfig(log.getName());
Level oldLevel = lconf.getLevel();
lconf.setLevel(level);
ctx.updateLoggers(conf);
return oldLevel;
}
Despite amoe's comment, this seems to be working correctly for me using Log4J 2.5.
Gary Gregory is correct.
Also the answer to this question is right there on the FAQ page in log4j2's site
https://logging.apache.org/log4j/2.x/faq.html#reconfig_level_from_code
Sample Code below:
Configurator.setLevel(logger.getName(), Level.INFO);
On my side, i had to use this code in order to have this working fine (based on previous answers).
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
...
public static void changeLoggerLevel(final String module, final Level level) {
String moduleRenamed = module.replaceAll("/", ".");
LoggerContext ctx = (LoggerContext)LogManager.getContext(false);
AbstractConfiguration configuration = (AbstractConfiguration) ctx
.getConfiguration();
if (configuration.getLogger(moduleRenamed) != null) {
LoggerConfig loggerConfig = configuration.getLoggerConfig(moduleRenamed);
loggerConfig.setLevel(level);
} else {
LoggerConfig loggerConfig = new LoggerConfig(moduleRenamed, level, true);
configuration.addLogger(moduleRenamed, loggerConfig);
}
ctx.updateLoggers(configuration);
}
The problem was with the getLoggerConfig() call; if the module you are trying to give a new level is not yet registered, this method returns the root logger (or any intermediate sub path registered), and thus instead of altering the level for com.mycompany you will alter root or com level. That's why you have to add a new LoggerConfig in case the module to alter is not yet registered.
The following APIs in the class org.apache.logging.log4j.core.config.Configurator allow you to change Levels:
setAllLevels(String, Level)
setLevel(Map)
setLevel(String, Level)
setRootLevel(Level)

How to specify Log4J 2.x config location?

Is there any way to specify Log4J 2.x log4j2.xml file location manually (like DOMConfigurator in Log4J 1.x), without messing with classpath and system properties?
You could use the static method #initialize(String contextName, ClassLoader loader, String configLocation) (see source here) in org.apache.logging.log4j.core.config.Configurator.
(You can pass null for the class loader.)
Be aware that this class is not part of the public API so your code may break with any minor release.
For completeness, you can also specify the location of the configuration file with this system property:
-Dlog4j.configurationFile=path/to/log4j2.xml
If you are using log4j2 and properties are in defined in log4j2.properties file then use this.
-Dlog4j2.configurationFile=file:/home/atul/log4j2.properties
For log4j version 2.12.1, you can find how to reconfigure log4j2 here.
Below is an example
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
File file = new File("C:\\Path for Windows OS\\yourConfig.xml");
LoggerContext context = (LoggerContext) LogManager.getContext(false);
context.setConfigLocation(file.toURI());
Logger log = LogManager.getLogger(YourClass.class);
It seems to me that way of configuring log4j2 is changing with new releases, so you should be aware of that.
In Windows, be aware that you need to use a URI with the log4j.configurationFile property
-Dlog4j.configurationFile=file://C:\path\to\log4j2.xml
Using the LoggerContext allows to setConfigLocation.
File f = new File(this.logConfigFile);
URI fc = f.toURI();
System.out.println("Loading logging config file: " + fc);
Logger l = (Logger) LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
l.getContext().setConfigLocation(fc);
or alternatively
LoggerContext.getContext().setConfigLocation(java.net.URI);
You can initialize like below as well
ConfigurationSource source = new ConfigurationSource(new FileInputStream(log4j file Path));
XmlConfiguration xmlConfig = new XmlConfiguration(source);
Logger logger = (Logger) LogManager.getLogger();
logger.getContext().start(xmlConfig);
In each class you can get logger instance as below
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private final Logger logger = LogManager.getLogger(ABC.class);
Step: 1 - Get ready with your log4J.xml file with the appender details (Mostly under the resource folder)
Step: 2 - Following code should be added to the configuration class (In the previous log4J we had PropertyConfigurator and now we need to go with LoggerContext)
String log4JFilePath = "file path of your log4J.xml file";
LoggerContext loggerContext = (LoggerContext)LoggerManager.getContext(false);
File file = new File(log4JFilePath);
loggerContext.setConfigLocation(file.toURI());
Step: 3 - Add the following line to utilise the logger in any classes
private static final Logger logger = LogManager.getLogger(yourClassName.class);
logger.info("log here");
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
public class Foo {
public static void main(String[] args) {
Configurator.initialize(null, "src/main/config/log4j2.xml"); //path specify
Logger logger = LogManager.getLogger(APITestToolMain.class);
logger.info("working");
}
}
resource: https://www.baeldung.com/spring-boot-change-log4j2-location
void initializeLogger()
{
try
{
String basepath=checkpath();
File f = new File(basepath+"\\config\\log4j2.properties");
URI fc = f.toURI();
LoggerContext context = (LoggerContext) LogManager.getContext(false);
context.setConfigLocation(f.toURI());
}
catch (Exception e)
{
errorlog="Unable to load logging property:";
System.out.println(errorlog+": "+e.getMessage());
}
}
This is the way I initialize my log4j2 properties file from a different location, so what I simply do is to call the initializeLogger() method in my main method.
And it works perfectly.
Perhaps you need to see what the checkpath() blocks looks like, I added the function below.
String checkpath()
{
String parentpath="";
try
{
URL url=getClass().getProtectionDomain().getCodeSource().getLocation();
File f=new File(url.toURI());
parentpath=f.getParent();
}
catch(Exception ex)
{
//logger.error("unable to retrieve application parent path");
}
return parentpath;
}

Categories

Resources