Log4J 2.8.1 using PropertiesConfigurationBuilder to load properties from strings - log4j2

I am looking for a way to prepare configuration and initialize a logger context from hard coded properties string; not from properties file. Following is the code that seems to be working; it logs the lines that I am expecting as per the log level. However there are few things which I am not able to figure out. I have asked them as inline questions in the code itself; appreciate inputs on the same.
Thanks in advance.
-WinCPP
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("status", "TRACE");
properties.setProperty("appenders", "CONSOLE");
properties.setProperty("appender.CONSOLE.name", "ConsoleAppender");
properties.setProperty("appender.CONSOLE.type", "Console");
properties.setProperty("appender.CONSOLE.layout.type", "PatternLayout");
properties.setProperty("appender.CONSOLE.layout.pattern", "%r [%t] %p %c %notEmpty{%x }- %m%n");
properties.setProperty("rootLogger.level", "ALL");
properties.setProperty("rootLogger.appenderRefs", "theConsoleRef");
properties.setProperty("rootLogger.appenderRef.theConsoleRef.ref", "ConsoleAppender");
PropertiesConfigurationBuilder pcb = new PropertiesConfigurationBuilder();
// Q1: Is it correct to call setConfigurationSource with 'null'?
pcb.setConfigurationSource(null).setRootProperties(properties);
PropertiesConfiguration config = pcb.build();
Configurator.initialize(config);
/* Q2: How do I do the setStatusLevel e.g. the 2nd line in commented
code below, when I am using property route to initialize the logger? Even
if I set rootLogger.level to 'ALL' (above code), I still do not see
Log4j 2.x internal messages. I can see those if I use ConfigurationBuilder, though.
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.TRACE);
EDIT: Add "properties.setProperty("status", "TRACE");"
*/
Logger logger = LogManager.getRootLogger();
logger.error("This is error message");
logger.info("This is info message");
logger.trace("This is trace message");
}

Related

Expected getter for property [tempLocation] to be marked with #default on all

I am trying to execute a Dataflow pipeline that writes to BigQuery. I understand that in order to do so, I need to specify a GCS temp location.
So I defined options:
private interface Options extends PipelineOptions {
#Description("GCS temp location to store temp files.")
#Default.String(GCS_TEMP_LOCATION)
#Validation.Required
String getTempLocation();
void setTempLocation(String value);
#Description("BigQuery table to write to, specified as "
+ "<project_id>:<dataset_id>.<table_id>. The dataset must already exist.")
#Default.String(BIGQUERY_OUTPUT_TABLE)
#Validation.Required
String getOutput();
void setOutput(String value);
}
And try to pass this to the Pipeline.Create() function:
public static void main(String[] args) {
Pipeline p = Pipeline.create(PipelineOptionsFactory.fromArgs(args).withValidation().as(Options.class));
...
}
But I am getting the following error. I don't understand why because I annotate with#Default:
Exception in thread "main" java.lang.IllegalArgumentException: Expected getter for property [tempLocation] to be marked with #Default on all [my.gcp.dataflow.StarterPipeline$Options, org.apache.beam.sdk.options.PipelineOptions], found only on [my.gcp.dataflow.StarterPipeline$Options]
Is the above snippet your code or a copy from the SDK?
You don't define a new options class for this. You actually want to call withCustomGcsTempLocation on BigQueryIO.Write [1].
Also, I think BQ should determine a temp location on it's own if you do not provide one. Have you tried without setting this? Did you get an error?
[1] https://github.com/apache/beam/blob/a17478c2ee11b1d7a8eba58da5ce385d73c6dbbc/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java#L1402
Most users simply set the staging directory. To set the staging directory, you want to do something like:
DataflowPipelineOptions options = PipelineOptionsFactory.create()
.as(DataflowPipelineOptions.class);
options.setRunner(BlockingDataflowPipelineRunner.class);
options.setStagingLocation("gs://SET-YOUR-BUCKET-NAME-HERE");
However if you want to set gcpTemporaryDirectory, you can do that as well:
GcpOptions options = PipelineOptionsFactory.as(GcpOptions.class);
options.setGcpTempLocation()
Basically you have to do .as(X.class) to get to the X options. Then once you have that object you can just set any options that are part of X. You can find many additional examples online.

How to customize an existing Grails plugin functionality, modifying behavior of doWithSpring method

