
=============================================================================
    The logger

This document explains the function and hence api of libatalk/util/logger.c
it was written on 4th January 2002, by Simon Bazley (sibaz@sibaz.com)

=============================================================================


The logger was written to provide a means of storing log messages relating to
netatalk in a file rather than using the syslog facility.  The feature list 
was increased to include a number of ideas culminating in the current code.

-----------------------------------------------------------------------------
    Feature list

1) logging to syslog (old behaviour).
2) logging the file name and line number of the caller.
3) logging to a file.
4) allowing callers to specify what logical area of code the log comes from.
4) maintaining different log levels for different logical areas of code.
5) logging separate files for separate areas if required.

-----------------------------------------------------------------------------
    Method

The logger works by storing an array of data containers each responsible for
logging to a single file.  
Initally the array is initalised containing NULL references to the data, 
except for the zeroth position with contains the defaults (default data is
statically defined in the logger code, but can be overridden easily).  
As calls are made to the log_setup and syslog_setup functions with unused
code area references, new memory is allocated then a pointer to that memory
stored in the array.  It is intended that all data containers are setup and
initialised early on in the program life cycle.  There is no means to erase
initialised data containers other than by calling the log_close function, 
which should only be done after the last log messages has been sent.  Logger 
behaviour is unspecified after log_close has been called.  

=============================================================================
    API Functions
=============================================================================

-----------------------------------------------------------------------------
    void log_init();
Log_init allocates and zeros memory for the global data containers, then 
sets up then copies the statically defined default loggers into the zero 
position.  It is called by both of the setup functions and will only do 
anything if the global data hasn't already been initialised.  
Its only made externally accessibly for completeness, and need never be used.  

-----------------------------------------------------------------------------
    bool log_setup(char *filename, enum loglevels loglevel, 
                   enum logtypes logtype, int display_options);
log_setup specifies the filename and the loglevel that should be used when 
logging logs with the specified logtype.  The display_options indicate what
system stuff should be prepended to each line in the log file, in the same
way as the parameters in an openlog call.  The options defined in the logger.h
file correspond to equivalents in the syslog.h file.  The values they represent
are equivalent in both files, but should not be assumed.  
Some logoptions not in syslog have been added, see logger.h for details.  

-----------------------------------------------------------------------------
    void syslog_setup(enum loglevels loglevel, enum logtypes logtype, 
                     int display_options, int facility);
syslog_setup is equivalent to the log_setup call but setups data to log to the
syslogger for the area specified instead of a file.  syslog_setup will call 
openlog for each call to syslog_setup.  The facility parameter is stored, but 
used purely in this call to openlog.  I assume it you call syslog_setup 
multiple times with different values for display_options and facility, only the
last call is used.  Refer to syslog(3) for more details.  
The display options are global and are only stored once regardless of the 
logtype.  The value is only used by the logger used in-so-far-as the option is 
not a feature of the syslogger. 
The loglevel of the given logtype is stored even though the syslogger is used. 
The loglevel passed to each call to LOG is translated before being sent to the
syslogger.  The assumtion is that a log_severe (for example) applies only to 
netatalk and hence should not be translated to LOG_CRIT, but instead LOG_ERR 
is used.  (this translation is implemented in the function at the end of the 
logger.c file.  This means should you want to you could log debug messages to 
the syslogger without having to alter the syslogger for every other 
application.  
Some logoptions not in syslog have been added, see logger.h for details.  

-----------------------------------------------------------------------------
    void log_close();
log_close simply frees up all the memory and closes any open files.  Ideally 
you could then recall log_setup and start again, but I think its too permanent 
for that.  This also closes up the syslogger.  

-----------------------------------------------------------------------------
    void set_processname(char *processname);
set_processname stores the name of the funning process for use in identifying 
the caller in each line in the log.  This is equivalent to the parameter used 
in the call to openlog, but since it is global for the process it seemed 
sensible to separate it from every log_setup call.

-----------------------------------------------------------------------------
    make_log_func set_log_location(char *srcfilename, int srclinenumber);
This sets up the temporary variables indicating the caller name and line number
logged in calls to make_log_entry.  The return value is a pointer to the 
make_log_entry function, thus enabling a single command to call both functions,
hidden behind a macro (the LOG macro, see later).  

-----------------------------------------------------------------------------
    void make_log_entry(enum loglevels loglevel, enum logtypes logtype, 
                          char *message, ...);
make_log_entry is an equivalent function to syslog except for the logtype 
parameter.  The logtype given indicates which area of code the call has come 
from and is used to determine which log_setup call is used to log the message.

-----------------------------------------------------------------------------
    void load_proccessname_from_proc();
This function can be called instead of set_processname, to determine the 
processname from the /proc file system instead of from a given parameter.  This
probably only works on linux, but is included because it was the original 
method used to get the procname.  

-----------------------------------------------------------------------------
    LOG Macro
This is actually defined as a call to set_log_location with parameters 
__FILE__ and __LINE__.  This returns a pointer to the make_log_entry function
which is then what the compiler will use to pass the given parameters to.  
What this means on the face of it is the LOG Macro can be considered to be this
    void LOG(enum loglevels loglevel, enum logtypes logtype,
              char *message, ...);

-----------------------------------------------------------------------------
    LogTypes
logtypes have been mentioned earlier.  They specify the logical area of the 
code that a log call has come from.  This information is then used in a 
number of ways (see earlier).  
logtypes is infact an enum defined in the logger.h file.  To add a new 
logtype for a new area of code, add a new item to the logtypes enum, and a
corresponding string to the LOGTYPE_STRING_IDENTIFIERS macro.  
The string identifier is used as part of the message string for each log
message.  

=============================================================================
    API in brief
=============================================================================
-----------------------------------------------------------------------------
    SyslogSetup in brief
To setup the logger for use in a new application, call set_processname with 
the proccess's name, then call syslog_setup with your prefered default loglevel
and the default logtype (logtype_default).  The display_options and facility
are dependant on how you choose to display your logs.  

For Example:

set_processname("afpd");
syslog_setup(log_debug, logtype_default, logoption_ndelay | logoption_pid, 
		logfacility_daemon);

this sets the default loglevel to debug and the display options to include the
pid with the each log message.  The syslogger is told the application is of
type LOG_DAEMON
Make further calls to syslog_setup if other logtypes should have a different
loglevel.  

-----------------------------------------------------------------------------
    LogSetup in brief
Logsetup is similar to syslogsetup except multiple calls are more common.  
First setup the filename and loglevel for the default logtype then call 
log_setup with the filename and loglevel for any logtypes that should take 
nondefault parameters.  A NULL filename indicates that the default filename 
should be used (this removes the need to store multiple instances of the same string).  

For Example:

log_setup("/var/log/netatalk.log", log_debug, logtype_default, 
            logoption_ndelay | logoption_pid);

-----------------------------------------------------------------------------
    Logging in brief
Logging is very similar to using the syslogger, just call the LOG macro in 
your code with the level of that log message, and the area of code it applies 
to (or logtype_default if you can't think of one), and a message.  The message
is passed with identical parameters to that in an sprintf statement.  Give a
format string as first parameter, then any variables in that string as extra
parameters.  
The logger will only log the message if the loglevel given is more severe 
(lower in value) than that given in the call to logsetup for the given logtype 
(or default logtype if your given one is uninitialised).

For Example:

  LOG(log_info, logtype_logger, "Logger %d setup complete", n);


