I have a new proposal for a syslog facility. The underlying premise is
that explicit opening and closing of syslog channels (connections)
should be deprecated, since providing multiple channels typically requires
multiplexing them onto a single connection. Just create & use them, letting
the run-time system figure out how to manage them. This means that we no longer
have to have facilities for opening-a-connection/doing-some-work/closing-
the-connection in a fashion that protects the connection-close across
non-local exits, which simplifies things a fair amount. E.g., I can punt
the analogs to (CALL-WITH-INPUT-FILE filename proc) and
(WITH-INPUT-FROM-FILE filename thunk).
The API follows. After we converge, I will post to the scsh and scheme
newsgroups for discussion, but we would not have to wait for that to proceed.
-Olin
** Basic operations on syslog channels
======================================
A syslog channel is a value that describes a particular connection to the
syslog system. Scsh permits multiple such channels, each describing a
different client of the logging service. This can be particularly useful
in a multithreaded context, where different threads may be executing
completely distinct applications.
The following procedures allow one to create, examine, recognise, compare and
hash syslog channels.
(syslog-channel [name class option]) -> syslog-channel
A syslog channel is characterised by three values that describe
the client facility using the logging service: the facility NAME,
the facility CLASS, and any connection OPTIONS.
- NAME is either a string, or false, meaning no name string is associated
with the channel. It is intended to name the facility performing the
logging, and is typically the name of the process' program (in a
one-application-per-process setting).
Additionally, the NAME parameter may be one of
syslog-channel This channel's name
#t The current channel's name
If no NAME parameter is provided, it defaults to #F.
- CLASS is a value specifying the general class of the facility performing
the logging. The range of possible classes are the values of these
named constants:
syslog-class/authorisation Security/authorization messages
(private)
* syslog-class/cron Clock daemon (cron and at)
syslog-class/daemon Other system daemons
syslog-class/kernel Kernel messages
syslog-class/local0 Reserved for local use
syslog-class/local1 Reserved for local use
syslog-class/local2 Reserved for local use
syslog-class/local3 Reserved for local use
syslog-class/local4 Reserved for local use
syslog-class/local5 Reserved for local use
syslog-class/local6 Reserved for local use
syslog-class/local7 Reserved for local use
syslog-class/lpr Line-printer subsystem
syslog-class/mail Mail subsystem
* syslog-class/news USENET news subsystem
* syslog-class/syslog For msgs generated internally by syslog
syslog-class/user Generic user-level messages (default)
* sysloc-class/uucp UUCP subsystem
The unstarred constants comprise a portable set. The starred facility
classes are common facility classes that are not provided on all Unix
systems; if such a facility class is not available on the particular
system, the named constant is bound to #F. Other facility classes
may be provided by a particular Unix system as well.
The default facility class is SYSLOG-CLASS/USER.
Additionally, the CLASS parameter may be one of
syslog-channel This channel's class
#t The current channel's class
[Some work needs to be done to pick out the standard BSD/Linux/Solaris
facility classes, i.e. the starred entries above. This is the same game
played with tty-driver flags -- see section 3.12.1 of the manual.]
- OPTION is the bitwise-or of the following options:
syslog-option/console-on-error
syslog-option/pid
syslog-option/echo-on-stderr
Other options may be available on a particular Unix implementation.
Additionally, the OPTION parameter may be one of
syslog-channel This channel's options
#t The current channel's options
Creating a syslog channel does not open a connection to the syslog
service; the actual connections are made on-demand. (See below.) Note that
the NDELAY option is not provided by scsh; this effect can be obtained
with FORCE-SYSLOG-CHANNEL if desired.
Design note: Allowing syslog-channels as parameters makes it easy to
"clone" a channel, overriding specific attributes where desired.
Implementation note: the syslog system is provided using standard C
interfaces that do not permit nul-byte or multi-byte characters for the
facility name. So things are somewhat iffy for Schemes that provide
super-Latin-1 character types. Such an implementation might employ an
alternate interface to the system logger, or might be forced to filter
these characters out of the name. Similarly, nul characters are currently
filtered out of the facility name, as C is unable to deal with them.
(syslog-channel:class syslog-channel) -> int
(syslog-channel:name syslog-channel) -> string or false
(syslog-channel:options syslog-channel) -> int
These functions export the three attributes of a syslog channel.
Design note: syslog channels are pure -- they cannot be side-effected.
(syslog-channel? x) -> boolean
(syslog-channel= syslog-channel1 syslog-channel2) -> boolean
(syslog-channel-hash syslog-channel [bound])
Basic type predicate, equality and hash support for syslog channels.
Syslog channels are distinct from other values in the system.
Two channels are equal if their three attributes are all equal.
The function used to hash syslog channels is implementation specific.
BOUND is an exact non-negative integer, defaulting to zero.
If it is positive, the hash function returns an integer in the range
[0,BOUND); if it is zero, the hash function returns an integer
in a large, implementation-chosen range, e.g. the range of non-negative
integers that can be represented in a single machine word.
** Forcing channels to open and close
=====================================
These operations are not strictly necessary to normal use of the syslog
facility, and are, in fact, deprecated for normal use.
(force-syslog-connection syslog-channel) -> unspecified
If the channel is not currently connected to the syslog service,
connect it. This is *not* required in order to log messages
with the channel -- logging routines such as SYSLOG-WRITE will
make connections on-demand when given an unconnected syslog channel.
This routine simply forces the connection to be made at an earlier
point in time.
Implementation note: Most underlying implementations of the syslog
facility will only allow one syslog connection at a time, requiring the
scsh run-time system to multiplex different syslog-channels onto this
single connection on-demand. Hence forcing a syslog channel to be
connected in one thread could be undone a millisecond later by a syslog
operation on a different channel in another thread.
(close-syslog-channel syslog-channel) -> boolean
Close the channel; return true if the channel was previously open,
and false if it had already been closed.
Attempting to force or write to a closed channel will raise an error.
Syslog channels are also closed when they are garbage collected.
Implementation note: closing a channel does not necessarily break
the connection to the syslog daemon, since there may be other open
channels in the system, multiplexed onto the same underlying socket.
** The dynamicaly-scoped current syslog channel
===============================================
The scsh run-time systems maintains a "current syslog channel" which
is the default channel used for logging messages. The binding of the
current channel is maintained with dynamic scope, and can be manipulated
with the following forms. Note that this allows threads both to share a single
binding, or isolate themselves from other threads, as desired.
The top-level dynamic scope of the scsh run-time system binds the current
syslog channel to a value with default facility class, no options, and
a facility name taken from the name of the Unix process.
(with-current-syslog-channel* syslog-channel thunk) -> value(s) of thunk
(with-current-syslog-channel syslog-channel body ...) -> value(s) of body
Bind the current syslog channel to SYSLOG-CHANNEL and
call the thunk / execute the body forms in that dynamic scope.
(set-current-syslog-channel! syslog-channel) -> unspecified
Change the current syslog channel in the current dynamic scope.
The side-effect is only performed on the binding of the current
syslog channel that is visible in the current dynamic scope.
The binding remains unaltered in other scopes.
(current-syslog-channel) -> syslog-channel
Return the current syslog channel.
** Logging messages with syslog channels
========================================
(syslog-write level msg [syslog-channel]) -> unspecified
Log string MSG with severity LEVEL to SYSLOG-CHANNEL, which
defaults to the current channel.
The severity level is one of the following named constants:
syslog-level/emergency System is unusable
syslog-level/alert Action must be taken immediately
syslog-level/critical Critical condition
syslog-level/error Error condition
syslog-level/warning Warning condition
syslog-level/notice Normal, but significant, condition
syslog-level/info Informational message
syslog-level/debug Debug-level message
Implementation note: the syslog facility is provided using standard C
interfaces that do not permit nul-byte or multi-byte characters. So things
are somewhat iffy for Schemes that provide super-Latin-1 character types.
Such an implementation might employ an alternate interface to the
system logger, or might be forced to filter these characters out of the
message. Similarly, nul characters are currently filtered out of the
message, as C is unable to deal with them.
[Do we need to select & name standard but non-portable Linux/Solaris/BSD
severity levels, a la facility classes above?]
(syslog-format level syslog-channel format-string arg ...) -> unspecified
As in SYSLOG-WRITE, but the message is produced in the manner
of the FORMAT procedure, using the given format-string and arguments.
Passing #t for the SYSLOG-CHANNEL parameter will use the current
syslog channel.
[I dislike FORMAT, and intend to design a replacement, extensible little
language for doing I/O one day. Should I enshrine FORMAT in this API?]
-------------------------------------------------------------------------------
How it works:
Making a syslog channel simply allocates a record with the three indicated
attributes, and an OPEN? bit which is cleared when the channel is closed.
The run-time maintains a private bit of state, the channel currently installed
via the C syslog(3). When a SYSLOG-WRITE is done, the channel to which we are
writing is compared to this installed channel (using SYSLOG-CHANNEL=, not
EQ?). If they differ, we use the C openlog(3) routine to install the new
channel as the currently-installed one and proceed.
We keep a "weak set" of the allocated, open channels. Closing a channel
removes it from this set; if the set is empty we call the C closelog(3)
routine. This weak set is also checked after each GC.
Should also have a low-level, sub-thread-system facility for getting at the C
openlog(3) and closelog(3) routines, procedures named with % prefixes.
SYSLOG-WRITE must scan the message string to escape %'s and remove nul chars,
since %'s are interpreted by syslog(3) as printf codes, and nul chars will
mess it up.
-------------------------------------------------------------------------------
Random notes:
Open issues are marked in the above API with [...]'s.
syslog design alternatives
- no channel values, full spec on each call
No state, no dynamic scope hoo-hah, completely functional.
But options are problem. No good.
- encode as regular ports, use regular I/O funs like WRITE-STRING & FORMAT.
Must buffer data & split at newline boundaries. How to handle two distinct
writes, where the first is distinct from the second, but doesn't end in
newline?
+ Require the newline (best)
+ Add an "end-of-msg" proc. (sux)
This approach means you must bind the severity level to the port when
you make the port; different severity levels require different ports.
Awkward.
Note that with the current design you *can't specify* the facility class at
write time; it is fixed when the channel is created. But channel creation is
very cheap, and, actually, something one does rarely, so no problem.
|