7.1. The Log Service

The main purpose of the Log service is to record events and errors, and to retrieve them afterward. Unlike desktop systems, residential gateways usually do not have conventional input/output devices such as keyboard and display. As a result, when an error occurs in the framework, we often cannot hope to display an error message to the user and get his intervention to correct the erroneous condition.

The Log service is a good place to save error and other messages so that remote administrators can analyze them either manually or through the use of a tool. The Log bundle registers two services: LogService for writing logs and LogReaderService for retrieving them.

7.1.1. Using LogService to Write Logs

To use LogService, your bundle must do the following:

1.
In your manifest, declare the LogService package in the Import-Package header:

Import-Package: org.osgi.service.log

2.
In your bundle or service (usually in the activator), get an instance of LogService:

import org.osgi.service.log.LogService;
...
ServiceReference ref = bundleContext.getServiceReference(
   "org.osgi.service.log.LogService");
LogService logService = (LogService) bundleContext.getService(ref);

The LogService interface provides APIs with which you can write log entries. How the log entries are processed and saved is up to the implementation of LogService. They may be saved on the disk locally if the gateway is so equipped, or they may be sent to a remote server over the network.

LogService also defines severity levels. When you call one of the log methods to write an entry to the log, you need to specify a level that indicates how important the entry is in your call. The predefined levels are

  • LogService.LOG_DEBUG: a debugging message

  • LogService.LOG_INFO: an informational message

  • LogService.LOG_WARNING: a warning message

  • LogService.LOG_ERROR: an error message

The level can be used by the LogService implementation to filter out log requests below a certain cutoff. For example, the Log service may want only to accept any log requests more severe than LOG_DEBUG during real deployment, but may lower the threshold to include LOG_DEBUG requests during development and maintenance.

We present some examples of how to perform logging by calling the Log service API. The following code logs a debugging message that reports the user's login/account name.

						logService.log(LogService.LOG_DEBUG,
   "The value of user.name system property is " +
      System.getProperty("user.name"));

The following code logs an error message when an attempt to download data from a URL fails:

try {
   URL url = new URL(destination);
   URLConnection conn = url.openURLConnection();
   ...
} catch (Exception e) {
   logService.log(LogService.LOG_ERROR,
      "Cannot download from URL " + destination, e);
}

The following code logs a warning message if the print queue is crowded, and logs an error when we fail to print through the print service. In this example, we include the service reference to PrintService in printRef as part of the log method call, indicating the service associated with the log entry being produced:

ServiceReference printRef = bundleContext.getServiceReference(
      "com.acme.service.print.PrintService");
PrintService svc = (PrintService)
   bundleContext.getService(printRef);
String printer = "tree-killer";
String source = "file:/home/joe/memo";
try {
   // get status from the printer "tree-killer"
   String status = svc.getStatus(printer);
   if (status != null)
      logService.log(LogService.WARNING,
         "print queue " + printer + " is not empty.");
   svc.print(printer, new URL(source));
} catch (IOException e) {
   logService.log(printRef, LogService.LOG_ERROR,
      "failed to print file " + source, e);
}

7.1.2. Using LogReaderService to Get Logs

You can use LogReaderService to read entries that have been logged or to monitor logs as soon as they are written. Because the service is defined in the same package as LogService, you don't need to add anything to the Import-Package manifest header in your bundle. The following code gets LogReaderService:

ServiceReference logReaderRef = bundleContext.getServiceReference(
      "org.osgi.service.log.LogReaderService");
LogReaderService logReader = (LogReaderService)
      bundleContext.getService(logReaderRef);

With LogReaderService, you can achieve the following two tasks:

1.
Get all the log entries that have been created so far. The Log service in the Java Embedded Server product has a size limit on the number of entries being kept; older entries are discarded.

2.
Add a listener so that you can be notified whenever a log entry is written.

7.1.2.1. Getting a Snapshot of the Logs

By calling the getLog method on LogReaderService, you get an enumeration of log entries. A log entry, as defined by the LogEntry interface, contains the information provided to LogService's log call when the entry was created. For example,

Enumeration enum = logReader.getLog();
while (enum.hasMoreElements()) {
   LogEntry entry = (LogEntry) enum.nextElement();
   displayLogEntry(entry);
}

where the method displayLogEntry may be defined as

