HowTos Log Management

Aus Salespoint

Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Define a Log

Description: As described in #Understand logging, a Log is the representation of the "log file", which only is a real file, if the specified OutputStream is the recommended FileOutputstream. For a global Log, you only have to define a GlobalOutputStream by calling the static method Log.setGlobalLogOutputStream(OutputStream newOS). For local logging, you have to initialize a new instance of Log, either by using the constructor, the static method Log.createLog(OutputStream newOS) or a LogCreator.

ToDo:

  1. For the initialization find a place at the beginning of your application runtime to make sure, the LogOutputStream exists before any logging starts.
  2. Due to the new FileOutputStream, remember to catch the thrown IOException.
  3. Use the static method setGlobalOutputStream, which will initialize a new Log and a globally reachable OutputStream, enabling logging to it anywhere in your application.

Example Source Code:

1
// within the main method, but may be anywhere before any logging starts
public static void main(String[] args)
{
LogShop logShop = new LogShop();
Shop.setTheShop(logShop);
 
2
// remember to catch the possible IOException of creating a new FileOutputStream
try
{
3
// the static method will initialize a new Log and set the FileOutputStream global
// so it can be used anywhere in the application
Log.setGlobalOutputStream(new FileOutputStream("machine.log", true));
}
catch(IOException ioException)
{
System.err.println("Unable to create log file.");
}
}

Define a new LogEntry

Description: A LogEntry is what is being put into the LogOutputStream when logging an event. The information needed for the Log is provided by the two methods public String toString() and public Date getLogDate(). These are the methods to be redefined in order to suit your event log. By default they return "Object logged: " + getLogDate() and the present system date at the point of logging. In our OpenLogEntry we keep the system date as return of getLogDate, but redefine the toString() method to return the String "Counter opened".

ToDo:

  1. Create a new subclass of LogEntry.
  2. Redefine the toString() method to return a suitable text for the event.
  3. Use the LogEntry to be returned by the method getLogData() of the implementation of Loggable.

Example Source Code:

1
public class OpenLogEntry extends LogEntry
{
 
2
public String toString()
{
return "Counter opened";
}
}

Define a new LogEntryFilter

