Common Lisp syslog interface.
Common Lisp interface to local and remote Syslog facilities.
- Local sysloging implemented via foreign-function calls.
- Remote syslog over UDP sockets.
- Optional, complete RFC5424 support.
The source files are relatively well documented, so we recommend looking at them, but here's a quickstart guide.
- cl-syslog.udp has been removed due to duplicate functionality in cl-syslog: udp-log-writer
- All functionality has been moved into system cl-syslog package cl-syslog (Sorry if this causes breakage)
cl-syslog, use Quicklisp or ASDF as usual:
> (ql:quickload :cl-syslog) ;; or, if ASDF can already see it: > (asdf:load-system :cl-syslog)
Priorities and facilities are stated by their keyword name. For
instance, the priority
:warning on facility
:user. All of these
keywords are housed in the variables
*variables*. Should you provide invalid facilities or priorities,
cl-syslog:invalid-priority will be signaled.
To log to your local syslog daemon, use
cl-syslog:log (note that
this is shadowed, and is obviously not the same as
> (cl-syslog:log "MyApp" ':user ':warning "Low on memory.")
Then look in your
/var/log/messages or other location if you have
/etc/syslog.conf. On macOS, you can use the Console
RFC 5424 Support
RFC 5424 is supported. (You can check if your CL-SYSLOG installation
support it by checking if
cl-syslog::rfc5424 is present in your
The first thing you'll need to do is create an instance of the
rfc5424-logger class, filling out as many details as you
please. You're encouraged to fill out as many slots as you can so that
log messages contain more information.
Of particular interest is the
:log-writer initarg. This controls how
log messages get processed in the end. The default log writer will use
a function wrapping
cl-syslog:log. Should you instead be interested
in writing to standard output, you can supply
(cl-syslog:stream-log-writer) as an argument, like so:
(defparameter *logger* (make-instance 'cl-syslog:rfc5424-logger ;; optional :facility ':local0 :app-name "MyApp" :hostname "computer.local" :log-writer (cl-syslog:stream-log-writer)))
There are a few options for log writers, including a UDP log writer
for remote RFC-compliant logging. You can always support your own
lambda function, too.
Once you have a logger, the simplest way to create a compliant message
is to use
> (cl-syslog:format-log *logger* ':warning "Low on memory.") <132>1 2018-12-13T22:17:56Z computer.local MyApp 69840 - - Low on memory.
RFC 5424 has support for sending structured data within the log
message. It's a somewhat complicated arrangement, in that the
structured data has to be defined and "agreed upon". Specifically,
there are a set of IETF-approved structured data. They're identified
by "structured data IDs", and the standard IDs are
meta. Each of these have different structured data
Personal structured data IDs must have some name, followed by
followed by a usually 4 or 5 digit integer. For instance,
mycompany@0001 is a valid personal structured data ID. One can
define a structured data ID using
cl-syslog:define-structured-data-id. The definition of the standard
origin ID is:
(define-structured-data-id |origin| (:standard t) (|ip| :allow-repetitions t :validator 'ip-address-p) |enterpriseId| (|software| :length (integer 0 40)) (|swVersion| :length (integer 0 32)))
(This can be seen in the file
Your own structured data ID might be:
(cl-syslog:define-structured-data-id |mycompany@0001| () |who| |what|)
Note that the field names are escaped to follow the RFC's usual idiom. Also node that the field names are here symbols scoped to your package. CL-SYSLOG exports all of the symbols of the standard ID's.
How can we use these? We use the log macro
rfc-log. It's a macro
because it does extra work at compile time to build code that only
runs if the log message is to be sent.
Suppose we've defined the above structured data ID. Then we might log the following:
> (cl-syslog:rfc-log (*logger* :warning "Running out of memory! I got ~D byte~:P left!" 10) (cl-syslog:|origin| cl-syslog:|ip| "188.8.131.52" cl-syslog:|software| "My Testing App" cl-syslog:|swVersion| "12.2") (|mycompany@0001| |who| "John Doe" |what| "Needs to buy a new computer.")) ;; output: <132>1 2018-12-13T22:28:02Z computer.local MyApp 69840 - [origin ip="184.108.40.206" software="My Testing App" swVersion="12.2"][mycompany@0001 who="John Doe" what="Needs to buy a new computer."] Running out of memory! I got 10 bytes left!
Optionally, a message ID string can be supplied. This is useful if the
message is a part of a category of similar messages. Suppose the
message ID is
"LOG1234", then we could write the above message as:
> (cl-syslog:rfc-log (*logger* :warning "Running out of memory! I got ~D byte~:P left!" 10) (:msgid "LOG1234") (cl-syslog:|origin| cl-syslog:|ip| "220.127.116.11" cl-syslog:|software| "My Testing App" cl-syslog:|swVersion| "12.2") (|mycompany@0001| |who| "John Doe" |what| "Needs to buy a new computer.")) ;; output: <132>1 2018-12-13T22:29:36Z computer.local MyApp 69840 LOG1234 [origin ip="18.104.22.168" software="My Testing App" swVersion="12.2"][mycompany@0001 who="John Doe" what="Needs to buy a new computer."] Running out of memory! I got 10 bytes left!
RFC 5424 Caveats
Currently, Unicode isn't properly handled. It is expected all data is
ASCII. It is also expect that your Lisp's
ASCII. The necessary BOM will not be inserted for Unicode.
The RFC 5424 log handling functions generally write to streams, but the entry points cons up strings (if and only if the log message is actually sent). There is currently no support for allowing streaming log data at the log generation level.
Many log processors do not implement all of RFC 5424, but nonetheless
cope rather well with the format, and can understand the RFC's
Raw C Functions
The raw C interfaces are CFFI-accessible by their standard UNIX names:
syslog. None of these are necessary
unless you are looking for complete control. Beware, if you mess these
calls up, you will break your Lisp process.