[[logger]]

== jPOS' Logger

Yet another Logger subsystem?

You may wonder why we've chosen to develop our own Logger subsystem. The
answer is very simple: when we wrote it, there were no other suitable logger
subsystems available. Log4j was just a tiny library hosted in IBM alphaWorks. 

You may wonder why we don't deprecate it now that there are other options
available. The main difference between our logger sub-system and other logger
sub-systems out there is that we deal with *live objects*. A LogEvent holds
live objects that can be handled by the LogListeners, for example to protect
sensitive information (PCI requirement) or to act on special conditions (i.e.
e-mailing an Operator on an Exception without having to parse the serialized
message). 

[NOTE]
======

While other logger subsystems are mostly "line oriented", jPOS' is mostly
"transaction oriented". A jPOS LogEvent is likely to carry information for the
whole transaction making it very suitable for audit and debugging purposes.

======

[TIP]
=====
In order to avoid the initial desire to get rid of the jPOS Logger and
use your the logger you're used to use, you may want to consider jPOS'
as an *Event Logger*, or *Audit Log*. We don't use it to add debug or
trace statements in applications, we use it to log business related data.

You can still use your preferred logger subsystem as part of your business
logic.
=====

jPOS's logger subsystem is very easy to extend, so one can easily plug in other
logger engines (such as Log4j, commons logging or the new JDK's 1.4 logging
stuff), but that has little use. One of the benefit of our logger is the
fact that it produce easy to read (very lightweight) and easy to parse
XML output. The +LogChannel+ for example can read a jPOS log file and parse
ISO-8583 messages from it. If you plug another layer of logging on top of it,
the output is likely to add per-line timestamps that will render the file
difficult to parse.

Our logger is implemented by the following main classes: 


.Logger's main classes
[cols="2,6", options="header"]
|===============
|Class|Description
|Logger|Main logger class
|LogListener|Listens to log events
|LogSource|A log event producer has to implement LogSource
|LogEvent|The Log Event
|===============

The +Logger+ class has the following important methods: 

[source,java]
----

   public class Logger {
      public static void log (LogEvent ev);
      ...
      public void addListener (LogListener l);
      public void removeListener (LogListener l);
      public boolean hasListeners();
      ...
      ...
   }
  
----

+LogSource+ looks like this: 

[source,java]
----

   public interface LogSource {
      public void setLogger (Logger logger, String realm);
      public String getRealm ();
      public Logger getLogger ();
   }
  
----

And +LogEvent+:

[source,java]
----

    public class LogEvent {
       public LogEvent (LogSource source, String tag);
       ...
       ...
       public void addMessage (Object msg);
       ...
    }
  
----

(please take a look at 
link:http://jpos.org/doc/javadoc/org/jpos/util/LogEvent.html[jPOS's javadoc] 
or source code for a full description) 

Here is a simple way to create a Logger: 

[source,java]
----

   Logger logger = new Logger();
   logger.addListener (new SimpleLogListener (System.out));
  
----

Now you can easily attach that logger to any jPOS component implementing
LogSource such as channels, packagers, multiplexers, etc. You can easily call: 

[source,java]
----

   component.setLogger (logger, "some-component-description");
  
----

You can use jPOS's logger subsystem to log events of your own. In those cases,
you have to either implement LogSource or extend or use the the +org.jpos.util.SimpleLogSource+ 
class or better yet, use the newer +org.jpos.util.Log+ class.

Then you can write code like this: 

[source,java]
----

   LogEvent evt = new LogEvent (yourLogSource, "my-event");
   evt.addMessage ("A String message");
   evt.addMessage (anyLoggeableObject); 
   Logger.log (evt);
  
----


The +Loggeable+ interface is a very simple way of letting an object render itself: 

[source,java]
----

    public interface Loggeable {
       public void dump (PrintStream p, String indent);
    }
  
----

Most of jPOS's components already implement the +Loggeable+ interface, but you
can easily  wrap any given object with a Loggeable class that holds the former
object as its payload, e.g.: 

[source,java]
----
package net.swini.util;

