Restlet Cookbook - Access Log (Recipe 4)

Posted: 11/09/2007 by Serge Ilyn

Coding and configuring access logging using Restlet framework is as easy as everything else with Restlets. However, if you are not familiar with Logging API in Java (used as a default logging mechanism in Restlets), dealing with logging may be confusing. Unfortunately, Restlet documentation is very laconic and provides few insights on how to use access logging. This recipe contains practical recommendations on most of things you need to do to setup Restlet access logging.

Defining the Problem

Actually, if you do nothing, you will get some king of logging "out of the box" - but, most likely, that would not be the logging you want. First, logging messages will be printed on the console only. Second, the output format will look something like

Nov 10, 2007 2:42:59 PM com.noelios.restlet.LogFilter afterHandle
INFO: 2007-11-10 14:42:59 127.0.0.1 ....    .....  ......

with two lines per one request. The second line will be in the W3C Extended Log File Format format (which is OK); however, you probably will not be happy with the first line of the log message (in most cases absolutely useless). Thus, most changes to logging setup will consist of two tasks:

  • How to output to a file, and
  • How to tune up the output format.

General Considerations

Restlet entry point to control access logging is through the org.restlet.Component class (you create a new Component very soon after you start your Restlet application). The Component's getLogService() method will return an instance of the org.restlet.service.LogService class. Restlet LogService class provides access to logging service.

     Component component = new Component();
     LogService logService = component.getLogService();
One of the things you can do with LogService is to disable it:
     logService.setEnabled(false);

By the way, even if it does not relate directly to the topic of this recipe (which is access logging), I would like to mention how to get rid of this type of very annoying messages that appear contrary to your will on the console output:

INFO: Converted target URI: file:///c:/navirest2/html/images/bg/footer.jpg
Jun 16, 2009 9:44:57 AM com.noelios.restlet.local.DirectoryResource getVariants

The API mentioned above: logService.setEnabled(false); or something like component.getContext().getLogger().getLogger("com.naviquan.Naviquan").setL evel(Level.WARNING); will not have any effect on the output. What may help is the following method public Logger getLogger() available in Restlet class (hence, in all its subclasses). Accordingly, you can do this (use any Level value you want)

    Directory directory = new Directory(...);
    directory.getLogger().setLevel(Level.WARNING);

and this vexatious output will disappear.

Now, let's get back to the original topic - access logging. If you want to configure it the way you want, your most important step is to obtain "a link" (for the lack of better terminology) between Restlet LogService and an instance of the java.util.logging.Logger class (that belongs to the Java Logging API). You do this by giving a name to the LogService instance, like this:

     logService.setLoggerName("com.naviquan");

To get an instance of Logger, use the following API:

     Logger logger = Logger.getLogger("com.naviquan"); 

It can be a static member of your application, and you can instantiate it as such:

      private static Logger logger = Logger.getLogger("com.naviquan");

To continue configuration of your custom logging, you need to know that the way to configure many things in Java Logging is to manipulate parameters in the logging.config properties file located by default in the \jre\lib folder. You can change this by setting the java.util.logging.config.file System property. The same can be done programmatically using the following API:

System.setProperty("java.util.logging.config.file", 
"/your/path/logging.config");

It is also possible to ignore at least some of logging.config parameters by setting/modifying them programmatically (this is the reason we do not worry about logging.config file in this recipe).

Outputting to File

Previously, I mentioned that one of the problems you may want to solve is to redirect your logging output to a file (and possibly disabling completely or changing console output). How do we accomplish this? By creating an instance of java.util.logging.FileHandler and adding it to the Logger instance:

     FileHandler fh = new FileHandler("c:/navi%g.log", true);
     logger.addHandler(fileHandler);

To disable console logging, we can use this approach:

     logger.setUseParentHandlers(false);

