Log Reference Utility

The log reference utility of the ProSyst Util Bundle offers support for creating logs in the two main log systems of mBS - the OSGi Log Service and the Tracer.

Contents:


Writing Logs in the Log Service

The log utility, whose class is com.prosyst.util.ref.Log, forwards message from a bundle to the OSGi Log Service (delivered by the Log Bundle) or 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.

If the Log Service is not registered (i.e. the Log Bundle is not active) and the mbs.util.ref.log.autoPrintOnConsole system property is set to false , depending on its printOnConsole flag the log utility maps provided log messages to the system output (see " Displaying Logs in the Framework Console "). After the Log Service is registered, the utility forwards messages to the service.

To create a Log instance, call one of the following constructors:

The parameters of the Log constructors are as follows:

Creating Logs

For writing logs strictly compliant with the OSGi Log Service Specification, the Log class defines separate methods for each log level:

Where str is the text of the log message and ex is the Java exception being logged.

Checking the Log Level

To check if the logs should be printed in the system output if the Log Service is not registered, use the isAutoPrintOnConsole() method of the log reference utility.

To check if logs with error and warning levels should be printed in the framework console, use the isLogErrorLevel () method.

Log Example

The following simple example illustrates the usage of the log reference utility on top of the Log Service. It writes a message with an info log level to the system output.

  import org.osgi.framework.*;
import com.prosyst.util.ref.Log;
 
public class TestActivator implements BundleActivator {

Log log;
String message = "Everything is all right!";
 
public void start(BundleContext bc) {
log = new Log(bc);
// Turning on the printOnConsole flag
log.setPrintOnConsole(true);
// Writing a simple info message
log.info(message);
}
 
public void stop(BundleContext bc){
// Closing the log utility
log.close();
}
}
Listing 1: Using the log utility.

Writing Logs in the Tracer

It is also possible to use the log reference utility - com.prosyst.util.ref.Log, to send log messages written in the Log Service to a remote GUI application through the Tracer module or have them printed in a user-friendly format in the text console.

The log reference utility can also be used to trigger framework measurements and provide the received log information in a remote GUI application or print it in the text console. For more information about creating mixed logs, refer to the "Triggering and Logging Measurements" section of this document.

In addition, if the mbs.log.usedispatcher system property is true, the log utility takes advantage of the framework's log dispatcher for non-blocking printing of logged information in the text console.

Creating Tracer-Enabled Logs

Writing log message to the Tracer can be done with the following Log methods corresponding to the log levels, defined in the OSGi Log Specification:

The Tracer-enabled methods of the Log class take the following parameters:

Printing Traced Messages in a User-Friendly Manner

Sometimes displaying module and message IDs for a message does not provide a clear way to debug the behavior of an application. Through the setMaps method you can provide a "Tracer Map" holding meaningful string equivalents of used module and message IDs.

The Tracer Map has two parts:

For convenience, a bundle can have a separate class for Tracer Map definition, as shown in the example that follows.

Example of a Tracer-Enabled Log

The following example illustrates the usage of logs on top of Tracer. The example periodically generates debug information by using notifications from the Timer service (Listing 2.1). Generated messages are shown in a user-friendly manner due to a defined Tracer Map (Listing 2.2).

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.prosyst.util.ref.Log;
import com.prosyst.util.timer.Timer; import com.prosyst.util.timer.TimerListener;
public class TimerToTracer implements BundleActivator, TimerListener {
         
  private ServiceReference refTimer = null; 
  private Timer timer = null; 
  private final static String SERVICE_TIMER = Timer.class.getName();
  private static Log log = null;
  protected static final int MODULE_ID = 1;
  protected static final int ODD_SUBMODULE_ID = 11;
  protected static final int EVEN_SUBMODULE_ID = 12;
  private int EVENT_TYPE1 = 1;
  private int EVENT_TYPE2 = 2;
  int timerCounter = 1;
  boolean odd = true;
  public void start(BundleContext bc) throws Exception {
    log = new Log(bc);
    log.setDebug(true);
    log.setPrintOnConsole(true);
    // Defining the Tracer Map
log.setMaps(TracerMap.getMap(), TracerMap.getStarts());
// Debug about initiating registration for timer notification

log.debug(MODULE_ID, 41, "", null, true);
try { refTimer = bc.getServiceReference(SERVICE_TIMER); if (refTimer != null) { timer = (Timer) bc.getService(refTimer); // Registering periodical timers with different timeouts and event types.
timer.addNotifyListener(this, Thread.NORM_PRIORITY, Timer.PERIODICAL_TIMER, (long) 1000, EVENT_TYPE1); timer.addNotifyListener(this, Thread.NORM_PRIORITY, Timer.PERIODICAL_TIMER, (long) 700, EVENT_TYPE2);
     }
} catch (Exception e) { log.error(MODULE_ID, 30, e.getMessage(), e, true); }
// Debug about terminating registration for timer notification
log.debug(MODULE_ID, 42, "", null, true); }
  public void stop(BundleContext bc) throws Exception {
    if (timer != null) {
      timer.removeListener(this, EVENT_TYPE1);
      timer.removeListener(this, EVENT_TYPE2);
    }
    if (refTimer != null) {
      bc.ungetService(refTimer);
    }
    if (log != null) {
      log.close();
    }
  }
  // Method inherited from TimerListener and called by the Timer Service  
  // when the specified time period expires.
public void timer(int event) {
// Iterate 24 times. if (timerCounter < 25) { if (odd) { // Debug about event with odd sequence number
log.debug(ODD_SUBMODULE_ID, 10, Integer.toString(timerCounter), null, true); odd = !odd; } else { // Debug about event with even sequence number
log.debug(EVEN_SUBMODULE_ID, 20, Integer.toString(timerCounter) , null, true); odd = !odd; }
// Debug about type of received time event
log.debug(MODULE_ID, 1, Integer.toString(event), null, true); timerCounter++; } else { timer.removeListener(this, event); } }
}
Listing 2.1: Logging information through the Tracer utility.

