casacore
|
Send, record, and filter informational messages. More...
Modules | |
Logging_module_internal_classes | |
Internal Logging_module classes and functions. | |
Classes | |
class | casacore::LogFilter |
Filter LogMessages on message priority. More... | |
class | casacore::LogIO |
ostream-like interface to creating log messages. More... | |
struct | casacore::LogIO_global_functions_command |
Functions to send commands to a LogIO object. More... | |
struct | casacore::LogIO_global_functions_output |
Functions to accumulate text in the output message. More... | |
class | casacore::LogMessage |
Informational log messages with with time, priority, and origin. More... | |
struct | casacore::LogMessage_global_functions_LogMessage_ostream |
Write a LogMessage to an ostream. More... | |
class | casacore::LogOrigin |
LogOrigin: The source code location of the originator of a LogMessage. More... | |
struct | casacore::LogOrigin_global_functions_LogOrigin_ostream |
Write a LogOrigin to an ostream. More... | |
struct | casacore::LogOrigin_global_functions_SourceLocation |
Helper struct to get the source line. More... | |
class | casacore::LogSink |
Distribute LogMessages to their destination(s) More... | |
class | casacore::MemoryLogSink |
Save log messages in memory. More... | |
Send, record, and filter informational messages.
See below for an overview of the classes in this module.
Logging, as in "log book", or "processing log."
The classes in the logging module have two essential purposes:
The two fundamental classes in the Logging module are the LogMessage and LogSink classes. However, the class which is most of interest to application programmers is the LogIO class since it forms the usual interface to logging.
A LogMessage
consists of an informational message tagged with the time, a priority (DEBUGGING, NORMAL,
, WARN
, or SEVERE
) and the source code location of the origin of the message (for use in debugging primarily).
The LogSink
is used to send the LogMessage
to its destinations. Usually the message will be sent to both a global sink, intended for user information (e.g., a GUI window), and to a sink associated with the dataset(s) which are being modified. In practice, the application programmer does not need to worry about where the global messages go, that policy is implemented by the Tasking system. In practice, global messages will be sent to Glish, where they will appear in a GUI, unless Glish is not available, in which case SEVERE messsages (only) will be sent to stdout. However, the principle is that "ordinary" application programmers shouldn't worry about the details of the global sink - they should just use it.
A LogFilter can be used to filter messages (based on priority only at the moment) before they are sent to their appropriate sink(s).
The LogIO class puts an ostream like interface on top of the loggins system. Basically, the application programmer just has to create messages using and os << items
type interface.
The first issue that the application programmer has to decide is whether to use logging at all, or if instead he should put his messages into a String or an ostream
. It is never wrong to use log messages, however it is reasonable for low level classes to use String
s or ostream
s, since the caller of that class will have the opportunity to put the text in a log message if he decides that's the most appropriate thing to do. Note that it is always wrong to write directly to cout
or cerr
(other then for debugging) - use an ostream
, so the caller can replace it with, for example, an ostringstream
.
Once you decide to use logging, the application programmer only has to decide at every location he wants to log:
Schematically, application programmers would use the logging system as follows:
os
is when it is passed in to this function). When a dataset is created from several other datasets, their input "histories" should be merged if possible. This can be done if the local log sink is in fact a Table. The way you do this is by interrogating the local sink to find out if it is in fact a TableLogSink. If it is, you can use a concatenate method of TableLogSink. Schematically this would be implemented as follows in some DataSet class that has a logSink method that returns a LogIO reference:
Of course, DataSet might provide some convenience function for merging histories. However the point is that given a sink, you can safely determing whether or not it is in fact a TableLogSink, and if it is you can call its concatenate function, which takes another TableLogSink.
The following example code is checked into the system as dLogging.cc
. It is found in the Logging test directory.
This toy class is meant to represent one which is to have "attached" logging information. Generally, these classes would be fairly high level astronomical classes, e.g. Image
, not Array
. Note that only operations which change the data should be logged in the internal log. Operations which only read the data should be logged either globally, or in the class that is taking the results and modifying its own data.
MeasurementSet
will have a processing log table with a known name. toWhat
. The private LogSink
data member is initialized with one that the caller provides. Note that LogSink uses reference semantics, so that if another "copy" of the sink is made then all the log messages will go to the same place. For example:
This can be useful if several classes might be modifying the same data, or if a data is spread over several objects.
Also, os_p is intialized from the sink.
postGloballyThenThrow()
if you only wanted to send the message to the global sink (for example, if the object is hopelessly corrupted, or if the problem occurs in a read-only operation). The thrown exception is an AipsError
. The post*Throw()
functions will always set the priority to SEVERE
, however it doesn't hurt to show your intentions set()
from the constructor to give the data values an initial value. LogMessages are relatively expensive to produces and consume. Use of them in a very tight loop should either be ifdef
'd out as in this example, or like:
The advantage of this code is that it's always available - so, for example, you can turn it on and off by manipulating the global variable aips_debug_on
. However very tight loops cannot even afford this extra if
, and should prefer the ifdef
.
Normally the DEBUGGING
messages are "boring but low-volume", and you should just send them normally.
This function shows how a global function that modifies an object can send log messages to that objects LogSink
using a function of that object to get access to its sink.
This is an example of a global function that only reads – does not change – an object.
sum()
might log the message somewhere else if the return value is used to modify data in some other object. Instead we send it to the global sink. Here we don't POST the message ourselves, we rely on the LogIO destructor to do it for us. DEBUGGING
from the default NORMAL
. The default global sink logs to cerr. The global sink can be replaced with LogSink::globalSink()
. LogSink
if it knows where it wants its messages to go.