Note that all three lines of coding above use the Java Logging API. In the FileHandler constructor, we specify the file name and a boolean parameter. If the latter is true, messages are appended to file. Our file name contains a pattern %g - it instructs Java Logging to insert generation number (for log rotation). If you use this pattern, keep in mind that rotation is controlled by two FileHandler parameters: limit and count. The first parameter, limit, sets the max file size that, when reached, will result in creation of a new log output file. The second parameter, count, defines the number of files that can be created before the file name rotates to the first one (created with count of 0 - in our example, navi0.log). Both parameters can be set either using a FileHandler constructor (that's why there are several of them) or can be set in the logging.config file. There are other patterns that can be used in specifying log file names. You can consult Java Logging API javadoc for all the details.

Several comments regarding disabling console output. To understand the respective line in the example above, you need to know that by default the root handler created by Java logging mechanism is an instance of the java.util.logging.ConsoleHandler class. Thus, it's always there, unless you disable it like we did above.

Changing Log Format

Our second problem to deal with was the output formatting. It is controlled by an instance of java.util.logging.Formatter. This is an abstract class from Java Logging API which also offers two subclasses: SimpleFormatter and XMLFormatter. If you are not happy with either one, you need to implement your own Formatter or extend the existing subclasses (depending on what exactly you want). For the purposes of our recipe we will extend SimpleFormatter - all you need to do is to override public String format(LogRecord record) method

public class NaviquanLogFormatter extends SimpleFormatter {
  public String format(LogRecord record) {
    return Naviquan.sdf.format(new Date()) + " - " + record.getMessage() + "\n";
  }
}

where sdf is an instance of java.text.SimpleDateFormat defined in the Naviquan class as:

 
     public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd 'at' 
HH:mm:ss");

Once a Formatter is crafted, you need to let your FileHandler know about it. You can do this in the logging.config file, or use API:

     fileHandler.setFormatter(new NaviquanLogFormatter());

We are almost done with the formatting issue, but one of its pieces is still missing. If you look at my implementation of the NaviquanLogFormatter and, in particular, its format(LogRecord record) method, you will see that it relies on the record.getMessage() call. Accordingly, the result will be formatted following rules enabled for this 'message'. So, what is the 'message' in the java.util.logging.LogRecord class and how to control it?

Make no mistake - this 'message' comes from your application, and the java.util.logging.LogRecord class is nothing more than a utility to perform some (very unexciting) operations (for example, localization) with messages coming from elsewhere. If this is true, the question is - where does actual formatting come from? As I mentioned above, the org.restlet.service.LogService provides some default formatting. But it also has API that allows for specifying any format you want. Here it is:

logService.setLogFormat("{cia} {m} {S} {rp}    AGENT:{cig}   REF:{fp}");

The setLogFormat(String format) method takes as its argument a String that describes the exact log message format using {name} syntax through the internal Restlet mechanism based on org.restlet.util.Template class. In the example above the following names are used:

cia

-

request.clientInfo.address

m

-

request.method

S

-

response.status

rp

-

request.resourceRef, path

cig

-

request.clientInfo.agent

fp

-

request.referrerRef, path

 

The above formatting string will result in the output similar to the following (as one line):

2007.11.10 at 14:02:24 - 127.0.0.1 GET 200 /pages/Money          
AGENT:Mozilla/5.0 (Windows; U; ...... )        REF:/pages/Tech

For the full list of available {name}s, you need to consult Restlet API javadoc for the org.restlet.util.Template class

That's it. The example below summarizes in a concise form everything that has been done.

Complete Example of Setting Restlet Access Logging
public class Naviquan extends Application {
  ...

  private static Logger logger = Logger.getLogger("com.naviquan");
  public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd 'at' 
HH:mm:ss");

  public static void main(String ...args) throws Exception {
    Component component = new Component();

    // start logging
    LogService logService = component.getLogService();
    logService.setEnabled(true);
    logService.setLoggerName("com.naviquan");
    logService.setLogFormat("{cia} {m} {S} {rp}     AGENT:{cig}    REF:{fp}");

    FileHandler fileHandler = new FileHandler("c:/navi%g.log", true);     
    fileHandler.setFormatter(new NaviquanLogFormatter());
    logger.setUseParentHandlers(false); 
    logger.addHandler(fileHandler);
    // end logging
   ...
  } 
...
}
public class NaviquanLogFormatter extends SimpleFormatter {
  public String format(LogRecord record) {
    return Naviquan.sdf.format(new Date()) + " - " + record.getMessage() + 
"\n"; }
}

Recent Blogs

Convergence of Technologies II: Restlets, Highslide Viewer & SlideShowPro for Flash How to use Restlets, Highslide Viewer & Slide Show Pro Flash component to generate slide shows and show them in pop-up iframes 08/14/2009

Convergence of Technologies: Restlets, Highslide Viewer & Google Maps API How to use Restlets, Highslide Viewer & Google Maps API to generate Google maps using links on Web pages and show them in pop-up iframes. 08/13/2009

Ambimorphic, Mobile Configurations for Lamport Clocks After years of technical research into Smalltalk, with close cooperation with MIT Computer Science and Artificial Intelligence Laboratory (CSAIL) and its SCIgen, we have been able to show the construction of link-level acknowledgements. As a result, we propose an analysis of A* search, which we call Volt. 10/02/2008

Restlets and HTTP Sessions (Recipe 9) This recipe discusses some options in available to Web site developers using Restlet API to emulate traditional HttpSession's 07/31/2008

Restlet Cookbook - Delving into handleGet() and handlePost() (Recipe 8) This posting provides related examples that illustrate at least some simple things you may do with several handleX() methods where X may stand for various types of calls. 07/30/2008

Restlet Cookbook - How to enable SSL (Recipe 7) How to enable SSL on Restlet Web sites wihtout knowing too much keys, encryption, SSL and such. 07/29/2008

Restlet Cookbook - Using Filters in Restlet Applications (Recipe 6) This recipe contains code snippets illustrating the use of Filters in Restlet applications 03/09/2008

Restlet Cookbook - How to Create Virtual Hosts in Restlet Applications (Recipe 5) This recipe contains a code snippet for creating virtual hosts 02/24/2008

Popular HTML Escape Codes Here is a table of HTML escape codes for most frequently used symbols. If you use HTML, sooner or later you will find this table useful. 12/29/2007

Restlet Cookbook - Access Log (Recipe 4) This recipe describes access Logging using Restlet framework 11/09/2007

more...