log4j2: custom formating for pattern parameter - java

Usually logging looks like this:
Duration duration = ...;
log.info("Duration: {}", duration);
But what do if you need custom formatting?
if (log.isInfoEnabled()) {
String value = duration.toSeconds() + "." + duration.toMillis();
log.info("Duration: {}", value);
}
In this case code is complicated. I'm looking for a way to do simple logging as in first sample but to be able to customize value formatting using some mechanism, e.g, define a format function for given type:
LogSystem.install(Duration.class, duration -> {
return String.format("%d.%d", duration.toSeconds(), duration.toMillis();
});
Questions:
does log4j 2.x have such or similar feature?
may be it exists in other logging libraries?
if no, can if (log.isInfoEnabled()) { ... } be simplified somehow?

does log4j 2.x have such or similar feature?
Yes, you can use
log.info("Duration: {}.{}", duration.toSeconds(), duration.toMillis());
You can pass as many parameters as you like. If you use parameterized logging, you don't need to check log.isInfoEnabled().
may be it exists in other logging libraries?
SLF4j also supports parameterized logging.

Yes, you can do it using the properties file.Set policies pattern in your log4j2.properties file like:
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ABClog/ABCapplication.log
appender.rolling.filePattern = ABClog/ABCapplication.%d{dd-MMM}.log
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = { [%-5level %d{dd-MM-yyyy hh:mm:ss.SSS } ] [%msg] }%n
After setting the above configuration in your log4j2.proerties file, you will get your desired output, even using properties file concept you can add extra info too e.g. Correlation-ID
log.info("hello")
Log4j2 document for more pattern style: https://logging.apache.org/log4j/2.x/manual/layouts.html

Related

Log4j2 SyslogAppender Set Layout Programmatically

i recently had the necessity to migrate from log4j 1.x to log4j 2.x.
In my code, i dynamically instantiate different Syslog appenders which may use two different layout types.
I'm using a getSysLogAppender method to generate a SyslogAppender object whenever i need in my code. Unfortunately, if i set the layout using the setLayout() function, my logs do not get sent one-at-a-time as i've expected. If i remove the setLayout() function from the builder and just leave the setFormat("RFC5424"), logs are sent correctly.
Due to application needs, i cannot just take the RFC5424 as it is implemented in log4j2, i need to customize the layout of the logs.
Looking at log4j2 SyslogAppender source code, if the format is RFC5424 and a layout is given, format RFC5424 is not taken into consideration: https://github.com/apache/logging-log4j2/blob/release-2.x/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java (line 114).
What's the correct way to customize the SyslogAppender layout?
Thanks in advance.
// Class variables
private static final String pattern1 = "%d %-1p - %m";
private static final String pattern2 = "%d %-5p [%t] %-4c{2} (%13F:%L) %3x - %m";
private static final String hostname = "localhost";
private static final String port = "2601";
private void createAppender() {
Appender appender = getSyslogAppender("TESTID", Facility.USER, pattern1); //third parameter may be also pattern2 var
appender.start();
}
// Method that generates my appender
private SyslogAppender getSyslogAppender(String id, Facility facility, String pattern) {
return SyslogAppender.newSyslogAppenderBuilder().withProtocol(myprotocol).withHost(hostname).withPort(port).setFacility(facility).setId(id).withBufferedIo(false)
.setLayout(org.apache.logging.log4j.core.layout.PatternLayout.newBuilder().withPattern(pattern).build())
.setName("log4jAppender").withImmediateFlush(true).setNewLine(true).setFormat("RFC5424").build();
}

How to print event level in lowercase using log4j12?

Is there any possibility to print event levels to log in lower case in log4j 2?
Like %level{lowerCase=true} in pattern in Log4j 2.
Can I process result string somehow, or use custom renderer?
You have to set a pattern to the appropriate appender in the log4j.properties file.
For instance, with ConsoleAppender:
log4j.appender.ConsoleAppender.layout.ConversionPattern=%d [%p{lowerCase=true}] %c{1} - %m%n
You can check the output here.

SimpleFormatter dd.mm.yyyy HH:mm:ss

I want to change the way my log looks (java.util.logging.Logger) and I found this:
System.setProperty("java.util.logging.SimpleFormatter.format","[%1$tF %1$tT] [%4$-7s] %5$s %n");
So my log entries look now like this:
[2017-08-24 15:55:40] [INFORMATION] Hello World!
I'd like to set the format to following:
[24.08.2017 15:55:40]
I've been trying for so long, shouldn't be that hard. Can anybody help me or sending me some good and easy documentations/examples?
From documentation Class Formatter
Instead of :
[%1$tF %1$tT]
You can use :
[%1$te.%1$tm.%1$tY %1$tT]
You can see the result in this example :
Calendar c = Calendar.getInstance();
String example = String.format("[%1$te.%1$tm.%1$tY %1$tT]", c);
System.out.println(example);
Output
[24.08.2017 15:19:30]
I've been trying for so long, shouldn't be that hard.
SimpleFormatter.format is a static property that is set at class loading time. You can only use System.setProperty to change the format if that is done before the java.util.logging.SimpleFormatter class is loaded. Instead you should modify your logging.properties or modify your startup script to set the property during the JVM startup.
If there is an error in your format the behavior is to just use a default format. This makes it hard to debug because you can't tell the difference between an error or just not setting the property correctly.
Your best bet is to use a test program like YCF_L made and then apply the format to the startup script.

How to change slf4j log into html link

I have java code as:
final URI myUri = new URIBuilder()
.setScheme(azkabanHostProtocol)
.setHost(azkabanHost + ":" + azkabanPort)
.setPath("/executor")
.setParameter("execid", executionID).build();
logger.info(myUri.toString());
I want to display myURI in form of an url/html link into Azkaban's logs so that by clicking on the url it is opened. I am using log4j for logging.
You may create your own Layout class extending HTMLLayout .
Then override the format method to suit your needs.
The actual implementation has the following lines, that you may want to replace :
sbuf.append(Transform.escapeTags(event.getRenderedMessage()));
See that all tags in the message String, will be escaped by default.
Your version could be based on a kind of marker, say String mark = "[LINK]"; for instance
if(event.getRenderedMessage().startsWith(mark)){
String uri = event.getRenderedMessage().substring(mark.length());
String link = "" + uri + "";
sbuf.append(link);
}
else
sbuf.append(Transform.escapeTags(event.getRenderedMessage()));
And you would call the logger this way :
logger.info(mark + myUri.toString());
The following topic will help you use a custom HTMLLayout : how to change htmllayout in log4j2
Here is the source code for the default HTMLLayout, as a starter.
How do you view the Azkaban log files? If they are just raw text files being viewed with a vanilla text editor then there is no way to accomplish what you want. If you are viewing them in a smarter UI then you need to format them according to what that UI requires.
In short, the answer to your question is completely driven by whatever tool you are using to view the logs.

Do resource bundles in Java support runtime string substitution?

Can you do the following with a Java ResourceBundle?
In the properties file...
example.dynamicresource=You currently have {0} accounts.
At runtime...
int accountAcount = 3;
bundle.get("example.dynamicresource",accountCount,param2,...);
To give a result of
"You currently have 3 accounts."
Not without using the MessageFormat class, such as:
String pattern = bundle.getString("example.dynamicresource");
String message = MessageFormat.format(pattern, accountCount);
On their own, ResourceBundle does not support property placeholders. The usual idea is to take the String you get from the bundle, and stick it into a MessageFormat, and then use that to get your parameterized message.
If you're using JSP/JSTL, then you can combine <fmt:message> and <fmt:param> to do this, which uses ResourceBundle and MessageFormat under the covers.
If you happen to be using Spring, then it has the ResourceBundleMessageSource which does something similar, and can be used anywhere in your program. This MessageSource abstraction (combined with MessageSourceAccessor) is much nicer to use than ResourceBundle.
There are various ways, depending on the view technology you're using. If you're using "plain vanilla" Java (e.g. Swing), then use MessageFormat API as answered before. If you're using a webapplication framework (which is true, if I judge your question history here correctly), then the way depends on the view technology and/or MVC framework you're using. If it is for example "plain vanilla" JSP, then you can use JSTL fmt:message for this.
<fmt:message key="example.dynamicresource">
<fmt:param value="${bean.accountCount}">
</fmt:message>
If it is for example JSF, you can use h:outputFormat for this.
<h:outputFormat value="#{bundle['example.dynamicresource']}">
<f:param value="#{bean.accountCount}">
</h:outputFormat>
Best place is to just consult the documentation of the technology/framework you're using (or to tell it here so that we can give better suited and more detailed answers).
Struts have a nice util called MessageResources which does exactly what you ask for....
e.g.
MessageResources resources = getResources(request, "my_resource_bundle"); // Call your bundle exactly like ResourceBundle.getBundle() method
resources.getMessage("example.dynamicresource",accountCount,param2,...);
Limitation
It only allows maximum of 3 parameters (i.e. resource attribute, param1, ..., param3).
I suggest using MessageFormat (if you want to use more than 3 parameter values) as suggested by David Sykes.
PS the getResources method is available only in the Struts Action class.
I don't think you can make this work for Non-English properties file.
My message.properties file has the following line:
info.fomat.log.message.start=Starting to parse log message in {0} format.
And my message_fr_FR.properties file has the following line:
info.fomat.log.message.start=A partir d'analyser le message connecter {0} format.
This code works only for the English one
String.format((String) messages .getString(GlobalConstants.MESSAGE_FORMAT_START), GlobalConstants.STR_JSON));
It does NOT replace the placeholder with the value when my language / locale is French :-(
Even MessageFormat.fomat() is no good
I don't believe ResourceBundle can do that itself, but String can:
String.format(bundle.getString("example.dynamicresource"), accountCount);
Remember that when using MessageFormat.format() you need to use a double quote ('') in your resource bundle if you want to express single quote (').
MessageFormoat#format will work for the case like:
greetingTo=Have Param, saying hello {0}
You can declare two methods like this where RB is a instance of ResourceBundle:
/**This is a method that takes the param to substitute the placeholder**/
public String getString(String key, Object... params ) {
try {
return MessageFormat.format(this.RB.getString(key), params);
} catch (MissingResourceException e) {
return "[" + key + "]";
}
}
/**Without a param, this will derectly delegate to ResourceBundle#getString**/
public String getString(String key) {
try {
return this.RB.getString(key);
} catch (MissingResourceException e) {
return "[" + key + "]";
}
}

Categories

Resources