import java.io.PrintStream;
import org.jpos.util.Loggeable;

public abstract class LoggeableBase implements Loggeable {
    protected String toXML (String tag, String value, String indent) {
        StringBuffer sb = new StringBuffer (indent);
        sb.append ('<');
        sb.append (tag);
        sb.append ('>');
        sb.append (value);
        sb.append ("</");
        sb.append (tag);
        sb.append ('>');
        return sb.toString ();
    }
    public abstract void dump (PrintStream p, String indent);
}

package net.swini.util;

import java.io.PrintStream;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.entry.ServiceInfo;

public class LoggeableServiceItem extends LoggeableBase {
    String tag;
    ServiceItem item;
    public LoggeableServiceItem (String tag, ServiceItem item) {
        super();
        this.tag  = tag;
        this.item = item;
    }
    public void dump (PrintStream p, String indent) {
        String inner = indent + "   ";
        p.println (indent + "<" + tag + ">");

        if (item.service != null) {
            p.println (toXML ("class", item.service.getClass().getName(), inner));
        } else {
            p.println (inner + "null item.service - (check http server)");
        }
        p.println (toXML ("id", item.serviceID.toString(), inner));

        for (int i=0 ; i<item.attributeSets.length ; i++) {
            if (item.attributeSets[i] instanceof ServiceInfo) {
                ServiceInfo info = (ServiceInfo) item.attributeSets[i];
                p.println (toXML ("name", info.name, inner));
                p.println (toXML ("manufacturer", info.manufacturer, inner));
                p.println (toXML ("vendor", info.vendor, inner));
                p.println (toXML ("version", info.version, inner));
                p.println (toXML ("model", info.model, inner));
                p.println (toXML ("serial", info.serialNumber, inner));
            }
            else {
                p.println (inner + "<attr>");
                p.println (inner + "  "+item.attributeSets[i].toString());
                p.println (inner + "</attr>");
            }
        }
        p.println (indent + "</" + tag + ">");
    }
}
----

There's a general purpose Loggeable class called +SimpleMsg+ which has an
overloaded constructor for several commonly used Java types. You can easily add
a +SimpleMsg+ to your log stream with code like this: 

[source,java]
----

    ...
    ...
    evt.addMessage (new SimpleMsg ("demo", "boolean", true));
    evt.addMessage (new SimpleMsg ("demo", "time", System.currentTimeMillis()));
    evt.addMessage (new SimpleMsg ("demo", "dump", "TEST".getBytes()));
    ...
    ...
  
----

jPOS comes with several +LogListener+ implementations and it's very easy to write your own. 
The ready available ones include: 

.LogListener
[cols="2,4", options="header"]
|===============
|Class|Description
|SimpleLogListener|Dumps log events to a PrintStream (such as System.out)
|RotateLogListener|Automatically rotate logs based on file size and time window
|DailyLogListener|Automatically rotate logs daily. Has the ability to compress old log files
|OperatorLogListener|Applies some filtering and e-mails log-events to an operator, not longer included in jPOS core, it's part of the jPOS-EE mail module
|ProtectedLogListener|Protect sensitive data from ISOMsgs in LogEvents for PCI compliance
|FSDProtectedLogListener| Same as `ProtectedLogListeer` for `FSDMsg` and `FSDISOMsg` instances.
|SysLogListener|Forward log events to the operating system syslog.
|RealmLogFilter|Filter log events by their realm. Enabled or disabled realms can be defined.
|===============


[TIP]
=====

In the jPOS-EE code base you can find some additional logger 
implementations such as IRCLogListener that forwards LogEVents
to an irc channel. In addition, there's a LogBack adaptor that
let us capture other loggers output (i.e. log4j, commons-logging,
etc.) into jPOS' log stream. This allows you to use your preferred
logger API in your code while getting the output in a centralized
jPOS file.

=====

LogListeners are called synchronously, so one listener has the chance to modify a given 
LogEvent; for example, +ProtectedLogListener+ analyzes  received +LogEvents+ and *protects* 
important information (such as track-2 data). 

