Monday, December 26, 2011

OSGi Tracing in Equinox

The Equinox implementation of OSGi provides the tracing APIs in org.eclipse.osgi plug-in since Eclipse 3.5. The tracing options are generally Boolean flags stored in a .options file as key-value pair. A typical .options file would look like this

The hierarchy is defined using the slashes. But this is only a general practice. The tracing does not understand this.

To load the values from the .options file, provide the file name as command line parameter (or program parameter in launch config)

command line arg example
eclipse.exe -debug C:\tracing\.options

program argument (in launch config) example

Using -debug is same as setting the osgi.debug system property.

Accessing the option values at runtime
1. In RCP application
If the RCP application has access to the org.eclipse.core,runtime.Platform object and thus they can access it directly like this

String value = Platform.getDebugOption("");

User can validate if the program is running in debug mode using Platform.inDebugMode() API.

2. In pure OSGi application
An OSGi application can access these options using the DebugOptions service

ServiceTracker debugTracker = new ServiceTracker(bundleContext, DebugOptions.class.getName(), null);;
DebugOptions debugOptions = (DebugOptions) debugTracker.getService()
boolean uiOption = debugOptions.getBooleanOption("", false);

Using the API has the advantage that user does not have to check for nullness.

More Tracing API
org.eclipse.osgi provides more APIs which are very helpful in logging the trace.

1. Registering a debug option listener
The listener can be registered as service using the BundleContext (that you receive in the activator class when the bundle is started)

final Hashtable<String, String> properties = new Hashtable<String, String>(4);
properties.put(DebugOptions.LISTENER_SYMBOLICNAME, TracingConstants.BUNDLE_ID);
bundleContext.registerService(DebugOptionsListener.class.getName(), new MyDebugOptionsListener(), properties );

2. Listening to the debug option change events
Once the listener is registered, a callback will happen each time a debug option is changed. The callback does not tells you the delta and instead throws the whole lot. So we can use this place to read and init from the DebugOptions

/* (non-Javadoc)
* @see org.eclipse.osgi.service.debug.DebugOptionsListener#optionsChanged(org.eclipse.osgi.service.debug.DebugOptions)
public void optionsChanged(final DebugOptions options) {
//DebugTrace trace;
trace = options.newDebugTrace("");
// boolean field
DEBUG = options.getBooleanOption("", false);

this is just an indicative code. You don't have to necessarily do it this way.

3. Writing to trace
This is the easiest part. Afterall the reason we did the whole exercise was to make the tracing simple. A handle to trace object can be obtained from the debug options.

/** Trace object for this bundle */
private final static DebugTrace TRACE = MyPluginActivator.getDefault().getTrace();  // returns the trace object obtained using options.newDebugTrace("");

protected Control createContents(final Composite parent) {
if (MyPluginActivator.DEBUG) {
TRACE.traceEntry("/debug", parent);

Checking MyPluginActivator.DEBUG is optional. TRACE.traceEntry will check for the flag "/debug" anyway. This is to improve of the performance. We store the the flag values to boolean fields and update them through the listener.

4. Where is the log file?
 The log is stored in the file as set in the debug options. By default, it tends to store them to workspace\.metadata\trace.log

options.setFile(new File("/path/to/trace/file"));

5. Controlling the trace logs
The logs are conntolled by two system properties

eclipse.trace.size.max : The system property used to specify size a trace file can grow before it is rotated
eclipse.trace.backup.max : The system property used to specify the maximum number of backup trace files to use

Easier way to manager the logs?
PDE will be providing UI for managing the logs and make them dynamic. See Bug 296631. This is mostly like to make it to Juno M5 release.

What do you mean by dynamic?
If you notice, the debug options can be loaded at the launch time (using -osgi.debug) or programatically when running. However, if you wish to turn the tracing on/off in a running product, its tricky. PDE will be providing an extension using which a plug-in can expose its debug option flags. The UI will display these flags in preferences where they can be turned on/off for a running application. Cool! isn't it?

* If you find any discrepancies in the above post, please bring it to my notice so that they can be updated.

Update: Tracing UI is now available in latest Integration builds and will be there from Juno M5.