Adventures in ADF Logging - Part 1

I see a lot of ADF code from both internal and external users of the framework and one thing that strikes me is how underused the ADFLogger is. There are a fair few blog articles in the community about the ADFLogger already, but they mostly repeat the basics and I wanted to go a little deeper here. 

The ADFLogger is a logging mechanism which is built into the ADF framework. It's basically  a thin wrapper around the java.util.Logging APIs with a few convenience methods thrown in and, most importantly some specific features integrated  into both JDeveloper and Enterprise Manager. All in all it's preferable to use this built-in logger for these reasons, plus it can help you avoid the kind of class-loading issues if you picked up some random version of something like Log4J.

Using the ADF Logger 

At it's most basic you create and use a one of these loggers like this:

First define the logger itself - usually a static variable in a particular class - for example one of your managed beans:

    private static ADFLogger _logger = 
            ADFLogger.createADFLogger(MainPageBackingBean.class); 

You'll notice here that the argument to the createLogger() function is the class. It can also be simply an identifying name or even a Java Package (check out the JavaDoc to learn more). By using a class reference here we are able to refine the logging output to focus very specifically on what's happening in this class. There is nothing wrong, however, in say defining a package level logger if you don't need that level of granularity / control. Indeed to probably want to define a at least a parent logger at the top level of the hierarchy of  loggers within your namespace (package structure) so that you can simply define a resource bundle for all the loggers to share. 

    private static ADFLogger _logger = 
                 ADFLogger.createADFLogger(
                              Package.getPackage("oracle.demo.whippet"),
                              "oracle.demo.whippet.LoggingResBundle"); 

This resource bundle will then* be inherited by any logger in the same hierarchy. Note also that the createADFLogger()  function will create the logger instance for you, or, in this kind of scenario return one that already exists.

*OK well actually no. That should happen but in my testing it's actually not working correctly in Patchset 4. So for now if you want to associate a resource bundle with the logger do so at the level of the logger you are using to log rather than some parent.

Next, throughout your code you can sprinkle logging statements at various levels which are (I hope) fairly self explanitory, for example in this constructor:

    public MainPageBackingBean() {
        super();
        _logger.info("Creating a new instance");
        if (BindingContext.getCurrent() == null) {
            _logger.warning("Injected Data-Binding Reference is null");
        } else {
            try {
                doSomethingComplex();
            } catch
            weirdAppException waex;
            {
                _logger.severe("Unexpected exception doing complex thing",
                               waex);
            }
        }
    }

As you can see we generally just pass a String message to the logger, although in the error case we can pass a throwable exception as well.  As I alluded to earlier you can also grab your Strings from a resource bundle, rather than hardcoding them. This probably makes sense for error messages but may be overkill for programmers eyes only messages. To grab the resource bundle you simply call the getFormattedMessage() function on the logger, or you can also get hold of the resource bundle that it references using getResourceBundle().

    _logger.severe(_logger.getFormattedMessage("errors.db_connection")); 

Where the resource  bundle contains something like:

   errors.db_connection=Unable to establish connection with database!

You can pass parameters to inject into the resource string as well 

The final thing I wanted to mention in this posting was the use of guard conditions. Although logging calls themselves are cheap if logging is not enabled, you may actually need to do quite a lot of work to prepare the stuff you want to log. For example, I have a standardized method that I use as a Task Flow Initializer to dump out the contents of the PageFlowScope for that taskflow.  That's a pretty expensive operation so you want to bypass that whole thing if you know that every single log message within it will be ignored.

Therefore you can use the  isLoggable() method to wrap the whole thing. Here's the example:

    public void diagnosticInitializer() {

        //Only do the work if we need to
        if (_logger.isLoggable(Level.INFO)) {
            AdfFacesContext actx = AdfFacesContext.getCurrentInstance();
            FacesContext fctx = FacesContext.getCurrentInstance();
            //Gather key information to dump
            String windowName =  actx.getWindowIdProvider().getCurrentWindowId(fctx);
            String viewPort = ControllerState.getInstance().getCurrentViewPort().getClientId();
            Map pageflowScopeMap = actx.getPageFlowScope();

            _logger.info("TaskFlow Diagnostics for " + viewPort);

            if (!pageflowScopeMap.isEmpty()) {
                Iterator mapIter = pageflowScopeMap.entrySet().iterator();
                while (mapIter.hasNext()) {
                    Map.Entry entry = (Map.Entry)mapIter.next();
                    String varKey = entry.getKey().toString();
                    Object varValue = entry.getValue();
                    String varClass = "n/a";
                    if (varValue != null){
                        varClass = varValue.getClass().getName();
                    }
                    StringBuilder bldr = new StringBuilder();
                    Formatter formatter = new Formatter(bldr, Locale.US);
                    formatter.format("Key:\"%s\" Value:\"%s\" Type:[%s]", varKey, varValue, varClass);                    
                    _logger.info(bldr.toString());
                }
            } else {
                _logger.info("No PageFlowScope associated with this TaskFlow instance");
            }
        }
    }

Thats all for now, next time we'll look at how you can actually view these log messages. 

Comments:

So, as I understand this, unlike log4j, definitions of a logger and changes to the logging level reported need to be done within the code? There is no properties file to modify the switch between a coarser or finer grain logging output; or to define filters for the output (e.g., com.oracle v. com.oracle.a.deeper.package)?

Posted by JW on May 31, 2011 at 08:11 AM BST #

In Part 3 I'll show you how you can actually switch on levels of logging for different classes or packages. You have the same functionality as you'll be familiar with when using java.util.Logging. All we've covered so fare is how to instrument your code with logging at various levels. In part 3 we'll show how to reveal each of those levels.

Posted by Duncan on June 01, 2011 at 11:55 AM BST #

Post a Comment:
Comments are closed for this entry.
About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today