Liferay Logging is Dead! Long Live Liferay Logging!

Although not publicly known, you can drop the use of the Liferay logging facilities and use SLF4J instead.

Introduction

So for the longest time I have been an advocate of Liferay's logging API. Folks have asked how to use Log4J or other frameworks and I've always advised against it.

Why?

Well, for 3 primary reasons:

  1. Liferay uses it. As with all things Liferay, it is always easier to go with flow than it is to fight your way upstream.
  2. It can be controlled by the Liferay Logging panel of the Server Administration control panel, so it is super easy to enable additional logging while the server is live.
  3. A separate logger can result in a split log, with your application messages going one place but any calls to the Liferay APIs will have messages going to another location.

For these 3 reasons, it was always clear to me that using Liferay's logging API was really the only good choice.

The problem with this, however, has always been 3rd party dependencies. They always wanted to use Log4j directly or some other logging framework, so getting them all to play nicely w/ Liferay's logger was sometimes a miserable chore.

As of Liferay 7.0+, though, there was a new alternative introduced that would make it a heck of a lot easier to log from custom code as well as support logging from 3rd party frameworks - SLF4J.

SLF4J and Liferay

You can find a brief intro to using SLF4J with your Liferay custom code here: https://dev.liferay.com/en/develop/tutorials/-/knowledge_base/7-0/implementing-logging

The missing info, for me anyway, was just how to get it all wired up correctly to work in OSGi.

For that, you will need to add as a dependency for your project the slf4j-api module. In Gradle, you add the following:

compileOnly group: 'org.slf4j', name: 'slf4j-api', version: '1.7.26'

For Maven, you'll add:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
    <scope>provided</scope>
</dependency>

Note that you are not going to be including the SLF4J API jar directly into your module, otherwise you'll end up with unresolved reference errors on the org.slf4j.impl package.

Also note that you will not be including any static SLF4J binding module into your project; Liferay actually provides the SLF4J binding that outputs into the standard Liferay logs.

Conclusion

So now that we know how to use SLF4J instead of the Liferay logger, what are the reasons why we would want to?

  1. Liferay uses it. Seriously, there has been a shift underway in the Liferay code to adopt the SLF4J API instead of directly using the Liferay loggers.
  2. It can be controlled by the Liferay Logging panel of the Server Administration control panel. Seriously, this is still a feature even when using SLF4J.
  3. Since it is a standard logging framework, it has much wider adoption amongst 3rd party dependencies and is much easier to integrate.
  4. Legacy code can still use the Liferay logger, so current code will continue to work unchanged.
  5. Liferay actually recommends using the SLF4J API over the Liferay logger. At some point they may want to switch from the legacy Log4j logger, and by using the standard SLF4J API your code will be more resilient to that change if/when it happens.

So you get a newer, updated logging API with support for formats, varargs, but you also still get the classic features of Liferay logging so you can dynamically change logging on the fly.

I should note that my other previous blogs about logging also still apply when using SLF4J:

https://community.liferay.com/blogs/-/blogs/liferay-7-dxp-making-logging-changes-persistent for making the Server Administration logging panel changes persist across restarts.

https://community.liferay.com/blogs/-/blogs/centralized-liferay-logging to add a JSON-based output logger that is perfect for consuming in an ELK stack.

Blogs

I think you meant `compileOnly` in gradle?

Good catch. I keep forgetting "compile" has been deprecated. Fixed.

I didn't know that the compile configuration is deprecated! I  could not tell any difference between compile and compileOnly until I recently tried to run some unit tests in Eclipse and the compile dependencies were being ignored. That's why I'm preferring compile over compileOnly.

Is there any other reason to prefer the compileOnly configuration? Should I file a bug with my problem?

The Gradle doco actually states that "compile" is deprecated: https://docs.gradle.org/current/userguide/java_library_plugin.html

However, generally Liferay recommends the following:

* compileOnly for dependencies provided by the portal, so that's all of Liferay's modules, commons-lang2, things of that nature.

* compileInclude for dependencies that you are including into your module.

* compile This directive would be for dependencies provided in OSGi but for ones that you are deploying into the container.

compileOnly and compile have the same effect, they will become dependencies that are part of the Import-Package in the Manifest, and OSGi will require them to be resolvable.

Now these distinctions, well they tend to satisfy Liferay's needs. As an external developer, I don't see a great difference between compile and compileOnly.

I tend to want to use compileOnly because I think it makes it explicit to another developer checking out my project that the dependency has to be provided by the container. I don't think it matters so much whether the dependency is satisfied by the portal or by my own deployed artifacts.

But Liferay does have visions for the future related to creation of custom boms to use in conjunction with the Liferay boms, and in that future it may be necessary to make these kinds of distinctions. So using the right compile or compileOnly directive may actually serve you in the long run although there's no actual benefit today.

Hey, if Liferay is working on logging system is time to take care of MDC? (ref. https://issues.liferay.com/browse/LPS-44869 and https://issues.liferay.com/browse/LPS-44900 )

Well, it is not so much that they are working on a logging system, it is more like they have added the SLF4J bridge so the same SLF4J API used in other projects can be leveraged under Liferay. I know the MDC is a like to have and can really improve traceability in logs, but I'm not aware of any ongoing effort to switch out the logging. That said, neither am I necessarily privy to all of the activity and planning for 7.2 and/or future releases. I think moving to SLF4J is a solid recommendation to be ready for when (if) the MDC support comes, though.

Yeah, it really is a pain in the ass that Liferay does not support MDC out of the box especially because many clients are asking for that feature. Luckily it is not that difficult to manually enable it.