# Logging

This unit provides a mechanism for logging events in the system. It decouples
the event logging part from the handling of that logging. The idea is that you
have, on one side, the units that log stuff that use this very unit to register
their event and then log event occurrences. Then, on the other side, units such
as [mem/logroll] registering for handling some or all of those events.

This unit also supplies a default handler, which prints the events and
parameters.

Logging handling is opt-in. By default, logging an event is a noop. Only through
explicit registering of handling through configuration does logging has an
effect. It is thus safe to sprinkle logging around without fearing adverse
effect.

## The log handler

When we refer to a log handler, we refer to a XT that has the same signature
as "log", except that it leaves everything on PS. That is, its signature is
( ... logevent -- ... logevent )

### Log handlers should be fast

Log events can be fired up quite often. For example, low level drivers can fire
them up. Garnering statistics about low level drivers is one of the main aims of
logging.

Therefore, a log handler, especially a global one, should be written with speed
in mind. This generally precludes any kind of printing. For example, the "log."
word supplied in this unit can be useful in a tight spot, but you would probably
not want to plug this as a global handler because it would slow down the system
by quite a bit.

### Log handling cannot recurse

It's possible that the code that handles log event itself triggers a logging
event. This brings us dangerously close to infinite loops and we can't allow it.

Therefore, this unit has a global flag (reset on a quithook [usage/flow] in case
of aborts) that tracks when we are handling a log event. When that flag is set,
further log events are counted in logcnt, but otherwise not handled.

## Documenting log events

Units creating log events should document each of them in a "Log Events" section
of their "API" section with the event's name, category and arguments, like this:

MYEVENT MEDIUM ( arg1 arg2 arg3 )
  The description of the event.

This would mean that "arg3" is on PS top right under the event when "log" is
called.

## Using this unit in the boot sequence

This unit is designed to be able to be part of the boot sequence, so it needs
to have a minimal dependency list.

## Example usage

needs io/log
3 MEDIUM createlogevent MYEVENT
: foo ( do stuff ) argA argB argC MYEVENT log ;

\ ... and then in system config
needs io/log
MEDIUM HIGH CRITICAL 3 ' log. categoryfilter handleall

## API

### Constants

HANDLERSCNT  4. Size of the handlers array. Actual capacity is one less because
             we keep the last entry as a zero as a sentinel.

### Log category constants

LOW
MEDIUM
HIGH
CRITICAL     0, 1, 2, 3. Categories for "error" types of events.
STORAGE      4. Storage (hard disk etc.. ) events
MEMORY       5. Memory events
NETWORK      6. Network events
IO           7. I/O event that isn't covered in a category above
TIME         8. Time-related events
OTHER        9. None of the above

### Structures

struct LogEvent {
  *LogEvent prev ;
  uint category nargs logcnt ;
  [uint,HANDLERSCNT] handlers ;
  [uchar,0] name ;
}

This represents an event that can be logged. It's what is created by
createlogevent. It's what must be passed to "log".

prev      LogEvent is a linked list
category  One of the log category constants
nargs     The number of arguments that is passed on PS when calling "log"
logcnt    Number of time this event was logged regardless of whether it was
          handled.
handlers  An array of handling XTs that have registered for handling this
          particular event.
name      The name of that event, a repeat of the word's name, as a string.
          We repeat it here to avoid having to depend on [mem/dict] for ?var>e

### Words

log ( ... logevent -- )
  Log an event of the specified LogEvent. "..." are the argument to log and must
  correspond exactly to the event's signature.

logk ( ... logevent -- ... )
  Same as "log" but keep the arguments on PS.

createlogevent ( nargs category "name" -- )
  Create a new word "name" with the proper event structure tied to it. That
  word can then be used with "log"

newcategoryid ( -- category )
  Yields a globally increasing number, thus guaranteeing a category that isn't
  used anywhere else. If you want to create a category specific to a unit,
  that's what you'll want to use.

handleall ( xt -- )
  Register "xt" as a handler for all events.

handleevent ( xt logevent -- )
  Register "xt" as a handler of this event. If you register a handler both with
  this word and "handleall", each event will fire the handler twice, so don't do
  that.

reseteventhandlers ( logevent -- )
  Remove all handlers from specified event.

resetallhandlers ( -- )
  Remove all handlers everywhere.

log. ( ... logevent --- ... logevent )
  Print the name of the event followed by all its arguments, with ".x".

categoryfilter ( ... n xt -- xt )
  Create a log handler that calls the supplied log handler "xt" only if the
  category of its logevent is present in the supplied PS list "... n".

.logstats ( -- )
  Print a list of all events with their vital stats (category, nargs, logcnt).
