Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider migrating from Commons-Logging to SLF4j [SPR-5327] #10000

Closed
spring-projects-issues opened this issue Nov 27, 2008 · 24 comments
Closed
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import status: declined A suggestion or change that we don't feel we should currently apply type: task A general task

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Nov 27, 2008

Grzegorz Borkowski opened SPR-5327 and commented

Currently Spring Framework depends on Commons-Logging. All of us know that for long time Commons-Logging has very bad reputation and is the source of many problems, bug, memory leaks etc.
The SLF4j is the newer implementation of similar idea, and many people claim it works much better than Commons-Logging. It contain even some adapter for Commons-Logging users, allowing for gradual transition. Perhaps with Spring 3, along with migration to Java 5, is the best time to migrate also to SLF4J and remove dependency on Commons-Logging?
In fact, you can probably do it somehow automatically, writing the script that will replace in all java files the imports and logger types.


Affects: 3.0 M4

Attachments:

Issue Links:

Referenced from: commits f4763a8, 1a06b6a, ad3fa50, 604a9f0, b7e37dd, 1202f67, 0400ccc

20 votes, 13 watchers

@spring-projects-issues
Copy link
Collaborator Author

Tim Wlcek commented

As I have been spending the last two days trying to migrate my project to slf4j and to fix the transitive dependencies from maven, I would dearly love to see this issue resolved. At least consider marking clogging as optional in the spring poms.

@spring-projects-issues
Copy link
Collaborator Author

Carlos Zuniga commented

Here is a stab at changing the logging framework from Commons Logging to SLF4J. Feedback is welcome.
Since this issue has not been assigned I don't know if the commiters will be able to see it. May be the watchers can ping them if you have more direct communication with them.

Carlos

@spring-projects-issues
Copy link
Collaborator Author

adrian commented

JCL can be a pain to setup on Websphere, see https://www.ibm.com/developerworks/forums/thread.jspa?threadID=227753. Migrating to SLF4J would resolve this issue.

