Log4j2 - PatternLayout dependend on LogLevel - log4j2

Is it possible to define two different PatternLayouts and dependend on the application LogLevel it uses the first or the second one?
Example:
If i run my application with ERROR Level, which is the default, it should print the log statements like:
$java -jar myApp.jar param1=value1
Error Message 1
Error Message 2
But if i run my application with INFO or DEBUG, it should print the log Statements like:
$java -jar myApp.jar --debug param1=value1
DEBUG | 18.03.2016 11:04:43,412 1058 | Debug Message 1
DEBUG | 18.03.2016 11:04:43,412 1058 | Debug Message 2
INFO | 18.03.2016 11:04:43,414 1060 | Info Message 1
ERROR | 18.03.2016 11:04:43,420 1066 | Error Message 1
ERROR | 18.03.2016 11:04:43,420 1066 | Error Message 2
Internally if the parameter --debug or --info is given, my app is switching the global LogLevel. The default is ERROR.
How should i configure my log4j2.xml to accomplish this?? I could not find any solution. For reference my commandLine parser and the util class where i switch the LogLevel at runtime.

After the hint of #RemkoPopma i did some more research and found a solution. My log4j2.xml looks now like:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout>
<ScriptPatternSelector defaultPattern="%highlight{%-5p | %d{dd.MM.yyyy HH:mm:ss,SSS} %r | %m%n}">
<Script name="selector" language="javascript"><![CDATA[
substitutor.replace("${main:pacifyLogLevel}");
]]>
</Script>
<!-- INFO and DEBUG are using the defaultPattern -->
<PatternMatch key="ERROR" pattern="%-5p %m%n" />
</ScriptPatternSelector>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Logger name="com.geewhiz.pacify" level="error" />
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
I also had to add the following line to my Logging Util class, where i switch the LogLevel at runtime:
MainMapLookup.setMainArguments(new String[] { "pacifyLogLevel", level.toString() });
The result of
substitutor.replace("${main:pacifyLogLevel}");
is used and looked up in the PatternMatch. If not found he uses the defaultPattern. So in my application only the ERROR Level has a different LogPattern. The other ones are using the default log pattern.
Is there a better solution?

Take a look at Pattern Selectors at the end of the PatternLayout documentation.
https://logging.apache.org/log4j/2.0/manual/layouts.html#PatternLayout

Related

Log4j2 logging level becomes different in Java 8 and Java 11

