log4J2 Store and use variables / lookup-values - java

In my java program I want to use several log files.
So according to the log4j documentation I would assume that lookup values are use to do that.
The Lookups page describe how to build the configuration files. But has only little information about how to store the values so that the configuration file is retrieving the values.
So I want to have logfilename dynamically filled.
Test with envrimonment works:
<File name="MyFile" fileName="${env:USERERNAME}" immediateFlush="false" append="false">
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</File>
I would assume that System.getProperty("USER") would match the java lookup ${java:USER} but it does not work.
I tested both variables using:
System.out.println("USER: " + System.getProperty("USER"));
System.out.println("USERNAME: " + System.getenv("USERNAME"));
So how to fill the variables for
Context Map Lookup
Java lookup
Which are the differences?

There are various ways of lookup in log4j2. Let me add some details to them -
Context Map Lookup - For putting value using context map, use below code -
org.apache.logging.log4j.ThreadContext.put("logFileName", "app.log");
Variable can be accessed in configuration file using ${ctx:logFileName} or %X{logFileName}.
Context map lookup is generally used in web application where you want some value for each user request i.e. for each thread.
Environment Lookup - Environment lookup is used for looking up system environment variables like USERNAME, PLATFORM. You can print all environment variables in Java using below -
System.out.println(System.getenv());
OR any specific environment variable -
System.out.println(System.getenv("NUMBER_OF_PROCESSORS"));
However, from Java program, you can not set environment variables.
As you mentioned in your code, environment variables can be accessed using ${env:NUMBER_OF_PROCESSORS} syntax.
Java Lookup - Java lookup is for Java environment information like java version, hardware etc. Variables are fixed and can not be set in java program.
Such variables can be accessed using ${java:vm} syntax where vm is java environment related variable name.
System Properties Lookup - It is easy to get and set such properties using below code -
System.setProperty("logFileName", "app.log");
System.getProperty("logFileName");
Variables can be accessed in log4j2 configuration file using ${sys:logFileName} syntax.
Good thing about these system properties is you can pass these properties as VM Arguments to your program like -DlogFileName=app3.log.
There are other lookup mechanism also like Date Lookup, Map Lookup etc which are mostly used. Rest details can be checked here
Since you want to set file name dynamically in your application and if you want to set file name only once, I would suggest to you System property. But ensure that, you set System Property before initializing log4j

Related

Adding a custom field in log4j using SyslogAppender

I'm using the SyslogAppender in my java application and I'm trying to add a custom field to the resulting log. How can I add an additional field to my log4j.properties?
My current log4j.properties (the last line shows what I want to achieve):
log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender
log4j.appender.SYSLOG.threshold=INFO
log4j.appender.SYSLOG.syslogHost=localhost
log4j.appender.SYSLOG.facility=LOCAL4
log4j.appender.SYSLOG.header=true
log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout
log4j.appender.SYSLOG.layout.conversionPattern=my-app: %m%n
log4j.appender.SYSLOG.applicationName=${STACKNAME}
The ${STACKNAME} is a system property configured by the deployment job, depending on the environment (e.g. prod, test, dev).
From this answer I get the answer: add the lines
log4j.appender.graylog2.additionalFields={'filed_name': 'field_value', 'field2_name': 'field2_value'}
log4j.appender.graylog2.addExtendedInformation=true
to add field_name and field2_name with values field_value and field2_value. The property addExtendedInformation=true indicates Graylog to add those fields to all log entries.

Log4J template pattern conversion

I want to output a log message exactly the way it is done using the bash "logger" command, but in Java using Log4j:
Feb 5 19:35:28 hostname program: mymsg
After trying many different patterns, I cannot reproduce the same output. Any idea how to write a pattern for this?
Thank you,
I am guessing you'll get host name from system variables or environmental variables because there is no default parameter for hostname in log4j. So let's assume you'll get hostname and program name from sys variables. So the pattern would be :
<PatternLayout pattern="%d{MMM d HH:mm:ss} ${sys:user.home} ${sys:program}: %m%n" />
See the reference

Log4j2 Rolling appender - fileName "sliding" according to pattern