void displayLogEntry(LogEntry entry) {
   Date timeStamp = new Date(entry.getTime());
   System.out.print(timeStamp + " ");
   switch (entry.getLevel()) {
      case LogService.LOG_DEBUG:
         System.out.print("debug ");
         break;
      case LogService.LOG_INFO:
         System.out.print("info ");
         break;
      case LogService.LOG_WARNING:
         System.out.print("warning ");
         break;
      case LogService.LOG_ERROR:
         System.out.println("error ");
         break;
   }
   Bundle b = entry.getBundle();
   System.out.print(b == null ? "" : b.getLocation());
   ServiceReference ref = entry.getServiceReference();
   if (ref != null)
      System.out.print(" "+ref);
   System.out.println(": " + entry.getMessage());
   Throwable t = entry.getException();
   if (t != null)
      t.printStackTrace();
}

7.1.2.2. Monitoring Logs

You can add an implementation of LogListener so that it will be notified whenever a log entry is created. For example,

LogListener myLogListener = new LogListener() {
   public void logged(LogEntry entry) {
      displayLogEntry(entry);
   }
};
logReader.addLogListener(myLogListener);

You can remove LogListener when you no longer need to monitor logs:

logReader.removeLogListener(myLogListener);

The Log bundle automatically removes the listeners added by a bundle when that bundle goes away (gets stopped or uninstalled). It also removes all instances of LogListener when it itself goes away.

7.1.3. Performing Persistent Logging

The LogService implementation in the Java Embedded Server product keeps the log entries in memory. Oldest entries are overwritten and lost as new entries are added. It is often desirable to commit log records to persistent storage locally to disk or at a remote host, so that administrators can trace the history for purposes of auditing or performance analysis.

In this section we look at an implementation of LogListener that transfers log entries to an HTTP servlet on a remote Web server for safekeeping:

import java.io.*;
import java.net.*;
import java.util.Date;
import org.osgi.framework.*;
import org.osgi.service.log.*;
class HttpLogListener implements LogListener {
   private URL serverURL;     // URL to remote HTTP servlet
   private LogEntry[] entries; // buffer of log entries
   private int index = 0;  // current index in log entry buffer

   public void logged(LogEntry entry) {
      if (index < entries.length) {
         // cache the log entry in the buffer
         entries[index++] = entry;
         return;
      }
      // the log entry buffer is full;
      // post to the remote HTTP servlet
      if (serverURL != null) {
         try {
            HttpURLConnection conn = (HttpURLConnection)
               serverURL.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            DataOutputStream out = new DataOutputStream(os);
            sendLogs(out);
            // discard whatever the Web server returns
            conn.getInputStream().close();
            out.close();
         } catch (IOException e) {
            // log entries will be lost
         }
      }
      // store the new log entry
      index = 0;
      entries[index++] = entry;
   }

   HttpLogListener(URL u, int bufferSize) {
      this.serverURL = u;
      this.entries = new LogEntry[bufferSize];
   }
   private void sendLogs(DataOutputStream out) throws IOException {
      for (int i=0; i<entries.length; i++) {
         String date = new Date(entries[i].getTime()).toString();
         String bundlestr = "";
         Bundle bundle = entries[i].getBundle();
         if (bundle != null)
            bundlestr = bundle.toString();
         String refstr = "";
         ServiceReference ref = entries[i].getServiceReference();
         if (ref != null) refstr = ref.toString();
         out.writeUTF(date);
         out.writeUTF(bundlestr);
         out.writeUTF(refstr);
         out.writeLong(entries[i].getLevel());
         out.writeUTF(entries[i].getMessage());
         Throwable t = entries[i].getException();
         String exception = "";
         if (t != null) {
            StringWriter sw = new StringWriter();
            t.printStackTrace(new PrintWriter(sw));
            exception = sw.toString();
         }
         out.writeUTF(exception);
         out.writeUTF("
");
      }
      out.flush();
   }
}

The listener maintains a simple buffer so that a number of log entries can be posted to the remote server all at once to improve efficiency and to reduce network traffic. The URL to the receiving remote servlet and the maximum buffer size are initialized when the listener is instantiated.

We also define a marshalling protocol so that the various ingredients in a log entry—date, bundle, service reference, level, and exception—are converted into strings before being delivered over the wire. An empty line (' ') is used as the separator between log entries.

Many enhancements can be made to the listener. For instance, we may use the persistent connection offered by the HTTP 1.1 protocol to improve network efficiency. We should check the status of the HTTP response to ensure our posting has indeed succeeded. We may also use a few backup URLs to which to send log entries in case the primary one fails. The servlet code that accepts the log entries is not shown. We leave that as an exercise for you.

It is not difficult to imagine alternative strategies by which the log entries may be saved to a local disk or sent to a remote plain socket end point.

Lastly, the standard services are not untouchables. You are free to reimplement any service interface to suit your own needs. For instance, you could rewrite implementations for LogService and LogReaderService entirely to do persistent logging directly. Instead of using the Log bundle provided by the Java Embedded Server product, you would install and activate your own Log bundle.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.220.16