changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/log/log.lisp

changeset 698: 96958d3eb5b0
parent: c4682fedd73d
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 (in-package :log)
2 
3 (deftype log-level-designator () '(member nil :fatal :error :warn :info :debug :trace t))
4 
5 (declaim (log-level-designator *log-level*))
6 (defparameter *log-level* :debug
7  "Logging is performed dynamically based on this variable. When NIL,
8 logging is disabled, which is equivalent to a level of :FATAL. When T,
9 Logging is enabled for all levels, which is equivalent to :TRACE.")
10 
11 (defvar *logger* nil)
12 
13 (defvar *log-router* nil)
14 
15 (defvar *log-timestamp* t
16  "If non-nil, print a timestamp with log output. The value may be a
17 function in which case it is used as the function value of
18 `log-timestamp-source', or a number which will be used as the input arg to GET-REAL-TIME-SINCE.")
19 
20 (declaim (fixnum *log-indent*))
21 (defvar *log-indent* 0
22  "Level of indentation to apply to multi-line log messages.")
23 
24 (defun get-real-time-since (n)
25  "Return the numbers of seconds since a relative value offset N."
26  (- (get-internal-real-time) n))
27 
28 (defun init-log-timestamp ()
29  (setq *log-timestamp* (get-internal-real-time)))
30 
31 ;; TODO 2023-09-20: (declaim (inline log-timestamp-source)) ;; this
32 ;; probably shouldn't be inlined.. bench it
33 (defun log-timestamp-source ()
34  (typecase *log-timestamp*
35  (function (funcall *log-timestamp*))
36  (number (/ (get-real-time-since *log-timestamp*) #.internal-time-units-per-second))
37  (t (/ (get-internal-real-time) #.internal-time-units-per-second))))
38 
39 (defun universal-timestamp () (get-universal-time))
40 
41 ;; the purpose of this struct is to route log messages to the
42 ;; appropriate output stream. It should be configured and bound to
43 ;; *LOG-ROUTER*.
44 (defstruct log-router
45  fatal error warn info debug trace)
46 
47 ;; TODO 2023-09-20: make-synonym-stream, make-synonym-stream-symbol
48 (defvar *default-log-router*
49  (make-log-router
50  :fatal *error-output*
51  :error *error-output*
52  :warn *debug-io*
53  :info *terminal-io*
54  :debug *debug-io*
55  :trace *trace-output*))
56 
57 (defstruct logger
58  "The logger is responsible for intercepting log messages and either
59 printing them to a stream based on the router slot, or doing nothing
60 based on the level slot. Additionally, the appenders slot may contain
61 a list of functions taking a single log message as input. Each
62 appender in the list is called on each message intercepted wrt level."
63  (level *log-level* :type log-level-designator)
64  (timestamp *log-timestamp* :type (or boolean function number))
65  (appenders nil :type list)
66  (router *default-log-router* :type log-router))
67 
68 ;; TODO: (defmacro generate-log-profile)
69 ;; (defmacro deflogger) ;; yalog
70 ;; (defmacro with-log-profile)
71 (defmacro with-logger ((logger) &body body)
72  "Activate the specified logger for the life-time of BODY. This is
73 useful if you don't want to dynamically overwrite the *LOGGER*
74 binding."
75  `(let ((*logger* ,logger))
76  ,@body))
77 
78 (defmacro define-log-level (name &body pred)
79  "Define a log-level of NAME with PRED being the body of the predicate
80 function 'NAME-P'."
81  (let ((%name (string-upcase name)))
82  `(progn
83  (defun ,(intern (concatenate 'string %name "-P")) ()
84  ,@(or pred `((eql *log-level* ,(sb-int:keywordicate name)))))
85  (defun ,(intern (concatenate 'string %name "!")) (&rest args)
86  (when (,(symbolicate (concatenate 'string %name "-P")))
87  (format t "#:~(~A~) ~@[~f~]"
88  ',name
89  (when *log-timestamp* (log-timestamp-source)))
90  (mapc (lambda (x) (format t "; ~A~%" x)) args))
91  (if (= 1 (length args))
92  (car args)
93  args))
94  (defun ,(intern (concatenate 'string %name "-DESCRIBE")) (&rest args)
95  (,(intern (concatenate 'string %name "!")) (apply #'describe args))))))
96 
97 (define-log-level trace (or (eql *log-level* :trace) (eql *log-level* t)))
98 (define-log-level debug (or (trace-p) (eql *log-level* :debug)))
99 (define-log-level info (or (debug-p) (eql *log-level* :info)))
100 (define-log-level warn (or (info-p) (eql *log-level* :warn)))
101 (define-log-level error (or (warn-p) (eql *log-level* :error)))
102 (define-log-level fatal t) ;; probably needs to be a special case
103 
104 ;; TODO 2023-08-31: single format control string
105 ;; (defun debug! (&rest args)
106 ;; (when (debug-p)
107 ;; ;...
108 ;; ;; RESEARCH 2023-08-31: what's better here.. loop, do, mapc+nil?
109 ;; (map nil (lambda (x) (format t "~X~%" x)) args))
110 ;; args)