Logging Best Practices
Logging Frameworks
SLF4J is the preferred logging API
Logback is the preferred logging implementation for Jasig portlets.
Project Setup
The following assumes a project built with Maven and using the Spring Framework.
Webapp Configuration
To allow for the log files to be written to a location relative to the webapp Spring's WebAppRootListener is added to the web.xml. This listener sets the absolute file system path to the webapp as a system property. Because this is a system property the webAppRootKey value needs to be set to the name of your portlet application.
<context-param> <param-name>webAppRootKey</param-name> <param-value>CHANGE_TO_YOUR_PORTLET_NAME.webapp.root</param-value> </context-param> <!-- Must be the first listener so the webAppRootKey gets set for logback --> <listener> <listener-class>org.springframework.web.util.WebAppRootListener</listener-class> </listener> <!-- Needed to remove JMX registration and allow for classloader GC --> <listener> <listener-class>ch.qos.logback.classic.selector.servlet.ContextDetachingSCL</listener-class> </listener>
Note: For uPortal code and BUNDLED portlets, current uPortal code substitutes the log file directory at build time into logback.xml or log4j.properties from the filters file so the webAppRootKey and org.springframework.web.util.WebAppRootListener listener class is not required (though it will not hurt anything either).
Remove entries for Log4j from web.xml:
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/classes/log4j.properties</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
Logback Configuration
Logback Configuration
Place logback.xml in src/main/resources/ The root of the classpath is the default location for logback configuration.
Change the two instances of the string CHANGE_TO_YOUR_PORTLET_NAME to the name of your portlet application.
Logback Test Configuration
Place logback-test.xml in src/test/resources/ Logback will use the logback-test.xml file from the classpath in preference to logback.xml.
Logback Configuration for Overlay portlets
If the portlet is included in uPortal as an overlay portlet (e.g. in directory uportal-portlets-overlay), copy overlay-logback.xml to uportal-portlets-overlay/PortletName/src/main/resources/logback.xml.
Change the one instance of the string CHANGE_TO_YOUR_PORTLET_NAME to the name of your portlet application. Also adjust the portlet package name for the DEBUG log statement that's commented out.
Logback configuration for portlets participating in mvn data-import
If the portlet uses the data-import maven goal to import data (Calendar portlet does this for example), copy command-line.logback.xml to src/main/resources and configure the java fork of cernunnos to use this file; e.g.
<java failonerror="true" classname="org.danann.cernunnos.runtime.Main"> <sysproperty key="logback.configurationFile" value="command-line.logback.xml" /> ... </java>
Remove the now obsolete log4j.properties files
Delete the log4j.properties file from
- src/main/resources
- src/test/resources
- uPortal's uportal-portlets-overlay/PortletName/src/main/resources folder (bundled portlets only)
Maven Configuration
Approach 1 (easiest and strongly recommended): Remove log4j and commons-logging using provided scope
Use provided scope. This has the advantage of being quick and easy, but when running unit tests your IDE, e.g. Eclipse, will still place commons-logging.jar on your projet's class path as seen by your IDE. You would need to make sure that jcl-over-slf4j.jar is visible before commons-logging.jar by your IDE. See http://www.slf4j.org/faq.html#excludingJCL.
Step 1: Remove all references to commons-logging and log4j from the dependencyManagement section, dependencies section, and from all exclude entries.
Step 2: Add dependencies to dependencyManagement and dependencies (and versions in properties). Update slf4j to the latest version and logback to the latest version.
<properties> ... <logbackVersion>1.0.13</logbackVersion> <slf4jVersion>1.7.5</slf4jVersion> </properties> <dependencyManagement> ... <!-- For sl4j/logback logging, see https://wiki.jasig.org/display/PLT/Logging+Best+Practices --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4jVersion}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>${slf4jVersion}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>${slf4jVersion}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4jVersion}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logbackVersion}</version> <scope>runtime</scope> </dependency> <!-- End of logging section --> </dependencyManagement> <dependencies> ... <!-- Logging section --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <!-- End of logging section --> </dependencies>
Approach 2 (not preferred)
Use the tree goal of the maven dependency plugin to see where these libraries are pulled in from. The end of the output will have a block that looks something like what you see below. The goal here is to find any common-logging or log4j dependencies, trace up to the root dependency and add an excludes block to it.
$ mvn dependency:tree .... +- joda-time:joda-time:jar:2.2:compile +- org.jasig.resourceserver:resource-server-content:war:1.0.29:compile +- org.jvnet.jaxb2_commons:jaxb2-basics-runtime:jar:0.6.4:compile +- org.springframework:spring-web:jar:3.2.2.RELEASE:compile | +- commons-logging:commons-logging:jar:1.1.1:compile | +- aopalliance:aopalliance:jar:1.0:compile | +- org.springframework:spring-aop:jar:3.2.2.RELEASE:compile | \- org.springframework:spring-context:jar:3.2.2.RELEASE:compile | \- org.springframework:spring-expression:jar:3.2.2.RELEASE:compile +- org.slf4j:slf4j-api:jar:1.7.3:compile +- org.slf4j:jul-to-slf4j:jar:1.7.3:compile +- org.slf4j:log4j-over-slf4j:jar:1.7.3:compile +- org.slf4j:jcl-over-slf4j:jar:1.7.3:compile +- ch.qos.logback:logback-classic:jar:1.0.10:runtime | \- ch.qos.logback:logback-core:jar:1.0.10:runtime +- javax.servlet:servlet-api:jar:2.5:provided \- junit:junit:jar:4.11:test \- org.hamcrest:hamcrest-core:jar:1.3:test ....
Example excludes:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.2.2-RELEASE</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
Setup the enforcer plugin in the build/plugins section to blacklist commons-logging and log4j
<plugin> <artifactId>maven-enforcer-plugin</artifactId> <executions> <execution> <id>enforce-versions</id> <goals> <goal>enforce</goal> </goals> <configuration> <rules> <bannedDependencies> <excludes> <exclude>commons-logging:commons-logging</exclude> <exclude>commons-logging:commons-logging-api</exclude> <exclude>log4j:log4j</exclude> </excludes> </bannedDependencies> </rules> </configuration> </execution> </executions> </plugin>
Step 2: Add sl4j and logback
Add the following dependencies to the project, update slf4j to the latest version and logback to the latest version.
<!-- For sl4j/logback logging, see https://wiki.jasig.org/display/PLT/Logging+Best+Practices --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.0.10</version> <scope>runtime</scope> </dependency> <!-- End of logging section -->
Update overlay files
If the portlet is also included in a maven overlay, update the maven overlay project pom.xml as well.