This document describes how to use the Log Reference Utility for logging in OSGi bundles.
Using a Configurable Log
The com.prosyst.util.ref package contains an extension to the Log class, called ManagedLog, which allows introducing and configuring levels of log information manageable by means of OSGi-compliant configuration dictionaries.
The ManagedLog identifies log levels by using integers and associates each level with the ID of a specific boolean configuration property. Based on the current value of the corresponding configuration property, a ManagedLog can indicate if a log level is switched on or off.
The ManagedLog implements org.osgi.service.cm.ManagedService, which is registered as a service with a custom PID in the OSGi framework when instantiating the log. In addition, you can attach a parent ManagedService to a ManagedLog – it may provide some non-log properties. The properties of the parent ManagedService will be updated along with the properties of the log itself.
The log management mechanism implemented in ManagedLog is founded on two log levels – DEBUG_ALL and PRINT_ON_CONSOLE. Other, custom, levels are handled only if both DEBUG_ALL and PRINT_ON_CONSOLE are enabled.
A custom log level should be a number between 2 and 32. Levels 0 and 1 are reserved for the predefined ManagedLog levels DEBUG_ALL and PRINT_ON_CONSOLE.
Once created, a ManagedLog can be used for the operation described above – writing messages to the Log Service and signaling fault situations.
Creating Configuration Metadata
To describe our implementation of the OSGi Configuration Admin the properties that the configuration dictionary of a ManagedLog contains, you should provide a metadata XML. Besides the properties representing custom log levels, this XML should include the properties for debug and print-on-console – they have IDs respectively DEBUG_ALL and PRINT_ON_CONSOLE.
Creating a ManagedLog Instance
To create a ManagedLog object for traditional logs in text format, call the constructor ManagedLog (BundleContext bc, HashIntObjNS mapping, String pid, ManagedService parentMS).
The ManagedLog specific constructor parameters are as follows:
Checking if a Log Level Is On
To check if the general log and print levels are enabled, use respectively the getLogLevel and getPrintLevel methods.
To check if a specific custom log level is on in the configuration dictionary, use the getLevel(int) method.
ManagedLog Example
Following is a simple example illustrating the usage of the ManagedLog utility. Basically, the example creates a ManagedLog, configurable under the my.log.pid PID. The ManagedLog has log levels ManagedLog.DEBUG_ALL, ManagedLog.PRINT_ON_CONSOLE and MY_PROP. The last level is a custom, defined for illustration. In addition, the log is assigned a simple parent ManagedService with a String property "property1" and an int[] property "property2".
Then, each 10 seconds the example checks the status of the defined logs levels and if all of them are on, prints an information message to the system output.
The example has a common configuration metadata XML, describing the properties of the parent ManagedService and of the ManagedLog.
You can turn on and off the example's log levels by using the Web Admin Console or the config console commands.
Metadata XML
Listing 1. Writing a configuration metadata XML for a ManagedLog
<metatype-provider>
<objectclass>
<locale>en</locale>
<name>My Log Debug Configuration</name>
<id>my.log.pid</id>
<description>Configuration illustrating logging of messages and
errors.</description>
<attribute modifier="req">
<name>PRINT ON CONSOLE</name>
<id>PRINT_ON_CONSOLE</id>
<description/> <type>&boolean;</type>
<cardinality>0</cardinality>
<value>
<scalar>false</scalar>
</value>
</attribute>
<attribute modifier="req">
<name>MY DEBUG PROPERTY</name>
<id>MY_PROPERTY</id>
<description/> <type>&boolean;</type>
<cardinality>0</cardinality>
<value>
<scalar>false</scalar>
</value>
</attribute>
<attribute modifier="req">
<name>DEBUG ALL</name>
<id>DEBUG_ALL</id>
<description/> <type>&boolean;</type>
<cardinality>0</cardinality>
<value>
<scalar>false</scalar>
</value>
</attribute>
<attribute modifier="req">
<name>First Property</name>
<id>property1</id>
<description/>
<type>&string;</type>
<cardinality>0</cardinality>
<value>
<scalar>"Default Value"</scalar>
</value>
</attribute>
<attribute modifier="req">
<name>Second Property</name>
<id>property2</id>
<description/>
<type>∫</type>
<cardinality>5</cardinality>
<value>
<array>
<scalar>1</scalar>
<scalar>2</scalar>
<scalar>3</scalar>
<scalar>4</scalar>
<scalar>5</scalar>
</array>
</value>
</attribute>
</objectclass>
</metatype-provider>
The above XML file, let us name it log_config.xml, should be included in a bundle JAR and declared in a Config manifest header under the my.log.pid PID:
Config: xml=log_config.xml; pid=my.log.pid; name=My Log Debug Configuration
Parent ManagedService
The following ManagedService simply prints to the system output in the updated method the values of two properties, "property1" and "property2".
Listing 2. Implementing a simple ManagedService.
import java.util.Dictionary;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
public class ParentManagedService implements ManagedService {
static final String PROP1 = "property1";
static final String PROP2 = "property2";
// Inherited from ManagedService.
// Simply prints the set values of the configuration properties.
public void updated(Dictionary properties) throws ConfigurationException {
if (properties != null) {
String prop1 = (String)properties.get(PROP1);
System.out.println("property 1 " + prop1);
int[] prop2 = (int[])properties.get(PROP2);
for (int i = 0; i < prop2.length; i++) {
System.out.println("property 2, element " + i + " " + prop2[i]);
}
}
}
}
ManagedLog User
The following class, ManagedLogTest, represents a bundle activator which starts a job, CheckPropertiesJob, for periodic check of the current log status. The job implements the Runnable interface and is executed in a thread supplied by the Thread Pool Manager.
Listing 3. Creating and destroying a ManagedLog.
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import com.prosyst.util.hash.HashIntObjNS;
import com.prosyst.util.ref.ManagedLog;
import com.prosyst.util.threadpool.ThreadPoolManager;
public class ManagedLogTest implements BundleActivator {
static final String MY_PROPERTY_ID = "MY_PROPERTY";
static final int MY_PROP = 5;
static final String MY_LOG_PID = "my.log.pid";
ManagedLog log = null;
BundleContext bc = null;
ServiceReference thPoolRef = null;
ThreadPoolManager thPool = null;
CheckPropertiesJob job = null;
ParentManagedService parentMS = null;
// Inherited from BundleActivator
public void start(BundleContext bc) throws Exception {
this.bc = bc;
// Defining the log levels
HashIntObjNS mapping = new HashIntObjNS();
mapping.put(ManagedLog.DEBUG_ALL, ManagedLog.DEBUG_ALL_PROP);
mapping.put(ManagedLog.PRINT_ON_CONSOLE, ManagedLog.PRINT_ON_CONSOLE_PROP);
mapping.put(MY_PROP, MY_PROPERTY_ID);
// Creating the parent ManagedService
parentMS = new ParentManagedService();
// Creating the ManagedLog
log = new ManagedLog(bc, mapping, MY_LOG_PID, parentMS);
// Getting the Thread Pool Manager service
getThreadPool();
// Executing the job for periodic checking of log levels
job = new CheckPropertiesJob(log);
thPool.execute(job, "My Checking Properties Job");
}
public void stop(BundleContext bc) throws Exception {
this.bc = bc;
parentMS = null;
if (job != null) {
job.stopJob();
}
ungetThreadPool();
if (log != null) {
log.close();
log = null;
}
}
// Gets the Thread Pool Manager service from the framework
private void getThreadPool() {
thPoolRef = bc.getServiceReference(ThreadPoolManager.class.getName());
if (thPoolRef != null) {
thPool = (ThreadPoolManager) bc.getService(thPoolRef);
}
}
// Releases the Thread Pool Manager service
private void ungetThreadPool() {
if (thPoolRef != null) {
bc.ungetService(thPoolRef);
thPool = null;
}
}
}
Listing 4. Using a ManagedLog.
import com.prosyst.util.ref.ManagedLog;
public class CheckPropertiesJob implements Runnable {
private Object synch = null;
ManagedLog log = null;
private boolean running = false;
public CheckPropertiesJob(ManagedLog log) {
this.log = log;
synch = new Object();
running = true;
}
public void run() {
while (running) {
synchronized (synch) {
try {
// Waiting 10 seconds
synch.wait(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Checking the current status of the log levels
checkProperties();
}
}
// Checks if MY_PROP is on along with DEBUG_ALL and PRINT_ON_CONSOLE.
private void checkProperties() {
if (log.getLevel(ManagedLogTest.MY_PROP)) {
log.info("All properties are on!");
}
}
void stopJob() {
synchronized (synch) {
running = false;
synch.notify();
}
}
}
Turning Debug Option On
To see the debug messages (i.e. written with the debug method), log utility's debug option should be turned on. There are two ways to turn on the debug option:
It is a good idea that you introduce a "debug" boolean flag for your application so that debug messages are produced only when needed.
Listing 5. Using a flag to generate debug messages.
import com.prosyst.util.ref.Log;
. . .
Log log = null;
// Instantiating the Log class
. . .
// Introducing the debug flag
private static boolean debug = Boolean.getBoolean("my.bundle.debug");
if(debug) {
log.setDebug(true);
}
. . .
Otherwise, the log utility will not forward debug logs to the OSGi Log Service.
Displaying Logs in the Framework Console
The log reference utility allows viewing log messages for the OSGi Log Service in the framework text console. To turn this option on, the utility should be set to print messages in the system output through:
Disposing the Log Utility
If you no longer need the utility, you should call its close method to dispose engaged resources.
Indicating Faults
Bundles can use the fault method of the log reference utility to indicate failures in their performance.
The fault (int action, String str, Throwable exc) method has signature similar to the signatures of the methods for writing logs to the Log Service. The method indicates a fault, represented by an action (log, restart the framework, restart the OS - all are available as fields of the com.prosyst.mbs.framework.fm.FaultManager interface) to execute to restore the system, by a message describing the fault, and by an exception exc causing the fault. When called, besides contacting the Fault Manager this method logs an error in the OSGi Log Service.
Triggering and Logging Measurements
The begin() and end() methods are deprecated and instead you should use the Benchmark Utility.
To perform the desired measurement, start the framework with measurements.
You can use the trigger(String module) method of the Log class to trigger measurements for the framework about memory consumption, threads count, etc., depending on the values of the framework measurements system properties and on the chosen Framework Measurement implementation.
To collect the measurement data, you must use the public final void begin(String event) and public final long end(String event) methods designating the beginning and ending of an event being measured.
To log the measurement data, you must use the public void printMeasurements() method printing the collected event measurements to the specified writer. This method should be typically called at the end of BundleActivator.start() and BundleActivator.stop() methods. The result prints out as a DEBUG message.
When printing the events, for each event the following data will be printed:
If clean-up of the collected profiling event measurements is needed, call the resetMeasurements() method.
The measurements, can be enabled globally (with measurements properties) or per-bundle (<BundleSymbolic-Name>.measurements). To enable event measurements data for your bundle only, set the system property <BundleSymbolic-Name>.measurements to true.
The following example illustrates using the measurement features of the log utility, where "gc" stands for garbage collector.
Listing 6. Using measurements features.
import org.osgi.framework.*;
import com.prosyst.util.ref.Log;
public class TestActivator implements BundleActivator {
Log log;
void myMeasurementMethod() {
log.begin("total");
log.begin("gc");
System.gc();
System.gc();
log.end(gc);
for(int i=0; i<10000; i++){
log.begin("loop");
Math.sin(Math.pow(Math.E, Math.log(i)));
log.end("loop");
}
log.end("total");
log.printMeasurements();
}
}
Listing 7. Output of the measurement example.
total: count = 1, total = 31 (ms), average = 31.0 (ms)
loop: count = 10000, total = 15 (ms), average = 0.0010 (ms)
gc: count = 1, total = 16 (ms), average = 16.0 (ms)
Writing Logs in the Log Service
The log utility, whose class is com.prosyst.util.ref.Log, forwards messages from a bundle to the OSGi Log Service (delivered by the Log Bundle) and to the system output. In addition, the utility listens for events concerned with the registration and unregistration of the Log Service, saving this operation for bundles.
The behavior of the log messages and the output are controlled by set/getLogLevel and set/getPrintLevel methods:
To create a Log instance, call one of the following constructors:
The parameters of the Log constructors are as follows:
The log utility's system properties introduced in the earlier version of log utility are still supported for backward compatibility but have to be considered as deprecated. Their values are mapped to the current log utility version in the following way:
Creating Logs
For writing logs strictly compliant with the OSGi Log Service Specification, the Log class defines separate methods for each log level:
For reasons of optimized performance, you should explicitly turn on processing of debug information as described in the Turning Debug Option On section.
Checking the Log Level
To check what is the level of log messages when printed in the system output, use the getPrintLevel() method of the log reference utility.
To check what is the level of the log messages sent to the Log Service, use the getLogLevel() method.
Log Example
The following example illustrates the usage of the log reference utility. It writes a message with an info log level to the system output.
Listing 8. Using the log utility
import org.osgi.framework.*;
import com.prosyst.util.ref.Log;
public class TestActivator implements BundleActivator {
Log log;
public void start(BundleContext bc) {
// Create the log instance, the log and print levels are automatically
// initialized with the values of the system properties:
// <BundleSymbolic-Name>.logLevel & <BundleSymbolic-Name>.printLevel.
log = new Log(bc);
// Optionally, uncomment the line below to manually set the log level
// to DEBUG - this enables ALL log levels.
// log.setLogLevel(LOG.DEBUG);
// Writing a simple info message
log.info("Everything is all right!");
}
public void stop(BundleContext bc){
// Closing the log utility to release the obtained reference to the
// LogService.
log.close();
}
}
System Properties
The system properties, that can be set for the Log Reference Utility are described in the Setup Guide.