import com.prosyst.util.hash.HashIntObjNS;

public class TracerMap { protected static HashIntObjNS getMap() { HashIntObjNS map = new HashIntObjNS(8); map.put(-(TimerToTracer.MODULE_ID), "TimerToTracer"); map.put(-(TimerToTracer.ODD_SUBMODULE_ID), "TimerToTracerODD"); map.put(-(TimerToTracer.EVEN_SUBMODULE_ID), "TimerToTracerEVEN"); map.put(1, "Received message of type"); map.put(10, "Odd timer event"); map.put(20, "Even timer event"); map.put(30, "Error in start method" ); map.put(41, "Registering timers" ); return map; } protected static HashIntObjNS getStarts() { HashIntObjNS starts = new HashIntObjNS(1); starts.put(42, new Integer(41)); return starts; } }
Listing 2.2: Defining a Tracer Map.

Triggering and Logging Measurements

To perform the desired measurement, start the framework with measurements. For more information about launching the framework with measurements, refer to the "Starting the Framework with Measurements" part from the "Starting the Framework" document.

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.

Bundles can use one of the following methods to write logs about measured parameters:

The methods for logging values of measured parameters take the following parameters in addition to those described for the Tracer-enabled methods:

Indicating Faults

Bundles can use the fault methods of the log reference utility to indicate failures in their performance:

Writing Binary Logs in a File

The log reference utility also provides means to write big logs as binary data in files without storing them in the OSGi Log Service so as to avoid service's overload.

As this option of the log reference utility is not related to the OSGi Log Service, there are no predefined log levels. Instead, logs are associated with a base name, which can be used to represent an application-specific type of log messages.

Each base name, i.e. custom log type, is handled by a separate instance of the Log class. To obtain such an instance capable of storing log files, you should use the Log(String baseName, boolean synch, org.osgi.framework.BundleContext bc) constructor. The constructor takes the following parameters:

Next, to write down log information, use one of the trace methods. If the synch flag has been set to true in the Log constructor, then the trace method won't return until the log is saved.

As a result, the Log instance saves logs in a <base_name> file within the directory specified with the mbs.logsDir system property. By default, this directory is called logs and is located in the working directory of the OSGi framework (bin/vms/<vm_name>).

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.

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.

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 be 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.

Note: A custom log level should be a number between 2 and 32. Levels 0 and 1 are reserved for the pre-defined 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 to the Tracer, signaling fault situations and writing binary logs in a file.

Creating Configuration Metadata

To describe to the ProSyst 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

Depending on the purpose it will be used for, call the proper constructor to create a ManagedLog object:

ManagedLog-specific constructor parameters are as follows:

Checking if a Log Level Is On

To check if the general debug (DEBUG_ALL) and print-on-console (PRINT_ON_CONSOLE) levels are enabled, use respectively the getDebug and getPrintOnConsole ManagedLog methods.

To check if a specific custom log level is on in the configuration dictionary, use the getLevel 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.

Tip: You can turn on and off the example's log levels by using the mConsole or the config console commands.

Metadata XML

<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>&int;</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>
Listing 3.1: Writing a configuration metadata XML for a ManagedLog.

The above XML file, let's 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".

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]); } } }
}
Listing 3.2: Implementing a simple ManagedService.

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.

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 3.3.1: Creating and destroying 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();
    }
}
}
Listing 3.3.2: Using a ManagedLog.

Turning Debug Option On

For debug messages (i.e. written with the debug method), log utility's debug option should be turned on through:

Otherwise, the log utility will not forward debug logs to the OSGi Log Service or the Tracer.

Tip: It is a good idea that you introduce a "debug" boolean flag for your application so that debug messages are produced only when needed.

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);
}
. . .
Listing 4: Using a flag to generate debug messages.

Displaying Logs in the Framework Console

The log reference utility allows viewing log messages for the OSGi Log Service and Tracer in the framework text console. To turn this option on, the utility should be set to print messages in the system output through:

Tip: Similarly to enabling the debug option of the log utility (see Listing 3), you can introduce a "print" flag for printing generated logs to the system output.

Disposing the Log Utility

If you no longer need the utility, you should call its close method to dispose engaged resources.

System Properties

The log reference utility has two system properties for configuration, which apply to all applications using the log utility.

System Property Default Value Description
mbs.util.ref.log.autoPrintOnConsole false Enables printing logs in the framework console in case the Log Service is inaccessible.
mbs.util.ref.log.debug false Turns on/off debug log messages created through the log reference utility.
mbs.util.ref.log.printOnConsole false Prints logs, produced through the log reference utility, in the framework console.
mbs.log.errorlevel false Turns on/off and prints all error and debug logs into the framework console regardless of the current debug settings. Setting this property to true is useful during development as it will significantly ease the tracking of problematic situations without the need of reproducing them with debug logging turned on.
mbs.logsDir ./logs Specifies the directory to store files with binary log data.
mbs.log.usedispatcher false Enables non-blocking logging in the text console through the log dispatcher.

References


Socket Factory and Event Interfaces