I am looking for rollover strategy where current log (active output target in manual's terminology) file name is not fixed but specified by a pattern, or - more precisely - same pattern as in filePattern attribute.
I want to achieve daily rollover where today's log is, say, log-2015-05-05.log and on midnight framework just stops writing it and starts writing into log-2015-05-06.log. However, AFAIK, current configuration allows only
<RollingFile name="ROLFILE"
fileName="log.log"
filePattern="log-%d{yyyy-MM-dd}.log"
>
Specifying same value into fileName attribute doesn't work (leads to file with sensitive characters literally interpreted). I noticed no example or SO question with such a dynamic value of fileName. Note the fileName="log-${date:yyyy-MM-dd}.log" doesn't solve problem since expression is evaluated only at startup and events are still sent into file even if their timestamp doesn't match the expression.
I am migrating from Log4j 1.2 to Log4j 2.2. In old version, required behavior was possible using
<appender name="ROLFILE" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="log-%d{yyyy-MM-dd}.log" />
</rollingPolicy>
...
I prefer to preserve current way since some log analyzing tools rely on it.
Is it possible in Log4j2?
Thanks.
Note sure if this works, but you can try using a double $$ in the date lookup. This allows the variable to be resolved at runtime.
<RollingFile name="ROLFILE"
fileName="log-$${date:yyyy-MM-dd}.log"
filePattern="oldlog-%d{yyyy-MM-dd}.log"
>
You may need to be careful to ensure that the active output target file name is different from the after-rollover file name. (I used 'oldlog' in the snippet above.)
Finally, I wrote my own rollover strategy which generates different set of rollover actions. Instead renaming active file the active file name is simply replaced inside RollingFileManager. Yes, it's ugly reflection hack and also appender must be initialized with fileName constant corresponding with current date and having same pattern, e.g.
<RollingFile name="ROLFILE"
fileName="log-${date:yyyy-MM-dd}.log"
filePattern="log-%d{yyyy-MM-dd}.log"
>
<SlidingFilenameRolloverStrategy />
...
yet for me this solution is worth doing it despite these small drawbacks.
(Initial fileName stays forever as a key in AbstractManager registry MAP even if in manager itself it has been changed - seems it doesn't mind, I also tried replacing manager in registry for new one but it's impossible to collect all parameters necessary for its construction.)
I hope this hack shouldn't have been so ugly if RollingFileManager API made it possible normal way. I got some hope seeing this javadoc but framework AFAIK never utilizes this field, let alone for mutating RollingFileAppender.
I think it would work just fine using:
fileName="log-${date:yyyy-MM-dd}.log"
filePattern="log-%d{yyyy-MM-dd}.log"
I use it with log4j2 version 2.5
This has been implemented in Log4j 2.8 (see issue LOG4J2-1101).
Currently it only works with a RollingFile appender by omitting the filename parameter and using a DirectWriteRolloverStrategy.
Also, this feature seems to have some issues with the TimeBasedTriggeringPolicy (the first rollover doesn't happen so every logfile is offset by one interval); CronTriggeringPolicy works properly.
Example config:
<RollingRandomAccessFile name="MyLogger"
filePattern="logs/application.%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<CronTriggeringPolicy schedule="0 * * * * ?" evaluateOnStartup="true"/>
</Policies>
<DirectWriteRolloverStrategy/>
</RollingRandomAccessFile>
Support for RollingRandomAccessFile appender is requested in issue LOG4J2-1878.
Edit: Changed to CronTriggeringPolicy policy after finding TimeBasedTriggeringPolicy had issues.

Does Logback SMTPAppender support environment variables?

Inside the definition for a Logback SMTPAppender, you can specify email configuration info like so:
<smtpHost>my.smtp.host</smtpHost>
<to>john.smith#example.com</to>
<from>no-reply#example.com</from>
<username>my_smtp_user</username>
<password>my_smtp_password</password>
<subject>%logger{20} - %m</subject>
Instead of hardcoding the <to>john.smith#example.com</to> field, I'd like it to pick up the local username of whatever account/machine the Java app is running on. For instance, if my Ubuntu username is ticketMonster, then I would like the <to> field to be:
<to>ticketMonster#example.com</to>
Or if my operating system username is bgates, I'd like it to be:
<to>bgates#example.com</to>
Hence, I'm looking for dynamic username evaluation. I tried:
<to>${username}#example.com</to>
But that does not work... Any ideas or suggestions here? Thanks in advance!
Variable substitution for the <to> element should work. If it does not, it's a bug. Could you please create a new issue on our jira so that this problem can be fixed?
I guess, There are few ways you can do it:
You can define your username in properties file and load your .properties file in logback.xml file.
property resource="filename.properties"
<username>${username}</username>
<password>${password}</password>
<to>${email1}</to>
<from>${from}</from>
You can use System variable like this in logback.xml file
< property name="user" value="${username}"/>

Formatting slf4j to log message types with colors

I am using slf4j for logging in my Java Application. It involves a lot logging and log monitoring.
Sometimes it is really difficult to read and find information from logs when the entire log is printed in the black color with.
Just to make it more readable, is it possible to log the different kinds of messages in different colors?
For example all Error level messages in Red color or a different font size and all Info level messages in blue color and a different font size.
Any suggestions or help is welcome.
Thnx.
It's not possible to change colors of slf4j logging, because there are no formatters. SLF4J is a middleware between your application and some logging facility, for example, Log4j or Logback.
You can change colors in Log4j output, as explained here. I would recommend to use MulticolorLayout from jcabi-log
Something you have to bear in mind.
First, SLF4J is only a logging facade. How the actual log message is handled depends on the binding it used. Therefore your question is invalid, instead, you should quote which implementation you want to use (LogBack? Log4J? etc)
Second, "Coloring" is not something meaningful in most case. For example, if you are referring a plain text log file, there is nothing that we can control the coloring because they are all plain text (unless your editor have special syntax highlighting built-in for your log message format). It may be meaningful if you want to see the color in the console/terminal, or if you are outputting your log into file format that allow you to contain color information (e.g. HTML).
With these two idea in mind, here is my suggestion.
LogBack has built-in support for coloring http://logback.qos.ch/manual/layouts.html#coloring in console output. If you are looking for way to see color in console output, and you are allowed to use LogBack, this is what you are looking for.
Two solutions come to my mind. They are not colors, but alternative solutions:
In your File Appender configuration, you can configure the pattern to include the log level (error, warn, etc). Then you can grep the file, to filter messages by level.
You can configure two file appenders (for two separate log files) with different level threshold. For instance, one would log all the logs above debug level (so info, warn, error) into let's say logs.txt and the other would log only the errors logs into errors.txt
Hope it helps.
Add next appender into logback.xml to colorize logs output:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<Pattern>%d %highlight(%-5level) [%thread] %cyan(%logger{15}): %msg%n</Pattern>
</encoder>
</appender>
In addition to using a colored ConsoleAppender, you could also color the statement itself. Note this has limitations and may not work with all systems. It worked well for internal projects, particularly when you want to log that a major task in a chain is complete or simply while debugging errors.
To quote an external link:
System.out.println("\u001B[31m" + "the text is red" + "\u001B[0m");
\u001B[31m is the ANSI code to color the output red and \u001B[31m is the ANSI reset command.
public class ColorLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(ColorLogger.class);
public void logDebug(String logging) {
LOGGER.debug("\u001B[34m" + logging + "\u001B[0m");
}
public void logInfo(String logging) {
LOGGER.info("\u001B[32m" + logging + "\u001B[0m");
}
public void logError(String logging) {
LOGGER.error("\u001B[31m" + logging + "\u001B[0m");
}
}
In your case, it sounds like you can try to write the contents of the aggregated log out and optionally color each statement by finding each string that concerns you.
This can alternatively just be done by printing it to an HTML page and using CSS selectively on your text.
I use filters for both logging level and package. This example comes from Spring boot application.properties
logging.level.root=warn
logging.level.org.springframework=warn
logging.level.org.hibernate=warn
logging.level.org.starmonkey.brown=DEBUG
This way I see only messages I want to see

Categories