Two ways to enhance the Eclipse log function
Level: Intermediate
Manoel Marques
(Manoel@themsslink.com) Advanced Consultant, The Missing Link, Inc. October 2004
Eclipse is lacking like a log4j such as J2SDK Logging Utilities or Apache's log-configurable log tool. In this article, we will learn how to configure and use the log frame for the Eclipse plugin, which is also a Apache log4j-based plugin. For your convenience you use and expand, this article also provides a complete source code.
Why use a log? Good developers know the importance of careful design, testing and debugging. Although Eclipse can help developers implement these tasks, how do it deal with logs? Many developers believe that for good software development practices, the log is an indispensable part. If you have amended the procedures that others have deployed, you will undoubtedly agree to this. Fortunately, the log is small for performance, and most of the cases do not even have any effect on performance, and because the log tool is very easy to use, the learning curve is also very smooth. Therefore, for existing excellent tools, we have no reason not to add logging in the application.
The tool that can be used If you are writing an Eclipse plugin, you can use org.eclipse.core.Runtime.ilog, it can be accessed through the GetLog () method of the PLUG class. Just create an instance of org.eclipse.core.Runtime.Status using the correct information, and calls the Log () method of the iLog.
This log object can receive multiple log listener instances. Eclipse adds two listeners:
A listener writes to the "ERROR log (Error Log)" view. A listener writes to the log file located in the "$ {Workspace} /. Metadata / .log".
You can also create your own log listener, just implement the org.eclipse.core.Runtime.iloglistener interface and use the AddLoglistener () method to add it to the log object. In this way, each log event can call the Logging () method of this class.
Although all content is very simple, this method has some problems. How do you handle if you want to modify a deployed plug-in target? Or how do you control the number of log information recorded? Also, this implementation may have an impact on performance because it always wants to send log events to all listeners. This is why we usually only see the reasons for logging logs only in extreme situations (eg, wrong conditions).
On the other hand, there are two outstanding tools specifically for logs. A java.util.logging package from Java 2 SDK 1.4; another comes from Apache, named log4j.
Both tools use the concept of the hierarchy of log objects, and you can send log events to any number of handles (Handler, called Appender in log4j), which represents send to formatted programs (Formatter, A formatted message is called Layout in log4j. Both tools can be configured by attribute files. Log4j can also be configured using an XML file.
The recorder can have a name and associated with a certain level. The recorder can inherit the parent's settings (level, handler). A recorder named "org" will automatically be another parent of the recorder named "org.eclipse"; thus don't care how to set "ORG" in the configuration file, these settings can be "org.eclipse) "Recorder inherits. Which tool I prefer? I have used these two tools, but I prefer Log4j. I only use java.util.logging in a very simple program, I don't want to add log4j.jar in such a program. For a detailed description of these two tools, see the Java documentation and Apache sites (see links in the reference).
An improved log if there is a way to improve the Eclipse log experience, isn't it great? But there is two problems:
Lack of external configuration files. Performance issues, and there is still a lack of fine-grained control for log behavior.
After giving this problem, I start considering how to integrate log tools into Eclipse. The first choice I can use is java.util.logging, which is very simple: this package has been included in the JSDK1.4 release.
I want to use an editor to customize log behavior by profiles, allowing log events to any available handles. I plan to create two handlers: a location responsible for sending the log event to the "ERROR log" view, and the location where the log is written to the plugin: "$ {.plugins / $ {plugin. ".
All content will be included in a Plug-in log manager. You can only join the plugin slave relationship and get the log object from it.
However, according to my experience, I don't recommend using java.util.logging to implement this feature. Because the implemented code will be a long, it can only reserve a LogManager instance; it uses the system class loader to achieve this. In this way, all users have only one hierarchy, you will lose isolation. Therefore, if many applications are using this logger, they will share the sharing settings, and an application can inherit the settings of other application loggers.
In this case, why don't we expand LogManager and implement a recorder yourself? The problem with this method is that the LogManager instance uses the system class loader to instantiate the class from the configuration file. One of the advantages of this plug-in is to provide isolation by using different type loading programs. If your log manager needs to isolate, Java.util.logging may not suit your requirements due to the limitations of the architecture.
On the other hand, Log4j has proven to be very useful. Regardless of whether you believe or whether the hierarchy of log4j's logger remains in an object called Hierarchy. Therefore, you can create a hierarchy for each plugin so that the problem is solved. You can also create a custom Appender to send events to the ERROR LOG view, create a location where the event is sent to the plugin. This life has become beautiful.
Now let's review how the entire process is implemented, we start from the perspective of the plug-in editor, create a plugin, add com.tools.logging to the slave type list, and create a log4j profile. Installation of PluginLogManager and configure it using the profile. Since this process only needs to be done once, you only need to perform this operation when you start the plugin. For log statements, just use it like it in log4j. Listing 1 gives an example: Listing 1. Configuration of PluginLogManager in the TestPlugin plug-in class
Private static final string log_properties_file = "Logger.properties";
Public void start (bundleContext context) throws exception {
Super.Start (Context);
CONFIGURE ();
}
Private vid configure () {
Try {
URL URL = GetBundle (). Getentry ("/" log_properties_file);
InputStream PropertiesInputStream = url.openstream ();
IF (PropertiesInputStream! = NULL) {
Properties PROPS = New Properties ();
Props.Load (PropertiesInputStream);
PropertiesInputStream.close ();
This.logmanager = New PluginLogManager (this, props);
this.logmanager.hookplugin
TestPlugin.getDefault (). GetBundle (). GetSymbolicName (),
TestPlugin.getDefault (). Getlog ());
}
}
Catch (Exception E) {
String message = "Error While Initializing Log Properties."
E.getMessage ();
IStatus status = new status (istatus.error,
GetDefault (). getBundle (). getSymbolicName (),
IStatus.Error, Message, e);
GetLog (). log (status);
Throw new runtimeException
"Error While Initializing Log Properties.", E);
}
}
Regardless of when deploying plugins, you only need to modify the log configuration file and log filtering, or modify its output without modifying any code. Better point is that if the log is disabled, then all statements do not affect performance because performance is one of the main considerations of log4j design. So you can use this recorder to use this recorder in any necessary.
How to implement the use of com.Tools.logging, we will talk so much; now let us look at its implementation.
Let's take a look at the class PluginLogmanager. Each plugin has a log manager. The manager contains a hierarchy object, as well as the data required to customize Appenders, as shown in Listing 2. This object is not directly derived from the hierarchy object, so it is inconvenient to expose it to the end user. It provides more freedom in implementation. The constructor creates a hierarchy object using the default Debug level, and then configure it with the supplied properties. It can also simply use the XML attribute; it is necessary to use Domconfigurator if you add from the XERCES plug-in to the Domconfigurator. This part of the content is left to the reader as an exercise. Listing 2. PluginLogManager constructor
Public PluginLogManager (Plugin Plugin, Properties "{
THISLOG = Plugin.getlog ();
THIS.Statelocation = Plugin.getStatelocation ();
This.hierarchy = new hierarchy (new rootcategory (level.debug);
THISHIERARCHY.ADDHIERARCHYEVENTLISTENER ());
New PropertyConfigurator (). Doconfigure (property, this.hierarch);
LoggingPlugin.getDefault (). Addlogmanager (this);
}
Note How the PluginLogManager internal class implements org.apache.log4j.spi.hierarchyeventListener. This is a solution to the customized appender to deliver the necessary information. When you have instantiated and completely configured with the appender and ready to add it, the addAppenderevent () method is called, as shown in Listing 3:
Listing 3. PlugineventListener class
Private Class PlugineventListener Implements HierarchyeventListener {
Public void addappenderevent (category cat, appender appender) {
IF (appender instanceof pluginlogappender) {
(PluginLogaPpender) .Setlog (log);
}
IF (appender instanceof pluginfileappender) {
(PLUGINFILEAPPENDER) .SetStateLocation;
}
}
Public void RemoveAppenderevent (category cat, appender appender) {
}
}
In order to better understand the life cycle of the appender and some decisions, the UML sequence diagram can be used (UML Sequence Diagram). Figure 1 shows the order sequence of creating and configuring the PlugInfileAppender instance.
Figure 1. PluginfileAppender configuration sequence diagram
For this appender, we have expanded org.apache.log4j.rollingfileAppender. This not only allows you to operate freely, but also provide a lot of useful features, such as the upper limit of the file size; when the log is reached, the log is automatically overlapping another file.
You also need to correctly process its behavior by selecting the RollingFileAppender. When log4j creates appender, the "setter" method will initialize its properties from the configuration file, and then call the activateOptions () method to make the additional program complete the unfinished initialization operation. When this operation is performed, the ROLLINGFILEAPPENDER instance calls setFile (), which will open the log file and ready to write the log. Only if log4j will notify the PlugineventListener instance. Obviously, you cannot open the file before you have the opportunity to set the plug-in location. Therefore, when the ActivateOptions () is called, if there is no location information, it will be marked as unrestricted; when the location information is finally set, the method will be called again. At this time, the appender is ready, which can be used.
Another life cycle of an Appender PluginLogappender is the same, but because it does not expand existing Appenders, you don't have to worry about initialization. Appender will not start before the AddAppenderevent method is called. The log4j document has been discussed in detail on how to write custom Appenders. Listing 4 gives an Append method.
Listing 4. Append method for PluginLogappender
Public void append (loggingEvent event) {
IF (this.Layout == null) {
THIS.ERRORHANDLER.ERROR ("Missing Layout for Appender )
THIS.NAME, NULL, ERRORCODE.MISSING_LAYOUT);
Return;
}
String text = this.Layout.Format (Event);
Throwable thrown = null;
IF (this.Layout.Ignoresthrowable ()) {
Throwableinformation info = event.getthrowableinformation ();
IF (Info! = NULL)
Thrown = info.getthrowable ();
}
Level Level = Event.getlevel ();
INT severity = status.ok;
IF (Level.Toint ()> = level.Error_INT)
Severity = status.error;
Else
IF (Level.Toint ()> = level.warn_int)
Severity = status.warning;
Else
IF (Level.Toint ()> = level.debug_int)
Severity = status.info;
This.pluginlog.log (New Status (Severity,
THIS.PLUGINLOG.GETBUNDLE (). getsymbolicname (),
Level.Toint (), text, thrown);
}
The LoggingPlugin maintains a list of PluginLogManagers. This is required, so that when the plugin is stopped, all hierarchies of the plug-in can be turned off and the Appender and the recorder correctly, as shown in Listing 5.
Listing 5. Loggingplugin class handling log manager
Private arraylist logmanagers = new arraylist (); public void stop (bundleContext context) throws exception {
Synchronized (this.logmanagers) {
Iterator it = this.logmanagers.iterator ();
While (it.hasnext ()) {
PluginLogmanager logmanager = (pluginlogmanager) it.next ();
Logmanager.InternalShutdown ();
}
THIS.LOGMANAGERS.CLEAR ();
}
Super.stop (context);
}
Void AddLogManager (PluginLogmanager Logmanager) {
Synchronized (this.logmanagers) {
IF (Logmanager! = NULL)
This.logmanagers.Add (logmanager);
}
}
Void RemovelageManager (PluginLogmanager Logmanager) {
Synchronized (this.logmanagers) {
IF (Logmanager! = NULL)
This.logmanagers.Remove (LogManager);
}
}
There are a lot of content in inserting a PluginLogManager class. Sometimes you are from the plug-in, especially those that are from Workbench, may cause an exception. These exceptions are usually recorded in the log of Eclipse. This is useful to be inserted from the Dependent Plug-IN into the log frame. When triggering an exception, all logs that Eclipse to be recorded are placed in the log frame, which share configuration files with other logger. This method is very useful because this can be concentrated in one position and can retain a factual historical sample, thereby helping to fix the application's problem.
This can be implemented by implementing org.eclipse.core.runtime.iloglistener and adds it to the ILOG instance of the slave plugin. Basically, you only need to associate it with the Eclipse log. This implementation can then redirect all requests to a recorder created using the name you selected (usually a plug-in identifier). You can then configure the output result with the same configuration file; just specify the name of the recorder, set the filter condition, add the appender. This class is shown in Listing 6:
Listing 6. PluginLoglistener class
Class PluginLoglistener Implements iLoglistener {
PRIVATE ILOG LOG;
PRIVATE LOGGER logger;
Pluginloglistener (Ilog log, logger logger) {
THIS.LOG = Log;
THIS.LOGGER = Logger;
Log.addloglistener (this);
}
Void Dispose () {
IF (this.log! = NULL) {
This.log.Removeloglistener (this);
THIS.LOG = NULL;
THIS.LOGGER = NULL;
}
}
Public void logging (iStatus status, string plugin) {
IF (null == this.logger || null == status) return;
INT severity = status.getseverity ();
Level Level = level.debug;
IF (severity == status.error)
Level = level.error;
Else
IF (severity == status.warning)
Level = level.warn;
Else
IF (severity == status.info)
Level = level.info;
Else
IF (severity == status.cancel)
Level = level.fatal;
Plugin = formattext (plugin);
String statusplugin = formattext (status.getplugin ());
String statusMessage = formattext (status.getMessage ());
StringBuffer message = new stringbuffer ();
IF (Plugin! = null) {
Message.Append (plugin);
Message.Append ("-");
}
IF (StatusPlugin! = NULL &&
(Plugin == Null ||! statusplugin.equals (plugin))) {
Message.Append (statusplugin);
Message.Append ("-");
}
Message.Append (status.getcode ());
IF (statusmessage! = null) {
Message.Append ("-");
Message.append (statusmessage);
}
THIS.LOGGER.LOG (Level, Message.toString (), status.getexception ());
}
Static private string formattext (string text) {
IF (Text! = null) {
TEXT = text.trim ();
IF (Text.length () == 0) Return Null;
}
Return TEXT;
}
}
The entire framework is implemented in a plugin item com.tools.logging. In order to show how it works, I created two plugins:
Helloplugin is built from a project template that displays a message dialog that displays "Hello, Eclipse World". TestPlugINLog is added as a slave plugin with Helloplugin, so it can be hooked in the same log level. It has a method DummyCall (), you can add a fake message using the Eclipse API, and then it will be redirected to the log of Helloplugin.
The slave type of other plugins has been set, such as org.eclipse.ui or org.eclipse.core.Runtime.
In order to display the power of the Logger.Properties configuration file, I am very careful when you create the file. As you can see in Listing 7, we define two appender: Appender A1 is a PlugInfileAppender class that is assigned to the root logger. Other recorders are inherited from this root recorder, and this appender will be used. As a result, all logs, including logs from the TestPluginLog plug-in, are written to a file located location in the plugin. Listing 7. Logger.Properties file in the Helloplugin project
Log4j.rootcategory =, A1
# A1 is set to be be a pluginfileappender
Log4j.Appender.a1 = com.tools.logging.pluginfileappender
Log4j.Appender.a1.file = helloplugin.log
Log4j.Appender.a1.Layout = Org.apache.log4j.patternlayout
Log4j.Appender.a1.Layout.conversionPattern =% P% T% C -% M% N
# A2 is set to be a platinlogappender
Log4j.Appender.a2 = com.tools.logging.pluginlogappender
Log4j.Appender.a2.Layout = Org.apache.log4j.patternlayout
Log4j.Appender.a2.Layout.conversionPattern =% P% T% C -% M% N
# Add appender a2 to helloplugin level only
Log4j.logger.helloplugin =, A2
Another appender is A2, which is a PlugINLogaPpender class that can only be added to the logger "Helloplugin", so TestPlugINLog does not use it. Otherwise, "TestPluginLog" in the "Error View" window will have two items: a from Eclipse, and another from com.tools.logging. You can do an experiment yourself, then you will understand what I mean. Simply add A2 to log4j.rootcategory and delete the line where log4j.logger.Helloplugin is located.
Listing 8 shows the contents of $ {Workspace} /. Metadata / .plugins / helloplugin / helloplugin.log after clicking "Sample Menu" and displays the message box. Note How the TestPluginLog Eclipse log writes in the last line. You can keep the sequence of log events by writing your own log and the Eclipse plugin log into an output file.
Listing 8. Helloplugin.log
Info main helloplugin.Actions.sampleAction - Starting Construction.
Info main helloplugin.Actions.sample - ending constructor.
Warn main helloplugin.Actions.sampleAction - init
WARN MAIN Helloplugin.Anactions.SAMpleAction - Run Method
TestPluginLog - 0 - Logging Using The Eclipse API. Ending brings show two ways to improve the Eclipse log feature. One way is to use com.Tools.logging in the plugin so that all useful features in log4j; if you prefer, it will be part of the Eclipse log frame. Another method is associated with a plug-in, which does not understand log4j, but instantly uses the Eclipse log API, or it can be configured.
In fact, you don't need to use com.tools.logging. Now, you can expand sample code and add it as a separate JAR file to your own plugin. Of course, don't forget the Log4j's JAR file.
The plugin is created using new OSGi. All code is developed using Eclipse 3.0 Release Candidate 1, Sun Java 2 SDK 1.4.2 and Log4J 1.2.8, and testing in these environments. In the code that can be downloaded, the log4j-1.2.8.jar file is not included. If you want to download these code, you should get this JAR file from the Log4j of Apache, and include the file in the com.tools.logging project and com.tools.logging_1.0.0 plugin directory.
Reference
You can see this article in our website on our world. On the Eclipse.org site, you can find the document, source code, and the latest Eclipse version of the plugin. At Sun Java 2 SDK 1.4.2, you can find Java runtime documentation about java.util.logging. You can find all the information about log4j you need on Apache's log4j logging service site. In the World Source area of DeveloperWorks, you can find more articles for Eclipse users. Please also see the latest Eclipse technology download in AlphaWorks. Buy a discount book on the Open Source topic in the Open Source area of the developer Bookstore. Here you can find a few books about Eclipse. Using the developerWorks tool package to develop and test your application: You can get IBM WebSphere® software, such as Eclipse-based WebSphere Studio Application Developer for Linux and WebSphere Studio Application Developer for Windows, and DB2 ®, Lotus®, Rational® and Tivoli®, there is a 12-month license that uses these software, and all cost is lower than your imagination.
About the author Manoel Marques is a software developer and technical consultant. He has worked for 15 years in these areas; during this period, he has worked in Brazil and the United States. Many projects and research work. He graduated from the Computer Science of Pontificia Universidade Catolica, Brazil, and obtained a master's degree in Science. You can contact Manoel via manoel@themsslink.com.
Page
download
Description file type file size Download way loggingplugins.zipzip16 kbftp loggingsourcecode.zipzip31 KBFTP