http://www.developer.com/

Back to article

Eclipse Tip: Don't Let Bugs Get Lost Without Trace


September 27, 2007

The ability to trace your program's execution may prove invaluable when you are trying to hunt down an elusive problem. Although the Eclipse Java Debugger is a powerful tool, there are situations when it just cannot be used effectively. Even during regular program execution, various error conditions can occur, and not necessarily due to bugs in your code. Systematically logging these errors aids in diagnosing the conditions under which a particular error occurred.

Eclipse Error Log

The Eclipse runtime makes a standard logging facility available to all plug-ins. However, those familiar with logging frameworks commonly used in enterprise applications, such as Log4J, might be disappointed—Eclipse logging is far less powerful at the first glance. For instance, it uses its own flat-text format, provides rather limited API, and generally lacks configurability (for example, you can only log to the console, or a rolling file located at a fixed path under your instance area). That said, it is fit for its purpose, and if all else fails, you can always implement an alternative solution (see the Resources section for some pointers).


Figure 1: Platform Error Log view.

The easiest way to view your IDE's error log is to open the Error Log view. It is not all too apparent where to find it, unless you're in the PDE perspective—it is tucked away under the PDE Runtime category (also try pressing Alt+Shift+Q, L). Once you open it, you'll see a table of log entries showing the status message (typically an error, but there are also warnings and information entries), the identifier of the plug-in that logged the entry, and the timestamp. Some entries may have "child" entries—this happens when multiple errors were grouped and logged together under one entry. Double-clicking an entry brings up the Event Details dialog, which also displays any attached exception stack trace, as well as your workbench session data.

You can also import and export a log to and from the view, respectively. If you're curious what its actual file contents look like, you can open the log in a text editor by clicking Open Log from the view's toolbar. In the filesystem, the log file is located in your workspace directory under .metadata/.log (note the leading dots).

Logging And Reporting Exceptions

To write to the Eclipse log, one must first obtain an instance of ILog. Each plug-in is provisioned with one for its own internal use. It can be obtained by calling Plugin.getLog(). All logging then is performed by calling log(IStatus). The argument is an instance of IStatus, which is used to represent the outcome of operations in Eclipse. As such, it is also used to carry detailed error information in CoreException, which is the standard checked exception used throughout Eclipse.

In most cases, you can use the Status class whenever you need to provide an IStatus (for instance, when throwing a CoreException). You also can subclass it to add application-specific data. If multiple status objects need to be aggregated and reported as one "super-status," you can use the MultiStatus and add child status objects to it. When a MultiStatus is logged, its children are properly expanded.

Listing 1: Logging a CoreException generated outside of your plug-in.
...
} catch (CoreException e) {
  IStatus status = new Status(IStatus.ERROR,
      TracePlugin.PLUGIN_ID, 0,
      "Could not perform requested action.", e);
  TracePlugin.getDefault().getLog().log(status);
  ErrorDialog.openError(
      window.getShell(), "Error", null, status);
}

Note that it also is possible to listen to an individual plug-in's logging requests. To do that, one would first obtain the plug-in's log by calling Platform.getLog(Bundle), and then registering an ILogListener, which is notified every time an entry is logged. For notification of all logging requests regardless of the origin, register your listener by calling Platform.addLogListener(ILogListener).

In a typical usage scenario, a plug-in would catch an exception generated elsewhere. As part of processing the exception, it would create a Status object describing the outcome of the operation (its own, not the one that threw the exception) and attach the caught exception to it. Finally, it would log the status to its log. If the attached exception is a CoreException, the logger detects it and expands the resulting status hierarchy (in other words, it retrieves the exception's status, and so on). Because the generated CoreException contains a status object created by the originating plug-in, it is not recommended (though quite tempting) to simply log the attached status to the receiver's log. Doing so results in a loss of context—you know that a basic operation failed, but you don't know where it was called and why. The attached example project illustrates the logging of various types of exceptions.

Optionally, the status object may be reused to report the error to the user by calling one of the ErrorDialog.openError(...) methods, which will pop up a standard error dialog capable of displaying status information. Note that as of version 3.3, a new service—StatusManager—is recommended for handling (that is, logging as well as reporting) exceptions. This service supports pluggable status handlers that can control the manner in which errors are logged and reported across the application.

Tracing Code Execution

In many logging frameworks, tracing is not much different from other kinds of logging. Typically, the same logging facilities also are used to capture the program's execution trace. Tracing, however, often generates a large amount of detailed information and thus it is not normally enabled in production other than for debugging. In Eclipse, tracing is a rather specific activity. In fact, it doesn't have all that much to do with logging. Rather, it has a lot to do with debugging.


Figure 2: Launching the example plug-in with tracing enabled.

Although there are no special facilities for logging trace messages (in fact, as horrible as it may sound, System.out.println() will often suffice), there is a special "debug mode" in which the platform can be launched. Plug-ins that support tracing must detect whether they are running in this debug mode (which, for good measure, has nothing to do with Java debugging) and only then generate trace messages. This is to avoid likely performance penalties, which are often incurred when tracing is enabled. Plug-ins may also query for "debug options," which is a simple property-based mechanism for configuring tracing.

Typically, a plug-in would first call Platform.inDebugMode(). This returns true if the platform was launched with the -debug argument. By convention, the plug-in should also check if the debug option <pluginId>/debug evaluates to true by calling Platform.getDebugOption("<pluginId>/debug"). This is considered the "root" debug option that determines whether a particular plug-in should enable tracing. Other arbitrary options may be used (by convention they should follow the pattern <pluginId>/debug/<option>). The PDE Launch dialog provides a Tracing tab that allows you to enable tracing (for example, launch the platform in "debug mode") and configure debug options for each plug-in. A plug-in must advertise which options it supports by specifying their default values in a .options file (note the leading dot), which must be located in the plug-in's root.

The example plug-in provided in the Resources section contains a small class (Debug) that can be used to implement simple tracing. Note that you should always check whether you are to generate a trace message before composing it, because not doing so could strain your resources unnecessarily. That is, rather than creating a generic trace method that takes arbitrary data to be logged and only then checks if tracing is enabled, do an if check before allocating any strings or other arguments for the message.

Example

The example plug-in provided in the Resources section demonstrates both logging and tracing. It contributes a top-level menu (Logging & Tracing) with actions that perform various logging and error reporting activities. The plug-in also supports several sample tracing options.

To get started, import the attached archive into your Eclipse SDK 3.3 as an Existing Project; click File -> Import, and then select General -> Existing Projects into Workspace. Provide the path to the downloaded zip file and click Finish.

To enable tracing for the plug-in, first create a launch configuration for it. Click Run -> Open Run Dialog, and then select Eclipse Application and click New. Go to the Tracing tab and check Tracing, and then select and check com.developer.tracing. Also, check all its debug options on the right. Figure 2 shows the configured Tracing tab. Click Run and observe debug messages in the console view as you invoke the various contributed actions.

Resources

About the Author

Peter Nehrer is a software consultant specializing in Eclipse-based business applications and development tools. He is the founder of Ecliptical Software Inc. and a contributor to several Eclipse-related Open Source projects. He holds an M.S. in Computer Science from the University of Massachusetts at Amherst, MA. Peter can be reached at pnehrer AT eclipticalsoftware DOT com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date