Log4j2 release 2.11.0 / 2.11.1 worked fine in Java 8 applications but not in Java 11
Here is an example of log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Properties>
<Property name="log-path">C:\\MyTestXXXX\\EnterpriseLogs</Property>
</Properties>
<Appenders>
<RollingFile name="MyTestOnly" fileName="${log-path}\ServerLog.log" filePattern="${log-path}\ServerLog_%d{yyyy-MM-dd}_%i.log" append="true">
<PatternLayout>
<Pattern>[#%d{HH:mm:ss}|%p|%m#]%n</Pattern>
</PatternLayout>
<Policies>
<CronTriggeringPolicy schedule="0 0 0 * * ?" evaluateOnStartup="true"/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="OnlyLOG" additivity="true">
<AppenderRef ref="MyTestOnly" level="ALL" />
<Logger>
<Root level="ALL" />
</Loggers>
</Configuration>
In the Java source code, I did this:
import org.apache.logging.log4j.*;
import org.apache.logging.log4j.core.Logger;
...
// Get org.apache.logging.log4j.core.Logger
Logger logger = (Logger) LogManager.getLogger("OnlyLOG");
logger.log(Level.forName("INFO", 600), "Log this text now!", new Exception("xxxxxx"));
Running in Java 8, I can see that serverLog.log has the content, which is good.
Running in Java 11, there is no log.
Through further study (debugging), I found this line interesting:
Logger logger = (Logger) LogManager.getLogger("OnlyLOG");
After execution, in Java 8, logger was something like "ALL in 2f333739", and log was fine.
But in Java 11, logger was something like "ERROR in 2f333739" and there was no log. Obviously the level became "ERROR" instead of "ALL".
So, I tries this in Java 11:
Level loggerLevel = logger.getLevel(); // Obviously it is ERROR level
logger.log(loggerLevel, "Log this text now!", new Exception("xxxxxx"));
The log happened in console (likely went through Root), not in the ServerLog.log that was configured in log4j2.xml for logger OnlyLOG.
So, what caused the level difference between Java 8 and Java 11? In other words, what should be modified so that in Java 11, log can still work the same way as in Java 8?
The issue is resolved. Here is the reason.
I used a new Eclipse IDE to run Java 11, when adding a project, I added lib folder to the class path, since log4j2.xml was in the lib folder. Now I inspected the IDE configuration a second time and found out that the lib folder was not in the class path. After a second attempt, the configuration is fine and logging becomes normal.
I made a second test by adding a brand new project into the Eclipse IDE. Interestingly enough, when adding lib folder to the class path, indeed I had to try twice, because the first time it was not added. By the way, the computer runs Windows 10. This did not happen when using a computer running in Windows 7.
Regardless, Log4j2 works fine in both Java 8 and Java 11.

log4j2 Socket Appender with SSL enabled

Here is my log4j2 SocketAppender configured with SSL
<Configuration status="warn">
<Socket name="transSocketAppender" host="lxddv002.test.intranet"
port="9400" protocol="TCP" reconnectionDelayMillis="30000"
immediateFail="false">
<PatternLayout pattern="%m%n" charset="UTF-8" />
<SSL>
<Truststore location="C:\Users\AC26252\Desktop\certs\keystore.jks"
password="123456" />
</SSL>
</Socket>
<Async name="transSocketAsyncAppender" bufferSize="204800">
<AppenderRef ref="transSocketAppender" />
</Async>
I get this error on my server restart and the first few(2 or 3) request logs are lost and not being transported.
2018-11-22 19:04:05,243 AsyncAppender-socketAsyncAppender ERROR Unable to write to stream TLS:lxddv002.test.intranet:9400 for appender socketAppender: org.apache.logging.log4j.core.appender.AppenderLoggingException: Error writing to TLS:lxddv002.test.intranet:9400
2018-11-22 19:04:05,259 AsyncAppender-socketAsyncAppender ERROR An exception occurred processing Appender socketAppender
org.apache.logging.log4j.core.appender.AppenderLoggingException: Error writing to TLS:lxddv002.test.intranet:9400
at org.apache.logging.log4j.core.net.TcpSocketManager.write(TcpSocketManager.java:229)`at org.apache.logging.log4j.core.appender.OutputStreamManager.write(OutputStreamManager.java:201)`
Is there anything wrong with my configuration? Any help is appreciated. thanks
The issue was with my log4j2 version. I have upgraded to the latest version and its working fine now.

How to set Classpath on Jboss EAP openshift image

Image used : https://access.redhat.com/containers/#/registry.access.redhat.com/jboss-eap-7/eap70-openshift
I can set an ENV variable but it does not work.
Dockerfile :
FROM registry.access.redhat.com/jboss-eap-7/eap70-openshift
ENV CLASSPATH $CLASSPATH:/opt/eap/standalone/lib/required_libs/fscontext.jar
It sets this variable but I still get following exception,
Caused by: java.lang.ClassNotFoundException: com.sun.jndi.fscontext.RefFSContextFactory from [Module "org.jboss.as.naming:main" from local module loader #b97c004 (finder: local module finder #4590c9c3 (roots: /opt/eap/modules,/opt/eap/modules/system/layers/openshift,/opt/eap/modules/system/layers/base/.overlays/layer-base-jboss-eap-7.0.6.CP,/opt/eap/modules/system/layers/base,/opt/eap/modules/system/add-ons/keycloak))]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:196)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:363)
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:351)
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:93)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.jboss.as.naming.InitialContext.getDefaultInitCtx(InitialContext.java:113)
Setting classpath inside container also does not work.
Looking for setting a classpath which would show up in java process like below :
[root#9a290d7939bb ~]# ps -ef | grep java
root 1 0 0 Jul18 ? 00:05:49 /usr/lib/jvm/java-1.8.0/bin/java -Dnop -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.library.path=/opt/webserver/lib -Djava.security.egd=file:/dev/./urandom -javaagent:/opt/jolokia/jolokia.jar=config=/opt/jolokia/etc/jolokia.properties -XX:+UseParallelGC -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:MaxMetaspaceSize=100m -XX:+ExitOnOutOfMemoryError -Djava.endorsed.dirs=/opt/webserver/endorsed **-classpath /opt/webserver/fscontext.jar:/opt/webserver/ldap.jar** -Dcatalina.base=/opt/webserver -Dcatalina.home=/opt/webserver -Djava.io.tmpdir=/opt/webserver/temp org.apache.catalina.startup.Bootstrap start
Why don't you put your lib in /opt/eap/modules or in one of the directory mentionned in the stacktrace ?
Don't forget to create also a module.xml file in EAP_HOME/modules/your/package/name/main, ex :
<module xmlns="urn:jboss:module:1.1" name="your.package.name">
<resources>
<resource-root path="fscontext.jar"/>
</resources>
<dependencies>
<module name="com.ibm.mq"/>
</dependencies>
</module>

Struts2 with SLF4J and Logback produces double logs

I have a very weird problem with Struts 2.1.8 and SLF4J with Logback. I think I have setup everything as it should be (logback-core, logback-classic, slf4j-api and removed commons-logging from Struts JARs), but the logs look like this:
16:23:59,985 INFO [stdout] (ajp-localhost-127.0.0.1-8009-3) 2013-05-11 16:23:59,985 DEBUG [BasicTilesContainer.java:615] [ODk3Cc2-mn-X8eVfnemQn5WZ.undefined] - Render request recieved for definition 'DocumentList'
Clearly there are two timestamps, and one logging framework is logging through another somewhere, which causes this problem.
JBoss 7.1.1 is used.
Any ideas how to solve this problem?
EDITED:
This is how Logback configuration looks like:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p [%F:%L] [%X{sessionId}] - %m%n</pattern>
</encoder>
</appender>
I had a problem with modules in JBoss 7, but now although they are solved and Logback is really loaded, but the log files still are a mess. They look like if they are logged through JUL:
20:23:46,128 INFO [stdout] (connector_xxxx) 2013-05-11 20:23:46,127 DEBUG [ReConnector.java:83] [] - Next connection attempt in (ms) 20000
It looks like one logger is logging through the other.

Nuget PowerShell script to modify Global.asax.cs

I'm building a nuget package for my company MVC4 template. I have run into an issue where I need the Global.asax.cs to be modified to add these two lines:
using System.Web.Optimization;
at the top, before the namespace and
BundleConfig.RegisterBundles(BundleTable.Bundles);
at the end of the Application_Start() method
I tried creating a Global.asax.cs.pp with namespace $rootnamespace$ inside it, but that doesn't seem to work. It seems Nuget wont overwrite existing files?
My last option, as I see it, is to write a powershell script (Install.ps1?) to do this. Here's my template.nuspec
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>Template.MVC4</id>
<version>1.5</version>
<title>MVC4 Template</title>
<description>Installs and configures files for MVC 4 application template.</description>
<authors>Me</authors>
<language>en-US</language>
<dependencies>
<dependency id="Microsoft.AspNet.Web.Optimization" />
</dependencies>
<iconUrl>http://www.someurl.com/Logo.jpg</iconUrl>
</metadata>
<files>
<file src="Template\Helpers\*" target="content\Helpers\" />
<file src="Template\Content\*.css" target="content\Content\" />
<file src="Template\Content\animGif\*" target="content\Content\animGif\" />
<file src="Template\Content\custom-theme\*" target="content\Content\custom-theme\" />
<file src="Template\Content\templateImages\*" target="content\Content\templateImages\" />
<file src="Template\Scripts\*" target="content\Scripts\" />
<file src="Template\Views\Shared\*" target="content\Views\Shared\" />
<file src="Template\Views\Home\*" target="content\Views\Home\" />
<file src="Template\Views\_ViewStart.cshtml" target="content\Views\" />
<file src="NuGetPackage\App_Start\*" target="content\App_Start\" />
<file src="NuGetPackage\Controllers\*" target="content\Controllers\" />
<file src="NuGetPackage\Helpers\*" target="content\Helpers\" />
<file src="NuGetPackage\Models\*" target="content\Models\" />
<file src="NuGetPackage\Views\*" target="content\Views\" />
<file src="NuGetPackage\*" target="content\" />
</files>
</package>
My question is 2 fold, 1) am I doing something wrong in my .nuspec? and 2) if modifying a Global.asax with .pp isn't an option, then what is the powershell script that I'd need to write to have this run automatically when this nuget is added to a project, and do I need to do anything special for it to run (reading the docs seems like just placing Install.ps1 in tools\ will do)?
Here's the answer in case someone needs it, this goes in your Install.ps1 inside the Tools folder:
param($installPath, $toolsPath, $package, $project)
# Read the transformed text from the custom template included in the package
$customGlobalAsax = $project.ProjectItems | where { $_.Name -eq "Global.asax.cs.custom" }
$customGlobalAsax.Open()
$customGlobalAsax.Document.Activate()
$customGlobalAsax.Document.Selection.SelectAll();
$replacementGlobalAsax = $customGlobalAsax.Document.Selection.Text;
$customGlobalAsax.Delete()
# Replace the contents of Global.asax.cs
$globalAsax = $project.ProjectItems | ForEach-Object { $_.ProjectItems } | where { $_.Name -eq "Global.asax.cs" }
if($globalAsax) {
$globalAsax.Open()
$globalAsax.Document.Activate()
$globalAsax.Document.Selection.SelectAll()
$globalAsax.Document.Selection.Insert($replacementGlobalAsax)
$globalAsax.Document.Selection.StartOfDocument()
$globalAsax.Document.Close(0)
}
I would take a look at WebActivator before going down the route of writing a PowerShell script to update existing source code. WebActivator is a NuGet package that you can use to add startup and shutdown code into an application without having to modify global.asax.
You will probably want to use the PostApplicationStartMethod attribute so your bundle registration is done at the end.
If you want to "replace" some text in some file you can do like this:
$file = $project.ProjectItems | ForEach-Object { $_.ProjectItems } | where { $_.Name -eq "Global.asax.cs" }
if($file) {
$file.Open()
$file.Document.Activate()
$file.Document.Selection.StartOfDocument()
$file.Document.ReplaceText("TextToFind`n", "TextToReplace")
}
Note the `n which is escaping for \n or enter (CR).
If you need quote character " it can be escaped as `" as well
In case anyone sees this thread, I want to point out that as of version 2.5 released April, 2013 Nuget will overwrite content files. If using the GUI, the install process will detect the issue and prompt whether or not to overwrite the file. A new option -FileConflictAction allows the setting of a default value.
See the release notes:
http://docs.nuget.org/docs/release-notes/nuget-2.5

Resources