I can easily set up something like:
(defvar *logger* nil)
(defun set-logger (fn) (setf *logger* fn))
(defmacro log (category &rest args)
`(when *logger* (funcall *logger* ,category ,@args))
The problem with this approach is that most logging libraries have
their main logging routine set up as a macro like this:
(defmacro log (category &rest args)
`(when (category-is-turned-on-p ,category)
(really-log ,category ,@args)))
In this way, they avoid having to evaluate the arguments when no-one
cares about the particular category:
(log :excruciating-detail (stringify-all-of-memory *gc* :pretty
t))
I can't think of a way to have the application load my network
library, then call something in my library to activate logging, and
not have my library unconditionally evaluating the arguments.
I pretty much want the user to be able to provide a function for me to
call to log things. I want this function to take two arguments, a
category and a form. I want the form to be evaluated in my library's
context if it gets evaluated at all, but I don't want it to get
evaluated before I pass it into the lambda. I don't know how to do
this.
Some trick with `&environment` and `eval`? What am I missing?
Thanks,
Patrick
(in-package :lib)
(defvar *logger* nil)
(defmacro log-it (category form &environment env)
`(when *logger* (funcall *logger* ,category ',form ,env)))
(in-package :app)
(defun app-cheesy-logger (category form env)
(when category (princ (eval form env))))
(setf *logger* #'app-cheesy-logger)
(defun try-it (a)
(lib:log-it t (incf a))
(lib:log-it nil (incf a))
a)
But... SBCL at least is complaining trying to compile the log-it macro
that "There is no MAKE-LOAD-FORM function for bootstrap type SB-
KERNEL:LEXENV". And, I have no idea how to interpret that.
Thanks,
Patrick
Anyway, for posterity:
(defpackage :lib (:use :cl) (:export :*logger* :try-it))
(in-package :lib)
(defvar *logger* nil)
(defmacro log-it (category form)
`(when *logger*
(funcall *logger* ,category #'(lambda () ,form))))
(defun try-it ()
(let ((a 0))
(log-it t (format t "log ~S" (incf a)))
(log-it nil (format t "log ~S" (incf a)))
a))
(defpackage :app (:use :cl :lib))
(in-package :app)
(defun app-log (category form-func)
(when category (funcall form-fun)))
(setf *logger* #'app-log)
(try-it)
;;; and of course, to use cl-log, the app-log function would look more
like:
(defun app-log (category form-func)
(cl-log:log-message category (funcall form-func)))