Also SLF4J provides a migration utility which could be used all the spring code baseline (didn't tested it though) : http://www.slf4j.org/migrator.html

@spring-projects-issues
Copy link
Collaborator Author

Neale Upstone commented

This doesn't need to be nailed down as JCL or SLF4J.

The main problem here, for which I'm going to report a separate bug, is that the logging dependency is not left for the runtime to provide, but is instead pulled in as "compile" scope within the Spring 3.0M3 POMs.

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>com.springsource.org.apache.commons.logging</artifactId>
  <version>1.1.1</version>
  <scope>compile</scope>
</dependency>

Could read:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>com.springsource.org.apache.commons.logging</artifactId>
  <version>1.1.1</version>
  <scope>provided</scope>
</dependency>

I agree that SLF4J is now a far better choice than JCL. It can work very nicely with Maven.

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

The current proposal (for discussion) from the Spring team is that we simply switch to declaring jcl-slf4j as the primary dependency, requiring users to provide their own bindings from there. This makes it easy to integrate third-party libraries that already use slf4j without going to whole hog ourselves. Or we could leave things as they are, and allow people to adapt in that case by using the same bindings (log4j, JUL, etc.) to our commons-logging and the third-party slf4j.

The suggestion from Neale that we simply make commons-logging a "provided" dependency is interesting, signalling that it is mandatory but must be provided by the host (who can choose jcl-slf4j if they wish). I would prefer to leave the primary logging dependency as "compile" scope, but it comes to the same thing really - when you run an application for the first time it will fail and tell you that there is a missing class. The one saving grace of commons-logging is that it doesn't do that, but of course that is also its downfall: the discovery process is flawed.

Using "provided" scope doesn't make life any easier for WAS users. Only moving our primary dependency to sl4j would help, and even then it might not help everyone - presumably there are people who actually like having WAS control their logging configuration, and all they have to do is leave out commons-logging from their WAR/EAR packaging.

@spring-projects-issues
Copy link
Collaborator Author

Grzegorz Borkowski commented

IMHO, the primary logging dependency of Spring should be switched to SLF4J. From all possible decisions about logging library choice, in year 2009 this one seems the most reasonable.
The only question is: make it compile scope or provided scope. I'd rather opt for compile scope, because most application servers probably don't contain SLF4J ootb. But I'm not 100% sure about this choice.

@spring-projects-issues
Copy link
Collaborator Author

Stevo Slavić commented

See here how Jetty (maven plugin) did it.

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

Stevo: I looked at what Jetty does, and we don't want to go there because it involves adding yet another logging abstraction with runtime classpath discovery (like commons-logging but smarter).

Grzegorz: SLF4J is undoubtedly superior to commons-logging, but the basic JCL API that we use in Spring by and large is not so evil. And we have a strong commitment to backwards compatibility: i.e. most if not all applications should work just by upgrading the version of Spring. Admittedly there has never been a concerted effort to match that commitment with our declared dependency management artifacts (POMs etc.), but that's no reason not to consider the general principle.

I actually think now that the best thing we can do is leave commons-logging as a primary compile-scoped dependency, but only in spring-core. Features of that approach are:

  • All existing applications continue to work with no changes.
  • All new applications work out of the box with no additional dependencies to declare other than Spring.
  • To switch to SLF4J, just exclude commons-logging from one dependency (spring-core) and add the jcl-slf4j bridge, plus your favourite binding.
  • If using Hibernate (or other SLF4J external library) as well as Spring, you have a choice:
  1. switch Spring to SLF4J with the bridge as above
  2. add SLF4J bindings to get match your choice for the external library (e.g. slf4j-log4j12)
  3. add a different SLF4J binding and have separate log configuration for Spring and the external library

\
To make that happen we need to make sure that commons-logging is compile scoped, and that it is the only mandatory (not optional and not test scoped) logging dependency, and that it is only declared in core-spring. I'm not sure if the last point is strictly possible yet until I try it (i.e. there might be Spring libraries that depend on commons-logging but not core-spring).

@spring-projects-issues
Copy link
Collaborator Author

allnightlong commented

In my opinion, major release is the best moment to switch to modern slf4j. If spring wouldn't do this, all community have to wait until the next major release (about 3 years?), for another chance.
Also, there are a lot of developers, which considering spring as flagship of java frameworks. And if spring finally make right descision, it will give a great leap to whole industry.
Also I think, that shipping spring with sl4j-api and jcl-over-slf4j will resolve all backward compability problem.

@spring-projects-issues
Copy link
Collaborator Author

Grzegorz Borkowski commented

Keeping backward compatibility is an important point, and this must be seriously considered.
There are several other considerations, though:

  1. putting explicit dependency on JCL only in spring-core (and use is as transitive dependency in other spring modules) could work, but it's not semantically correct, I think - because other spring modules use JCL directly in their code too. So in theory, they should declare JCL as their direct dependency - at least that's how I understand dependency management.
  2. SLF4J has one significant advantage over other logging APIs: parametrized logging (i still can't understand why it's not built into JUL since Java 5 - varargs seems perfect match for this). I've just look at first Spring class code that I've found: CachedIntrospectionResults:
if (logger.isDebugEnabled()) {
  logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}

The same code using SLF4J would look like:

logger.debug("Not strongly caching class [{}] because it is not cache-safe", beanClass.getName());

I've seen some projects where "if isDebugEnabled" was the most repeatable pattern around the code, that's why I'm allergic to it :) This means that new Spring code will be able to use this approach, which is better for code readability.
3) JCL is not a public API of Spring; that's only the dependency, so changing it shouldn't break someone else's code. If someone wants to switch from Spring <3.0 to 3.0 in your project, they probably want to rebuild the project anyway. Most projects nowadays use Maven or Ivy anyway, so the dependency would automatically be updated. Besides, it's not a big deal to publish information about the change of logging dependency and tell people how to manage it.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Actually, we do expose a JCL Log in plenty of Spring-provided base classes... Changing the type of that field to an SFL4J logger would break binary compatibility with external subclasses using that logger, not only with user code but also with third-party libraries that are built on Spring (and can't be recompiled as easily as user code).

Juergen

@spring-projects-issues
Copy link
Collaborator Author

adrian commented

Changing the type of that field to an SFL4J logger would break binary compatibility

Ok, this is a problem, but perhaps with version 3, you can allow yourself to break compatibility (and use private modifier for SLF4J logger attributes) ?
Or deprecating logger attribute and switch to SLF4J in Spring 3.1 (bof...) ?
A lot of open source projects [1] are switching to SLF4J, so perhaps it would be a good idea also for Spring to switch to SLF4J (hence we have only one logging facade in our libraries).

[1] Some projects of those projects :

Other projects not using SLF4J

Perhaps contacting main open source projects for a 'global' switch to SLF4J would be a good idea (struts2, wicket, gwt, ...) ?

About using jcl-slf4j and modifying spring's pom.xml dependencies to jcl-slf4j.

I thought this issue was more about modifying spring source dependency towards jcl. Everyone can already use Spring 2.5.6 with jcl-slf4j (I'm already using this setup in my projects).

Switching to SLF4J for WAS users

It would enable some of us to setup spring application with parent_first classloader setting (I'm tied to parent_last since I'm using jcl-slf4j)... until WAS also switches to from JCL to SLF4J)

@spring-projects-issues
Copy link
Collaborator Author

Grzegorz Borkowski commented

Actually, we do expose a JCL Log in plenty of Spring-provided base classes...

Well, if that's the case, than situation is much more difficult. I've never used such Spring logger, but many others could have done it. Spring always focused on keeping backward compatibility, so I understand that changing all loggers to SLF4J is problematic.

One choice is to break this compatibility, as it was suggested by others - major releases theoretically allow for this; besides, the change is not that big, and other libraries can be quite easily aligned. Spring already did some breaking compatibility change in 3.0, by changing naming scheme of modules (for maven/ivy), which cases problems in some cases.

However, if you decide that you simply can't do it, than we have to use some of the approaches proposed above, like e.g. restrictive dependency on JCL to spring-core, ... others?

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

Resolving this one as Won't Fix, but this is a good discussion. I have captured some of it in a new section of the user guide.

@spring-projects-issues
Copy link
Collaborator Author

Jon Fisher commented

So to summarize, the only reason for not migrating is backwards compatibility, is this correct? :(

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

Yes, that's it in a nutshell. Commons logging as an implementation has its problems but the API is good enough for our purposes so it doesn't make sense to break anything. The JCL-SLF4J bridge is a good option for people who want to mix and match with SLF4J/

@spring-projects-issues
Copy link
Collaborator Author

Keith Donald commented

Examples of projects with Maven POMs that pull down SLF4J with the commons-logging bridge instead are here:
Maven Central compatible: https://src.springframework.org/svn/spring-samples/mvc-ajax/trunk
Enterprise Bundle Repository: https://src.springframework.org/svn/spring-samples/petcare/trunk/

@spring-projects-issues
Copy link
Collaborator Author

Neale Upstone commented

I didn't look back on this at the time, but I'll make another play for htis on 3.1.

At the moment, we still have 3.1 with Commons Logging in compile scope. While it won't make life any easier for those on Websphere, can we please change to provided? Maven POMs are verbose enough without being cluttered with every Spring dep with an exclusion for CL

@spring-projects-issues
Copy link
Collaborator Author

Dave Syer commented

Sorry Neale, but I don't think it's going to happen - we don't want WAS users to be the only group for whom Spring works out of the box. You should only need an exclusion for spring-core, as that's the only POM that should depend on commons-logging. If that's not the case you can raise a separate JIRA ticket.

@spring-projects-issues
Copy link
Collaborator Author

Neale Upstone commented

Ah. Had that lightbulb moment (ding!), which I'll share for all, and.. I suggest should be the standard approach for Spring examples and for Spring Roo.

The wrong way of fighting to exclude SLF4J is as demonstrated in https://src.springframework.org/svn/spring-samples/mvc-ajax/trunk, which is to include a maven artifact, and exclude it. You can end up with something like this, which works. It makes sense that any single dependency recording an exclusion should exclude for all.

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>3.0.5.RELEASE</version>
    <scope>compile</scope>
    <exclusions>
     <exclusion>
         <artifactId>commons-logging</artifactId>
         <groupId>commons-logging</groupId>
     </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-oxm</artifactId>
    <version>3.0.5.RELEASE</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

This is unclear, and for anyone who doesn't know the detailed black box magic of Maven's internals, they may duplicate these (just create a vanilla project under Spring Roo 1.1.2 and you'll see this demonstrated).

The clear way of expressing what Dave has clarified above (that it only needs excluding on spring-core), is to use dependencyManagement:

  <dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>3.0.5.RELEASE</version>
          <exclusions>
           <exclusion>
               <artifactId>commons-logging</artifactId>
               <groupId>commons-logging</groupId>
           </exclusion>
          </exclusions>
        </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>
  </dependencies>

Simpler still, for a multi-module project, the dependencyManagemnt entry would go in a parent POM.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

FYI, and FWIW, Hibernate 4.0 migrates away from SLF4J and uses their own JBoss Logging now. So it doesn't look like common Java open source projects actually agree on SLF4J on the way forward.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Aaron Digulla commented

That isn't entirely correct. Someone seems to love the JUL API so much that they wrapped it in Java 5 (varargs support) and wrote a couple of plugins to translate it to various logging frameworks (JUL, log4j and slf4j are supported even though slf4j isn't mentioned in the docs). It's in 4.0 release the POM as a test dependency, though.

The code looks fast enough but I'm not sure how much value you get from adding another log-layer on top of the existing ones.

Since I can't find any pointers why they did it, my guess is currently that the JBoss logging layer allows to i18n the log messages. Great if you need it but I'm wary of the performance impact.

That said, I'd still vote to replace JCL with slf4j for these reasons:

  • slf4j gives consumers options
  • JCL's automatic logging framework recognition is flawed/broken/insufficient, depending on your point of view.
  • JCL is dead, slf4j is still supported by it's developer
  • Re backward compatibility: You're leaking a dependency. Eventually, you will have to fix that anyway. Why not replace something broken with something good? Yes, developers will complain but frameworks like logback have so many useful features and their config is so much more simple and more powerful that I regret every day that I didn't upgrade from log4j.

For example: I've written an appender with logback that logs nothing until an error happens. At that time, it will log the last 20 INFO messages and the last 200 DEBUG messages. The whole code is about 250 lines. I get a log file which only contains interesting information plus a fast logger.

To drive this home: I'm patching my third-party dependencies to use slf4j if they depend on JUL. The last bigger project on my list was BIRT (patched 400+ files). ZK is probably next.

Spring uses JCL, which is not a very big problem because I can use the slf4j bridge which causes only a small performance loss but I always prefer frameworks that don't tie my to a certain logging implementation at runtime. The problem with JUL is that the translation is very, very expensive.

@spring-projects-issues
Copy link
Collaborator Author

Adriano Machado commented

@Aaron Digulla: can you share that logback appender?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Nov 20, 2014

Hendy Irawan commented

Can this be reconsidered?

While it seems late, it's not late for next major version of Spring (i.e. 5.0) which presumably will have Java 9 support.

I'd vote for replacing commons-logging dependency with slf4j-api dependency, which won't break most applications. Spring's JCL Logger can be marked as deprecated, while the new Spring Logger uses SLF4J. Legacy subclass of Spring JCL Logger can still be supported by including the commons-logging in the user's project. Spring itself still depends on commons-logging but marked optional=true.

The deprecation can start in 4.1.x or 4.2.x and legacy support removed in Spring 5.0 or Spring 6.0.

To add weight to this issue, most Spring-related projects already use SLF4J API anyway, see LDAP-273, DATAREST-54, DATAMONGO-391, etc.

#11273 refers to this.

@spring-projects-issues spring-projects-issues added status: declined A suggestion or change that we don't feel we should currently apply has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import type: task A general task labels Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import status: declined A suggestion or change that we don't feel we should currently apply type: task A general task
Projects
None yet
Development

No branches or pull requests

1 participant