********************
Loggers
********************


I. Introduction to the Logging Sub-System
=========================================

The logging sub-system is a component of the channel sub-system.  The
logging sub-system has special access to the live :class:`Channel
Database <channels.channel_database.ChannelDatabase>`.  As channel
events occur (e.g. new channel, remove channel, new sample, etc.) the
logging sub-system is granted permission to make use of these events
before any other consumer module in the system.  Individual logger
instances receive these events and may record them.

Because the logging sub-system is channel event-oriented two principal
styles of loggers may be created: a journalling replay logger or a
simple event log.


Journalling Loggers
-------------------

A journalling replay logger implements the
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`
class and records events to a log file as a series of changes from the
initial state of the live :class:`Channel Database
<channels.channel_database.ChannelDatabase>`.  This allows a user to
access historical information from the log as if they were accessing
the live :class:`Channel Database
<channels.channel_database.ChannelDatabase>` by seeking the state of
the log to any arbitrary point in time.  This style of logging
functionality enables a user to answer queries such as, "show me the
entire state of the system at three in the afternoon, yesterday."

A logger implementing
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`
may also present information as a simple event log (see below) by
using the
:meth:`~channels.channel_database_interface.ChannelDatabaseInterface.log_event_iterator()`
function.

A journalling logger is implemented in
"src/channels/logging/file_logger".


Simple Event Loggers
--------------------

.. todo:: Does the Simple Event Logger really save events to a file? As far 
   as I know it only prints them. 

A simple logger simply records the events to a file or device in the
order they occur.  A simple event log does not implement
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`.
An example simple logger implementation is given in
"src/channels/logging/simple_logger".


II. Logging Details and Implementations
=======================================

A. Logging System Interactions 
------------------------------

The Path of an Event
````````````````````

The logging system is a component of the Channel sub-system.  When new
channel or :class:`~samples.sample.Sample` events occur in the system,
a member of the Channel sub-system known as the :class:`Channel
Publisher <channels.channel_publisher.ChannelPublisher>` is tasked
with distributing them.  The :class:`Channel Publisher
<channels.channel_publisher.ChannelPublisher>` notifies the
:class:`Logging Manager
<channels.logging.logging_manager.LoggingManager>` as its highest
priority task of the event.  The :class:`Logging Manager
<channels.logging.logging_manager.LoggingManager>` then wraps each
Channel sub-system event as a logging sub-system event and passes the
event to each logger registered with the
:class:`~channels.logging.logging_manager.LoggingManager` via each
logging instance's
:meth:`~channels.logging.logger_base.LoggerBase.log_event` method.

Because the system is waiting on this distribution to occur, it is
very important for a logger to ensure that the
:meth:`~channels.logging.logger_base.LoggerBase.log_event` call
executes quickly.  The File Logger implementation ensures that
:meth:`~channels.logging.logger_base.LoggerBase.log_event` returns
quickly by enqueuing messages and deferring the processing to a
second, decoupled thread.  The Simple Logger simply prints the events
and thus no decoupling in necessary.


Using a Logger in the System
````````````````````````````

Interactive loggers, such as the File Logger, implement the
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`
class.  Presentations may then present historical data using the same
interface used to present data from the live :class:`Channel Database
<channels.channel_database.ChannelDatabase>`.  Presentations such as
the console interface presentation allow users to interact with
logging instances via a special set of commands.

See the console interface's help text in its **logger_** commands
for more information.

Programatically the
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`
defines the following methods which a specific logger may choose to
implement:
   
   * **log_rewind** rewinds the channel database to the oldest
     known state.

   * **log_next** advances the channel database one event in time.

   * **log_prev** reverts the channel database back one event in time.

   * **log_seek** sets the state of channel database to an arbitrary
     point based upon an offset or event number.

   * **log_position** returns the current position of the log by
     reporting the current event number.

   * **log_event_iterator** returns Python iterator object which
     iterates through a list of logging event's objects between
     two given event numbers.


B. Logging Limitations and Requirements 
---------------------------------------
 
For a given logger, the main rule that needs to be followed is that the
**log_event()** method must happen very quickly. Simple operations can be
performed but anything that can take a significant amount of time will
delay the system and instead should be passed to another thread to be
handled in order to guarantee system responsiveness. 
 
In certain configurations, a logger may need to handle a very large
number of new events. It is generally a good idea to have some sort of
filter if it is possible that your logger may not be able to keep up
with the influx of events. 
 
Due to the delay of writing events out to flash, the File Logger may
encounter problems with a large number of samples. Configuring the
system and filters such that the File Logger receives no more events
than it can handle may be necessary.
 
 
C. Writing Your Own Logger 
--------------------------
 
The best approach for writing a new logger is to examine the Simple
Logger first, and then take a look at the File Logger, to see examples
of how things can be implemented. All loggers must be derived from
:class:`~channels.logging.logger_base.LoggerBase`.  If you are
implementing a journalling logger you will need to derive your context
class from
:class:`~channels.channel_database_interface.ChannelDatabaseInterface`.

The following methods must be defined on the logging instance:

    * **start()** start the logging instance, return True
    * **stop()** stop the logging instance, return True
    * **apply_settings()** apply new settings to the logger instance,
      return a tuple of accepted, rejected, and required settings not
      found.
    * **log_event()** handle a new logging event, this method must
      execute as quickly as possible.

The following method must be implemented when implementing a journalling
logger:

* **channel_database_get()** return a
  :class:`~channels.channel_database_interface.ChannelDatabaseInterface`
  implementation object.

.. todo:: Programing question. Why does the **Simple Logger** have a
   **channel_database_get()** method then? At the beginning of this document
   it was said the Simple Logger does not implement 
   :class:`~channels.channel_database_interface.ChannelDatabaseInterface`.

 
