Yesterday's post discussed some rudimentary methods of having log files stored as Lisp filter themselves. Today I planned to finish that discussion and remark on how Emacs provides a powerful environment for exploratory programming. The discussion of the 3 challenge questions from yesterday turned out to be longer than I anticipated, so I'll discuss them today and make my final comments about Emacs and exploratory programming in a subsequent post.
The first question wants us to combine queries with
or rather than
and. In particular, how would you filter on
level equals SEVERE or line > 30
One way of doing this is to make the definitions
(defun level (s) (not (eq s 'SEVERE))) (defun line (l) (<= line 30))
and then use
remove-if instead of
remove-if-not to filter the records.
The second question asks how you would filter on the
method field that is part of the frame sexpr. The easiest way of doing that is to change the definition of
frame to return the truth value of the condition. For example, if you wanted to filter on
method being 'main, you could define
(defun frame (class method line) (eq method 'main))
The third question shows that we've just been fooling around so far and that what we really need is access to all the fields. Our record structure looks like
record function can't see the
line fields that are underneath the
exception node. To get a general solution we need to arrange for
record to have access to all the fields.
We start by having the leaf nodes just return their value. Thus, we define
(defalias 'date 'identity)
and similarly for
Next, we define the internal nodes
frame to return a list of their leaf values:
(defalias 'exception 'list)
and similarly for
With these definitions,
record has access to all the fields. To see what
record's arguments look like, define
list and execute one of the records. We get
("2005-02-21T18:57:39" 1109041059800 2 nil SEVERE "java.util.logging.LogManager$RootLogger" log 10 "A very very bad thing has happened!" ("java.lang.Exception" ("logtest" main 120)))
To get access to the leaves under
exception, we need some accessor functions.
(defun exception.emessage () (car exception)) (defun exception.frame () (cadr exception)) (defun exception.frame.class () (car (exception.frame))) (defun exception.frame.method () (cadr (exception.frame))) (defun exception.frame.line () (caddr (exception.frame)))
With these definitions we can filter on conditions by writing the appropriate
record function. To filter on
(level equals SEVERE or line > 30) and thread equals 10
as the third question asked, we would define
(defun record (date millis sequence logger level class method thread emessage exception) (and (or (eq level 'SEVERE) (> (exception.frame.line) 30)) (= thread 10)))
and then evaluate
(remove-if-not 'eval (cdr log-file))
Note that given some record structure, everything but the
record function is fixed so that filtering on different criteria means that only the
record function needs to be changed.
UPDATE: would → wouldn't
1 I changed the
message field of the record to
emessage so that I wouldn't have to redefine Emacs'