2012-12-12

Утилитарный Lisp

Вот как выглядит "клиент" (если для такого простого кусочка кода уместно столь громкое название) для набирающего популярность лог-сервера Graylog2 на современном Lisp'е:
(ql:quickload "usocket")
(ql:quickload "cl-json")
(ql:quickload "salza2")
(ql:quickload "babel")
(ql:quickload "local-time")
(ql:quickload "rutils")
(named-readtables:in-readtable rutil:rutils-readtable)
(defvar *hostname* nil)
(defvar *graylog-host* #(127 0 0 1))
(defvar *graylog-port* 12201)
(defun get-hostname ()
(or *hostname*
(setf *hostname* (rutil:substr (rutil:read-file "/etc/hostname") 0 -1))))
(defun unix-timestamp ()
(local-time:timestamp-to-unix (local-time:now)))
(defun graylog (message &key level backtrace file line-no)
(let (sock)
(unwind-protect
(let ((msg (salza2:compress-data
(babel:string-to-octets
(json:encode-json-to-string #{:version "1.0"
:facility "lisp"
:host (get-hostname)
:|short_message| message
:|full_message| backtrace
:timestamp (unix-timestamp)
:level level
:file file
:line line-no
})
:encoding :utf-8)
'salza2:zlib-compressor)))
(setf sock (usocket:socket-connect *graylog-host* *graylog-port*
:protocol :datagram
:element-type '(unsigned-byte 8)))
(usocket:socket-send sock msg (length msg))))
(usocket:socket-close sock)))
view raw graylog2.lisp hosted with ❤ by GitHub
По-моему, этот кусочек кода неплохо развеивает миф о проблемах с библиотеками в Lisp-среде: в нашем пайплайне сначала сообщение сериализуется в JSON библиотекой cl-json, затем кодируется в байтовый поток babel, затем зипуется salza2, а затем отправляется через UDP-шный сокет usocket. А еще есть повод использовать прекрасную библиотеку для работу со временем local-time, основанную на статье Эрика Наггума. Ну и чуть-чуть синтаксического сахара из rutils, в том числе и буквальный синтаксис для хеш-таблиц (как в Clojure), модульно подключаемый с помощью named-readtables. Ничего лишнего.