Using Eclipse logging. This tutorial gives an overview how to do logging in an Eclipse application.

1. Eclipse Logging

1.1. Platform logging

The Platform class provides simple logging API.

Platform.getLog(getClass()).error();
Platform.getLog(getClass()).warn();
Platform.getLog(getClass()).info();

1.2. Logger service

The org.eclipse.e4.core.services plug-in contains the Logger class. An instance of this class can get injected via dependency injection. The default implementation is provided by the WorkbenchLogger class.

@Inject Logger logger;

The Logger implementation wraps the IStatus into an simpler interface and provides several methods to log info, warning or error message. The following code shows example calls.

logger.info("Info: Closing application");
logger.error("Error: Closing application");
logger.warn("Warn: Closing application");

Customers can replace the WorkbenchLogger implementation in the Context with there own implementation. This way customers could log to the Eclipse system as well as to other external log systems.

The log level can be setup in the config.ini file of your application via the eclipse.log.level parameter. The value can be set to INFO, WARNING and ERROR, INFO will for example show all log message.

By default Eclipse logs all messages.

In case dependency injection is not available in the class where logging is requested, the logger can be obtained by using the service API:

Logger logger = PlatformUI.getWorkbench().getService(org.eclipse.e4.core.services.log.Logger.class);
logger.info("Using the eclipse logger");

2. SLF4J in eclipse applications

Many Java developers are more familiar with the SLF4J logging facade, which is more powerful than Eclipse’s default logger.

2.1. Obtaining the SLF4J library from Orbit

The Eclipse Orbit Project provides many different open source libraries, which can be obtained as OSGi bundles from Obrit’s p2 update sites. This makes it easy to use third party open source libraries for target definitions and a Maven Tycho build.

slf4j orbit updatesite

One of the most popular slf4j implementations is the Logback logger, which is also available on Orbit’s p2 update site.

logback orbit updatesite

The Orbit p2 update sites URLs change quite regularly. Therefore it is recommended to look in the Orbit downloads section to find the right p2 update site URL.

2.2. Using SLF4J

The SLF4J dependencies just have to be added to the desired bundle.

Use org.slf4j as imported package

After doing this the SLF4J logger API can be used in the classes of the bundle.

private static final Logger LOG = LoggerFactory.getLogger(ClassWithInfosToBeLogged.class);
It saves a lot of effort, when a Template for the creation of this LOG instance is created.

2.3. Configuring Logback

Usually Logback is configured by a logback.xml or logback-test.xml file. (see Logback configuration manual)

In an OSGi environment it is not that easy for Logback to automatically determine where the logback.xml logger configuration is stored.

For Logback the ch.qos.logback.classic.joran.JoranConfigurator is used to parse the logback.xml configuration file.

By obtaining the ch.qos.logback.classic.LoggerContext and using the JoranConfigurator the location of the logback.xml file can be set programmatically.

package com.vogella.logging.config;

import java.io.IOException;
import java.net.URL;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.service.datalocation.Location;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;

public class Activator implements BundleActivator {


    @Override
    public void start(BundleContext bundleContext) throws Exception {
        configureLogbackInBundle(bundleContext.getBundle());
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
    }

    private void configureLogbackInBundle(Bundle bundle) throws JoranException, IOException {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        JoranConfigurator jc = new JoranConfigurator();
        jc.setContext(context);
        context.reset();

        // overriding the log directory property programmatically
        String logDirProperty = // ... get alternative log directory location
        context.putProperty("LOG_DIR", logDirProperty);

        // this assumes that the logback.xml file is in the root of the bundle.
        URL logbackConfigFileUrl = FileLocator.find(bundle, new Path("logback.xml"),null);
        jc.doConfigure(logbackConfigFileUrl.openStream());
    }

}

In some cases it is pretty handy to have this logback.xml logger configuration in the configuration folder of the RCP application rather than bundled in a JAR file. So logback.xml logger configuration can easily be changed in a production environment if necessary. The local version of the logback.xml is still kept so that the developer does not have to copy the logback.xml file manually to the Platform.getInstallLocation() location.

