Using SLF4J and Liferay Logging Framework in custom plugins

SLF4J LogoBefore starting this post I would like to recall a previous excellent article of Brett Swaim "Using log4j to ensure each portlet has it's own log file". In his post Brett summarizes (look at the article and comments) pros and cons of Log4j versus Liferay Logging Framework as the logging API to use in your custom plugins (portlets, web, hooks and so on).

In this article I would like to describe the SLF4J alternative. Starting from version 6.1.0 GA1 (LPS-24342) Liferay introduced SLF4J (http://www.slf4j.org) and the related Liferay Logging Framework SLF4J Adapter.

The combination of the two brings many benefits; in particular you can achieve:

  • SLFJ4 API:

    • the SFL4J logging API to ensure portability

    • SFL4J can safely be used with service builder

  • Liferay Logging Framework SLF4J Adapter:

    • adjust the logging on the fly through Control Panel

    • configure custom plugin logging without modifying portal files

    • let your plugin log in it's own separate log file

  • Log4j and extras (Liferay Logging Framework relies on it):

    • format your log strings

    • rotate your log file

    • compress your rotated log files and store them in a separate directory.

 

Using SLF4J in your custom plugins

To use SLF4J in your portlets or in general in Liferay plugins, you just need to:

  • Add SLF4J to your imports and declare a logger

  • Add SLF4J dependencies to your plugin. You can do that manually copying slf4j-api.jar and util-slf4j.jar (the Liferay SLF4J adapter) into your plugin WEB-INF\lib directory or include them as portal dependencies in liferay-plugin-package.properties (Liferay will add it for you during the auto deployment phase - if you are using Maven just add them as provided dependencies).

 

Let your plugin log in a separate file without modify portal files

During hot deployment phase Liferay checks if “META-INF/portal-log4j.xml” exists in the classpath of your custom plugin (just create it under /WEB-INF/classes/META-INF/portal-log.xml). If it exists, the Liferay plugin hot deploy listener invoke the initLogger() that will reinitialize the Liferay logging system merging your appenders with those defined in Liferay.

To get it working successfully avoiding Log4j classloader exceptions (LPS-9376) you need to remove the log4j dependency from your plugin (or start your JMV with the system property -Dlog4j.ignoreTCL=true).To prevent Log4j being copied to WEB-INF\lib during the auto deploy process, there are couple of alternatives:

  • add auto.deploy.copy.log4j=false to your portal-ext.properties (affects all plugins)

  • add deploy-excludes=**/WEB-INF/lib/log4j.jar in the liferay-plugin-package.properties file of your plugin (affects only your plugin)

 

Leverage Log4j Companion Extras to format, rotate and compress rotated files

As you probably already know Liferay Logging Framework is based upon Log4j 1.2.x. Starting from version 6.1 (LPS-18970 and LPS-33129) Liferay introduced the Log4j Extras Companion handling its deployment in custom plugins as well. (Apache Extras Companion for Apache log4j is a collection of appenders, filters, layouts, and receivers for Apache log4j 1.2 originally developed in the discontinued Apache log4j 1.3 and backported to 1.2).

Then you can take advantage of:

  • TimeBasedRollingPolicy, that offers:

    • automatic file compression. This feature is enabled if the value of the FileNamePattern option ends with .gz or .zip

    • decoupling the location of the active log file and the archived log files. Setting the ActiveFileName option you can decouple the location of the active log file and the location of the archived log files.

  • EnhancedPatternLayout: A flexible layout configurable with pattern string. The goal of this class is to format a LoggingEvent and return the results in a StringBuffer.

You can find a working slf4j sample portlet here: https://github.com/denissignoretto/slf4j-logging-sample-portlet

Hopefully this article helps and expands the range of choices for logging in your Liferay projects !

Denis.

 

 

Blogs
I got this to work for the most part, but can't seem to get MDC to work. I add values to the MDC and use a pattern that should print them, but no values get printed, but the rest of the log line does.
Hi Jan,

happy to hear that you tryied and it works. I can confirm that MDC(Mapped Diagnostic Context) doesn't work using Liferay Logging Framework SLF4J Adapter (util-slf4j.jar). Looking at source code of org.slf4j.impl.StaticMDCBinder (https://github.com/liferay/liferay-portal/blob/6.2.0-ga1/util-slf4j/src/org/slf4j/impl/StaticMDCBinder.java) it returns a BasicMDCAdapter and as BasicMDCAdapter javadoc reports "can be used with logging systems that lack out-of-the-box MDC support". It seems it's still an unsupported feature.

I've just seen, commented and voted your reported https://issues.liferay.com/browse/LPS-44869.

Thanks,
Denis.
I tried some (very) quick debugging yesterday and found that stuff got put into the MDC correctly, but when the log message is being formatted the values aren't there anymore. Didn't have the time yet to delve in deeper to see what causes it.
I opened a feature request to add MDC (Mapped Diagnostic Context) support to Liferay Logging API (https://issues.liferay.com/browse/LPS-44900) and extend it to SLF4J Adapter (https://issues.liferay.com/browse/LPS-44869).
Hi Denis,

I have follow you steps but it's not working for me, i am using liferay 6.1 - Jboss 7.1

Thank you
How would you log from a managedbean in a jsf portlet?
what import statements are needed ?
Hi,
It's working partially for me, I see my log output in the console, and in the liferay.log file
my custom rolling appender file gets created but my logout output is not in the file.
I do see other lines being logged in the file, but not the ones I logged from my managed bean.
Hi Eric,

it seems, you have setup correclty the Liferay logging system to log on a separate file but you are not sending log to Liferay logging system. Check your slf4j imports in your class and use an slf4j Logger instance for logging.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

You can find a working slf4j sample portlet here: https://github.com/denissignoretto/slf4j-logging-sample-portlet

Hope it can help!
Denis.
Hi Denis,

The example is working great for Liferay 6.2 running on Tomcat. However when I tried it with Liferay 6.2 running on JBoss 7 or JBoss EAP 6, the log file is created but nothing is written to it. Any idea on what might cause this problem?
Finally figured out that the problem is due to JBoss class loading. By default, JBoss always load its version of log4j and slf4j. These need to be excluded via the jboss-deployment-structure.xml file. App Manager seems to generate this file during deployment, but org.slf4j is not in the exclusion list. After adding org.slf4j, the custom logging works nicely on JBoss 7.1 and JBoss EAP 6.2.
Hi, Denis
Thank you Denis for the information.

Thanks
Alberto de Francisco
Hi Denis,
As per your blog I am trying to implement the logs for custom portlet on bundled JBOSS server 7.1.1, but it's generating the blank log file, could you please guide me what I am missing. Below are the configurations I have done to implement it.
1.Added the dependencies for slf4j-api.jar and util-slf4j.jar file in pom.xml.
2.If I add the log4j.jar dependency in pom.xml , at the time of deployment it raise below exception.

java.lang.ClassCastException: org.apache.log4j.EnhancedPatternLayout cannot be cast to org.apache.log4j.Layout

I searched for it , and found that by adding the log4j.jar in portlet I am initializing it again, as it is already initialized by JBOSS. So I added below configuration

Added jboss-deployment-structure.xml ,with below configuration
<exclusions>
<module name="javaee.api"></module>
<module name="org.apache.log4j"></module>
<module name="org.slf4j"></module>
<module name="org.slf4j.impl"></module>
<module name="org.jboss.logging"></module>
<module name="org.jboss.logging.jul-to-slf4j-stub"></module>
<module name="org.jboss.logmanager"></module>
<module name="org.jboss.logmanager.log4j"></module>
</exclusions>
But still it raise the same exception.

Thanks.
Parikshit
It is also not working for me on jboss EAP 6.2. I have excluded all the modules still no luck.

<exclusions>
<module name="javaee.api" />
<module name="org.apache.log4j" />

<module name="org.apache.commons.logging" />
<module name="org.jboss.logging" />
<module name="org.jboss.logging.jul-to-slf4j-stub" />
<module name="org.jboss.logmanager" />
<module name="org.jboss.logmanager.log4j" />
<module name="org.slf4j" />
<module name="org.slf4j.impl" />

</exclusions>