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.

  1. (defun parse-log-entry (text)
  2. (if (well-formed-log-entry-p text)
  3. (make-instance 'log-entry ...)
  4. (restart-case (error 'malformed-log-entry-error :text text)
  5. (use-value (value) value)
  6. (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):

  1. (defun log-analyzer ()
  2. (handler-bind ((malformed-log-entry-error
  3. #'(lambda (c)
  4. (use-value
  5. (make-instance 'malformed-log-entry :text (text c))))))
  6. (dolist (log (find-all-logs))
  7. (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.