Logging web server events in Racket to syslog
How do you write to the system log with the Racket web server?
I'm not talking about writing debug output to the console. (I'm not the only one who's done that, right?) That's a good start, but it's ephemeral.
Nor am I talking about
Logs or it didn't happen
log. I'm talking about logging events in your sweet Racket web site to the system log. Something that persists after Racket dies. And something that can serve multiple Racket servers on a single server. Imagine that you're not actively sitting at your Racket process, manually scanning or perhaps piping output from the console to a file/socket/some other process. Rather, let's restrict our attention to the system log, what your operating system maintains for you in the background. (On many Unix-y systems, that's usually stored in places like /var/log/syslog, or something to that effect.) The system log isn't necessarily for those really low-level programs. Racket can write to the system log, too. Logging to the system log opens up the possibility of using tools like Papertrail. Being able to keep track of your Racket web app using such a tool is a real boon. How do you do that? To get started, let's keep things very simple. The topic of logging is rich, and Racket's support for logging goes well beyond what you'll see here.
How to write to the system logWhen Racket starts up, there's already a logger sitting around. It passes some events to the system log, and some get presented to the console (standard error, in a Unix-y setting). (There are probably a bunch of variations on that, and I'm afraid I don't know all of them. I work with Ubuntu Linux often, and that's the source of my examples here. It may very well be the same for many other Linux-based systems.) Here's one of the gotchas that caught me by surprise: When working on a Unix-based system, the system log, be default, is not used by Racket. Or, more precisely, the facility that would write things to the system log filters out everything given to it. You can write error messages all day long and they'll never show up in your system log. There are two ways to make sure that your log messages get sent to the system log: environment variables, and command line switches.
Environment variableThe environment variable in question is PLTSYSLOG. Invoke Racket like so:
(I'll explain in a moment what info means.) Or, more or less equivalently, you can export the variable in your shell (or in a shell script) and then invoke Racket:
env PLTSYSLOG="info" racket site.rkt
This will ensure that log events at the info level (or greater) get logged to the system log. One thing to keep in mind with the environment variable approach is that you can't tweak the initial logger settings by using
export PLTSYSLOG="info" racket site.rkt
putenv. This won't work:
The reason, as far as I can make out, is that the initial logger is, well, part of the initial setup. If you're evaluating Racket code like
(putenv "PLTSYSLOG" "debug") (log-error "not gonna happen")
putenvcalls, you're trying to ride a horse has already left the barn. Even putting that kind of code in your .racketrc file isn't going to do the job.
Command line argumentsThe -L argument is what you're looking for. Use it like this:
This accomplishes the same thing as the environment variable setup a couple paragraphs ago. (For my fellow loggers: -W is the switch that controls what kinds of logs get dumped to how standard error. I'm afraid I don't know any good mnemonic…)
racket -L "info" site.rkt
Functions to use & log levelsUse these log functions in your code:
fallback, defined like this:
As long as we've set up logging to system log to error (or lower), we'll see the message. Here, we're logging an error before sending out a response (based on oops.html, discussed in another tutorial.).
;; url? exn? -> response? (define (fallback url exn) (log-error "~a ~~~~> ~a" (url->string url) (exn-message exn)) (response #:code 500 #:mime TEXT/HTML-MIME-TYPE #:body (include-template "oops.html")))
Disabling all loggingIf you would like to disable logging to the system log, you can use the special value none as the level severity. That is:
racket -L none site.rkt
env PLTSYSERROR=none racket site.rkt
(Recall that not logging anything to the system log is—as of Racket 6.9, anyway—the default. We are here only making explicit what is implicit.)
export PLTSYSERROR=none racket site.rkt