Description: A LogEntryFilter is used either to enable logging of certain LogEntries only or to display / process certain LogEntries of a Log (which is a representation of the "log file"). To "filter" the LogEntries, set the filter to the LogOutputStream or LogInputStream that is being used. It will check wether the handed over LogEntry suits the condition described in the method accept(LogEntry logEntry) of LogEntryFilter. The easyest way to decide on the acception is by cheking wether a LogEntry is an instance of something or not, but anything that tells LogEntries from will do. In this expample we define a LogEntryFilter that will only accept instances of OpenLogEntry, which is another example, defining a LogEntry for the opening of the a SalesPoint. (Therefore see: #Define a new LogEntry)

ToDo:

  1. Create a new class implementing LogEntryFilter.
  2. Implement accept(LogEntry logEntry) to return true if a le is an instance of OpenLogEntry.
  3. Use the LogEntryFilter with the designated LogInputStream or LogOutputStream.

Example Source Code:

1
public class OpenLogEntryFilter implements LogEntryFilter
{
 
2
public boolean accept(LogEntry logEntry)
{
// that will return true if le is an instance of OpenLogEntry
return (logEntry instanceof OpenLogEntry);
}
}

Define a new ProcessLogEntry

Description: The ProcessLogEntry is a subclass of LogEntry. The extension consists of the Process being handed over to the constructor and the redefinition of the toString() method, which roughly describes the process that is being logged on by the String "Process \"" + getProcessName() + "\" logged on " + getLogDate() + ".". There is also an additonal public method getProcessName(), which returns the name of the handed over Process. The reason for this subclass or for subclassing it by yourself might be the need to use the LogEntryFilter provided by the static field SaleProcess.LOGENTRYFILTER_PROCESSES_ONLY. It accepts only instances of ProcessLogEntry. So if you ever want to filter logs that belong directly to a SaleProcess, you have to subclass the ProcessLogEntry and use the LogEntryFilter on it.

ToDo:

  1. Create a subclass the SaleProcess.ProcessLogEntry.
  2. Add the constructor, call the superclass and hand over the SaleProcess.
  3. Redefine the toString() method to suit your log description.

Example Source Code:

1
public class GiveBackLogEntry extends SaleProcess.ProcessLogEntry
{
String name;
String customerId;
Object date;
2
public GiveBackLogEntry(SaleProcess process, String name, String customerId, Object date)
{
super(process);
this.name = name;
this.customerId = customerId;
this.date = date;
}
 
3
public String toString()
{
return name +
" gave back by customer " + customerId +
" (ID) at turn " + date;
}
}

Implement the Interface Loggable

Description: In order to be able to log anything, i.e. to put a LogEntry into the LogOutputStream, you have to implement the interface Loggable, which hands over the LogEntry to the Log. This can be done within the class, you want to log an event in, but also in an extra class implementing the interface. Such an extra class may of course be used form anywhere within the application. The second advantage is that you don't have to define conditions for different events in the getLogData method, but use seperate Loggable implementations for each event to be logged. The method public LogEntry getLogData() resolves the LogEntry that shall be put into the LogOutputStream. The implementation of Loggable here is used to log the opening of a SalesPoint by returning the OpenLogEntry, which was written for the event of opening a SalesPoint.

ToDo:

  1. Create a new class implementing Loggable.
  2. Implement the getLogData() method to return the right LogEntry for the logged event. Here it is the OpenLogEntry. (See: #Define a new LogEntry)
  3. Use the instance with Log.log(Loggable l) where you want to put the LogEntry into the LogOutputStream.

Example Source Code:

1
public class OpenLoggable implements Loggable
{
2
public LogEntry getLogData()
{
return new OpenLogEntry();
}
}

Log the opening and closing of a SalesPoint

Description: It might be quite useful to log events like the opening or closing of a SalesPoint. To be able to log these events, the SalesPoint already has the two methods protected void logSalesPointOpened() and protected void logSalesPointClosed(), which by default are empty. In order to have the two events logged, you have to make these methods put the right LogEntry into the desired Log. In this example we only show how to log the opening. For the other logging it is almost the same procedure, but only in the logSalesPointClosed() method.

ToDo:

  1. Implement the method protected void logSalesPointOpened() in your subclass of SalesPoint.
  2. Catch the LogNoOutputStreamException thrown by the getGlobalLog() method and the IOException thrown by the setGlobalOutputStream(OutputStream os), which is being called if no OutputStream exists, in order to create one.
  3. Get the Global Log and put a new Loggable implementation into it's OutputStream by using the method log(Loggable l). In our case it is a new instance of OpenLoggable.

Example Source Code:

1
protected void logSalesPointOpened()
{
 
2
// remember to catch the two Exceptions
try
{
 
3
// get the globalLog and put a new OpenLoggable into it
// this could also be logged into a local log
Log.getGlobalLog().log(new OpenLoggable());
}
catch(LogNoOutputStreamException lnose)
{
System.out.println(lnose);
}
catch(java.io.IOException ie)
{
System.out.println(ie);
}
}

Log the opening and closing of a log file

Description: It might be useful to log events like the opening or closing of a log file. To be able to log these events, Log already has the two methods protected void logOpenLog() and protected void logCloseLog(), which by default are empty. In order to have the two events logged, you have to make these methods put the right LogEntry into the desired Log. This might sound familiar to #Log the opening and closing of a SalesPoint, which it indeed is, so this example also shows how to extend Log, implement the Loggable and extend the LogEntry in one class, so everything is there when needed.

ToDo:

  1. Create a subclass of Log.
  2. Don't forget to hand over the OutputStream to the super class in order to create or append to the log.
  3. Implement the method logOpenLog() to log the opening of the logfile.
  4. Catch the two exceptions thrown by log(Loggable l).
  5. Implement the needed Loggable within the log method.
  6. Subclass the LogEntry within the implementation of the getLogData() method.
  7. Overwrite the toString() method of the LogEntry subclass.

Example Source Code:

1
public class LogLog extends Log
{
 
2
public LogLog(OutputStream outputStream)
{
super(outputStream);
}
 
3
protected void logOpenLog()
{
 
4
try
{
 
5
log(new Loggable()
{
 
6
public LogEntry getLogData()
{
return new LogEntry()
{
 
7
public String toString()
{
return "Log file openend";
}
};
}
});
}
catch(LogNoOutputStreamException noStreamException)
{
 
}
catch(IOException ioException)
{
 
}
}
}

Understand logging

Description: You can log almost anything that happens in your application, from the first start of it to the final shutdown and between these two, any use of salespoints, databaskets, logons, processes and so on. Although it is not quite obvious, this is a very useful feature. You may not only create some kind of history file, but also reconstruct interrupted processes, document buys and sells, resolve statistics, and more.

How it works: Logs can be put into any kind of InputStream, of which a FileInputStream is the most suitable. This OutputStream may be both, locally defined or globally, which, too, is the most common way to use logging. The GlobalOutputStream is being set by the static method Log.setGlobalOutputStream(OutputStream newOS), where newOS mostly is new FileOutputStream(String filename, true) (true to append the Stream to the file and not overwrite it), which creates a new file called filename if it doesn't exist. As said above, it is also possible to create a simple local Log by using the static method Log.createLog(OutputStream os) or use the constructor Log(OutputStream os). You may even implement the interface LogCreator and use it to create new Logs, but always remember to specify the Log you want to log an event into if you don't use the GlobalLog. Log by the way may be understood as a representation of the log file.

The interface Loggable: To log an event means to write a LogEntry to the OutputStream. This is done by the Interface Loggable, to be more precise, the method public LogEntry getLogData(). Known implementing classes are DataBasketEntryImpl and SaleProcess, but you may find the implementation quite poor and should feel free to redefine them. The most important thing for the event you want to log is, to put the right LogEntry into the OutputStream. This might be by implementing the getLogData method in the class you want to log in correctly, or by creating a seperate implementing class of Loggable and use it with Log.log(Loggable myLoggable).

The abstract class LogEntry (which is not really abstract...): Either way you have to make sure that the myLoggable or the method getLogData returns the right LogEntry. In this class, the method public String toString() defines what will be seen as the Description on the LogTableForm, which is being used to visualize a LogInputStream (normally the log file). The method getLogDate specifies the displayed Date on that TableForm. By default, the Date will be the present system time and the String will be "Object logged: " + getLogDate(). Feel free to redefine these two methods and add anything you want to your implementation in order to create sufficient input for the log file, but be aware that you allways have to get a String and a Date into it. It might also be useful to implement new LogEntry classes for each kind of log to be able to work with the LogEntryFilter. The method public boolean accept(LogEntry le) of that filter returns true, if le suits your specified condition, which, when implementing new LogEntry classes, might be the result of the class() method.

Persönliche Werkzeuge