I am new to grails and while working with Spring Security LDAP plugin it was identified that it accepts the ldap server password in plain text only. The task in hand is to pass an encrypted password which is decrypted before it is consumed by the plugin during its initialization phase.
I have already searched for all possible blogs and stackoverflow questions but could not find a way to extend the main plugin class to simply override the doWithSpring() method so that i can simply add the required decryption logic for the Ldap server password. Any help here will be appreciated.
I have already seen and tried jasypt plugin but it also does not work well if the password is stored in some external file and not application yml. So I am looking for a solution to extend the Spring security plugin main class, add the required behavior and register the custom class.
EDIT
Adding the snippet from Grails LDAP Security plugin, which I am trying to override. So If i am successfully able to update the value of securityConfig object before the plugin loads, the purpose is solved.
Some snippet from the plugin:
def conf = SpringSecurityUtils.securityConfig
...
...
contextSource(DefaultSpringSecurityContextSource, conf.ldap.context.server) { // 'ldap://localhost:389'
authenticationSource = ref('ldapAuthenticationSource')
authenticationStrategy = ref('authenticationStrategy')
userDn = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**password = conf.ldap.context.managerPassword // 'secret'**
contextFactory = contextFactoryClass
dirObjectFactory = dirObjectFactoryClass
baseEnvironmentProperties = conf.ldap.context.baseEnvironmentProperties // none
cacheEnvironmentProperties = conf.ldap.context.cacheEnvironmentProperties // true
anonymousReadOnly = conf.ldap.context.anonymousReadOnly // false
referral = conf.ldap.context.referral // null
}
ldapAuthenticationSource(SimpleAuthenticationSource) {
principal = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**credentials = conf.ldap.context.managerPassword // 'secret'**
}
You don't need to override the doWithSpring() method in the existing plugin. You can provide your own plugin which loads after the one you want to affect and have your doWithSpring() add whatever you want to the context. If you add beans with the same name as the ones added by the other plugin, yours will replace the ones provided by the other plugin as long as you configure your plugin to load after the other one. Similarly, you could do the same think in resources.groovy of the app if you don't want to write a plugin for this.
You have other options too. You could write a bean post processor or bean definition post processor that affects the beans created by the other plugin. Depending on the particulars, that might be a better idea.
EDIT:
After seeing your comment below I created a simple example that shows how you might use a definition post processor. See the project at https://github.com/jeffbrown/postprocessordemo.
The interesting bits:
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomeBean.groovy
package demo
class SomeBean {
String someValue
}
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomePostProcessor.groovy
package demo
import org.springframework.beans.BeansException
import org.springframework.beans.MutablePropertyValues
import org.springframework.beans.PropertyValue
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
class SomePostProcessor implements BeanDefinitionRegistryPostProcessor{
#Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition definition = registry.getBeanDefinition('someBean')
MutablePropertyValues values = definition.getPropertyValues()
PropertyValue value = values.getPropertyValue('someValue')
def originalValue = value.getValue()
// this is where you could do your decrypting...
values.addPropertyValue('someValue', "MODIFIED: ${originalValue}".toString())
}
#Override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/conf/spring/resources.groovy
beans = {
someBean(demo.SomeBean) {
someValue = 'Some Value'
}
somePostProcessor demo.SomePostProcessor
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/init/postprocessordemo/BootStrap.groovy
package postprocessordemo
import demo.SomeBean
class BootStrap {
SomeBean someBean
def init = { servletContext ->
log.info "The Value: ${someBean.someValue}"
}
def destroy = {
}
}
At application startup you will see log output that looks something like this...
2017-10-23 19:04:54.356 INFO --- [ main] postprocessordemo.BootStrap : The Value: MODIFIED: Some Value
The "MODIFIED" there is evidence that the bean definition post processor modified the property value in the bean. In my example I am simply prepending some text to the string. In your implementation you could decrypt a password or do whatever you want to do there.
I hope that helps.
After trying Jasypt plugin and BeanPostProcessor solutions unsuccessfully for my use case, I found below solution to work perfectly.
To describe again the problem statement here,
a) we had to keep the passwords in an encrypted format inside properties files
b) and given we were packaging as a war file so the properties must not be kept inside the war to allow automated deployment scripts update the encrypted passwords depending on the environment
Jasypt plugin was a perfect solution for the use case a), but it was not able to cover the b) scenario
Moreover, the Grails LDAP Security plugin was getting loaded quite early hence Bean Post processors were also not helping out here.
Solution:
Created a new class by implementing the interface SpringApplicationRunListener. Extended its methods and parsed the properties file using YamlPropertySourceLoader
Sample code:
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load(
"application.yml", new ClassPathResource("application.yml"),"default");
return applicationYamlPropertySource;
Once the properties were loaded inside the MapPropertySource object, parsed them for the encrypted values and applied the decryption logic.
This whole implementation was executed before any plugins were initialized during Grails bootup process solving the purpose.
Hope it will help others.

How to force log4j2 rolling file appender to roll over?