private void configureLogbackExternal(Bundle bundle) throws JoranException, IOException {
    LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
    JoranConfigurator jc = new JoranConfigurator();
    jc.setContext(context);
    context.reset();

    // overriding the log directory property programmatically
    String logDirProperty = // ... get alternative log directory location
    context.putProperty("LOG_DIR", logDirProperty);

    // get the configuration location where the logback.xml is located
    Location configurationLocation = Platform.getInstallLocation();
    File logbackFile = new File(configurationLocation.getURL().getPath(), "logback.xml");
    if(logbackFile.exists()) {
        jc.doConfigure(logbackFile);
    } else {
        URL logbackConfigFileUrl = FileLocator.find(bundle, new Path("logback.xml"), null);
        jc.doConfigure(logbackConfigFileUrl.openStream());
    }
}

How to provide external static files like a logback.xml file for a product build with Maven Tycho can be found here: Eclipse Tycho Tutorial

The LOG_DIR property, which is overridden in the code above, can be obtained like this:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- With a context scope the ${LOG_DIR} variable can be obtained from the LoggerContext -->
    <!-- By using :- in the value attribute a default can be specified -->
    <property scope="context" name="LOG_DIRECTORY" value="${LOG_DIR}:-\home\default-log-dir" />

    <!-- Make use of the LOG_DIRECTORY property for the general xml configuration of Logback -->

</configuration>

3. Exercise - SLF4J and Logback in Eclipse applications

3.1. Target

Make use of SLF4J and Logback in an RCP application.

3.2. Obtaining the SLF4J and Logback library from Orbit

Install SLF4J and Logback into your IDE and reuse the IDE’s target platform or create a target definition file and add all necessary features, like eclipse SDK and logger libs, to it.

See how to obtain the SLF4J libraries from Orbit and Logback dependencies.

3.3. Create a plugin using SLF4J

Create a com.vogella.logging.rcp plug-in project, which uses the E4 Application template.

Add the SLF4J dependencies:

Use org.slf4j as imported package

Use a Logger in the generated AboutHandler class and log some debug messages or throw some Exceptions, which are logged.

package com.vogella.logging.rcp.handlers;

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AboutHandler {

    private static final Logger LOG = LoggerFactory.getLogger(AboutHandler.class);

    @Execute
    public void execute(Shell shell) {
        LOG.debug("Executing about handler");
        MessageDialog.openInformation(shell, "About", "Eclipse 4 RCP Application");
    }
}
It saves a lot of effort, when a Template for the creation of this LOG instance is created.

3.4. Creating a plug-in that configures Logback

Create a com.vogella.logging.config plug-in project with an Activator.

The root folder of this plug-in should contain the following logback.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
            </Pattern>
        </layout>
    </appender>

    <logger name="com.vogella.logger" level="debug"
        additivity="false">
        <appender-ref ref="STDOUT" />
    </logger>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

The following dependencies are required to configure Logback.

logback dependencies

Also using Imported Packages for the Logback dependencies would be preferable, but unfortunately Logback has so many packages that they won’t fit on the screenshot.

The Activator is supposed to initialize the Logback configuration.

package com.vogella.logging.config;

import java.io.IOException;
import java.net.URL;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.service.datalocation.Location;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;

public class Activator implements BundleActivator {


    @Override
    public void start(BundleContext bundleContext) throws Exception {
        configureLogbackInBundle(bundleContext.getBundle());
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
    }

    private void configureLogbackInBundle(Bundle bundle) throws JoranException, IOException {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        JoranConfigurator jc = new JoranConfigurator();
        jc.setContext(context);
        context.reset();

        // this assumes that the logback.xml file is in the root of the bundle.
        URL logbackConfigFileUrl = FileLocator.find(bundle, new Path("logback.xml"),null);
        jc.doConfigure(logbackConfigFileUrl.openStream());
    }

}

3.5. Startup the logging config bundle

The configuration of the SLF4J implementation (Logback) should be separated from the other bundles. So that no dependency to the com.vogella.logging.config bundle is necessary and other bundles just need to import the org.slf4j package, like it is done com.vogella.logging.rcp.

Due to that the com.vogella.logging.config bundle will not be started automatically. Therefore it must be added to the Start Levels in the product configuration.

Configure Start Levels in the Product

3.6. Validate

Start the sample RCP application and see whether the logging looks like expected according to your logback.xml configuration.

4. Logging resources