Providing Multiple Restarts
Since restarts must be explicitly invoked to have any effect, you can define multiple restarts, each providing a different recovery strategy. As I mentioned earlier, not all log-parsing applications will necessarily want to skip malformed entries. Some applications might want parse-log-file
to include a special kind of object representing malformed entries in the list of log-entry
objects; other applications may have some way to repair a malformed entry and may want a way to pass the fixed entry back to parse-log-entry
.
To allow more complex recovery protocols, restarts can take arbitrary arguments, which are passed in the call to **INVOKE-RESTART**
. You can provide support for both the recovery strategies I just mentioned by adding two restarts to parse-log-entry
, each of which takes a single argument. One simply returns the value it’s passed as the return value of parse-log-entry
, while the other tries to parse its argument in the place of the original log entry.
(defun parse-log-entry (text)
(if (well-formed-log-entry-p text)
(make-instance 'log-entry ...)
(restart-case (error 'malformed-log-entry-error :text text)
(use-value (value) value)
(reparse-entry (fixed-text) (parse-log-entry fixed-text)))))
The name **USE-VALUE**
is a standard name for this kind of restart. Common Lisp defines a restart function for **USE-VALUE**
similar to the skip-log-entry
function you just defined. So, if you wanted to change the policy on malformed entries to one that created an instance of malformed-log-entry
, you could change log-analyzer
to this (assuming the existence of a malformed-log-entry
class with a :text
initarg):
(defun log-analyzer ()
(handler-bind ((malformed-log-entry-error
#'(lambda (c)
(use-value
(make-instance 'malformed-log-entry :text (text c))))))
(dolist (log (find-all-logs))
(analyze-log log))))
You could also have put these new restarts into parse-log-file
instead of parse-log-entry
. However, you generally want to put restarts in the lowest-level code possible. It wouldn’t, though, be appropriate to move the skip-log-entry
restart into parse-log-entry
since that would cause parse-log-entry
to sometimes return normally with **NIL**
, the very thing you started out trying to avoid. And it’d be an equally bad idea to remove the skip-log-entry
restart on the theory that the condition handler could get the same effect by invoking the use-value
restart with **NIL**
as the argument; that would require the condition handler to have intimate knowledge of how the parse-log-file
works. As it stands, the skip-log-entry
is a properly abstracted part of the log-parsing API.