To my best knowledge, RollingFileAppender in log4j2 will not roll over at the specified time (let's say - at the end of an hour), but at the first log event that arrives after the time threshold has been exceeded.
Is there a way to trigger an event, that on one hand will cause the file to roll over, and on another - will not append to the log (or will append something trivial, like an empty string)?
No there isn't any (built-in) way to do this. There are no background threads monitoring rollover time.
You could create a log4j2 plugin that implements org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy (See the built-in TimeBasedTriggeringPolicy and SizeBasedTriggeringPolicy classes for sample code.)
If you configure your custom triggering policy, log4j2 will check for every log event whether it should trigger a rollover (so take care when implementing the isTriggeringEvent method to avoid impacting performance). Note that for your custom plugin to be picked up, you need to specify the package of your class in the packages attribute of the Configuration element of your log4j2.xml file.
Finally, if this works well for you and you think your solution may be useful to others too, consider contributing your custom triggering policy back to the log4j2 code base.
Following Remko's idea, I wrote the following code, and it's working.
package com.stony;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.rolling.*;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
#Plugin(name = "ForceTriggerPolicy", category = "Core")
public class ForceTriggerPolicy implements TriggeringPolicy {
private static boolean isRolling;
#Override
public void initialize(RollingFileManager arg0) {
setRolling(false);
}
#Override
public boolean isTriggeringEvent(LogEvent arg0) {
return isRolling();
}
public static boolean isRolling() {
return isRolling;
}
public static void setRolling(boolean _isRolling) {
isRolling = _isRolling;
}
#PluginFactory
public static ForceTriggerPolicy createPolicy(){
return new ForceTriggerPolicy();
}
}
If you have access to the Object RollingFileAppender you could do something like:
rollingFileAppender.getManager().rollover();
Here you can see the manager class:
https://github.com/apache/logging-log4j2/blob/d368e294d631e79119caa985656d0ec571bd24f5/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java

Is there any way in Struts 2 by which I can get all namespaces in my app?

Is there any way in Struts2 by which I can get list of namespaces in my App ?
I want this as set or list at runtime .
I am using Struts2 RestActionMapper plugin.
When there invalid namespace is specified for valid action, Struts is throwing namespace error.
But I could not redirected to standard error page when this error occurs. I tried almost all options e.g.global error mapping default namespace etc . Nothing worked. So thought it would be great if I could get list of namespaces in my app, thus i could have checked invalid namespace against my list of valid namespaces and accordingly I could have thrown generic error which would finally result in my standard error page.
I am looking for how to get list of all namespaces in my project.
So basically I want to do something like this.
validNamespaces = getNamespaces();
if(validNamespaces.contains(namespaceRetrivedFromRestPlugin))
{Sysout("This is valid namespace.")}
else
{Sysout("Invalid namespace");}
This is possible, though like Steven has pretty much stated, I'm not convinced that this is the right approach to the problem you state of redirecting to an error page. But, I'll leave that part up to you and use this space to answer the namespace question.
This code will have to be in a Struts2-created object for the injection to work.
private Configuration configuration;
#Inject
public void setConfiguration(Configuration config) {
this.configuration = config;
}
protected Set<String> getNamespaces() {
Set<String> namespaces = Collections.emptySet();
Map<String, Map<String, ActionConfig>> allActionConfigs = this.configuration.getRuntimeConfiguration().getActionConfigs();
if (allActionConfigs != null) {
namespaces = allActionConfigs.keySet();
}
return namespaces;
}
The configuration can also be obtained from a ConfigurationManager. Also, you would obviously want to store these in a variable rather than calling above method over and over. If your object is, say, an interceptor, then you could call this method from the init() method and store it in a class-level variable.

Calling JBoss MBean functions to get threaddump

An application is using JBoss 4.2.2, and I have found it necessary to call listThreadDump(), and I expect it is in ServerInfo.
I am thinking the jar I need to find this information is jboss-jmx.jar.
So, how do I programmatically duplicate what is done by calling something similar to http://localhost:8080/jmx-console/HtmlAdaptor?action=invokeOpByName&name=jboss.system:type=ServerInfo&methodName=listThreadDump?
This is how I have accessed the ServerInfo MBean. I'm using JBoss AS 5.1, but this method should be the same.
To call listThreadDump(), you can invoke() the method on the ServerInfo MBean using an MBeanServer instance.
Additionally, you can access attributes of MBeans using the same MBeanServer.
Sample code:
// imports
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jboss.mx.util.MBeanServerLocator;
try {
MBeanServer server = MBeanServerLocator.locate();
ObjectName name = new ObjectName("jboss.system:type=ServerInfo");
// invoke the listThreadDump method and capture its output
String threadDumpHtml = (String) server.invoke(name, "listThreadDump", null, null);
// access a simple attribute of the ServerInfo object
String jvmName = (String) server.getAttribute(name, "JavaVMName");
} catch (Exception e) {
// Ideally catch the 3 exact exceptions
}
Finally, I find it handy when MBeans expose an 'instance' attribute, so you can then access the object directly (CastToType) server.getAttribute(name, "instance") instead of always going through the MBeanServer. For example, when using JMS, the ServerPeer instance is nice to have as you can get message counters on your queues and topic subscribers.

Resources