24 Error Formatting: Error

The Error module is concerned with various tasks related to error reporting. This encompasses the following:

At boot time, the system installs a default exception handler processing all uncaught exceptions. This involves printing out the exception with the mechanisms mentioned above and executing a handler as given by the properties 'errors.toplevel' and 'errors.subordinate', which see.

24.1 Data Structures

The central data structure used in this module is the error message. The general format is as follows:

<message> ::= <message label>(
   [kind: <extended virtual string>]      % origin subsystem or component
   [msg: <extended virtual string>]      % main message
   [items: [<line>]]      % additional information
   ...)      % internal fields

All fields of the record are optional and specify information as indicated by the comments (wherever applicable). It is recommended that both kind and msg start with a lower-case letter and do not end in a period.

The label of the record is currently ignored by the procedures from the Error module, but other system modules expect it to be either error or warn, depending on the severity of the condition.

<message label> ::= error | warn

The items describe a sequence of lines meant to give additional hints about the error, but one should make sure that the error message is comprehensible without this information. All keys should start with a capital letter.

<line> ::= hint([l: <extended virtual string>]
     [m: <extended virtual string>])      % key/value pair
 | <coordinates>      % source code error relates to
 | line(<extended virtual string>)      % full line of text
 | unit      % empty line (separator)

<coordinates> ::= pos(<atom>      % file name; '' if not known
    <int>      % line number; required
    <int>)      % column number; ~1 if not known

An <extended virtual string> is a virtual string that may contain, for convenience, embedded records with special interpretation.

<extended virtual string> ::= <atom> | <int> | <float> | <string>
 | '#'(<extended virtual string> ... <extended virtual string>)
 | oz(<value>)
 | pn(<atom>)
 | <coordinates>
 | apply(<procedure or print name> [<value>])
 | list([<value><extended virtual string>)

<procedure or print name> ::= <procedure> | <atom>

The embedded records are translated to virtual strings as follows:

oz(X)

transforms X using Value.toVirtualString, using the print depth and width given by the system properties 'errors.depth' and 'errors.width', respectively.

pn(+A)

considers A to be a variable print name, i. e., escapes non-printable characters according to variable concrete syntax if A is enclosed in backquotes.

pos(+A +I +I)

prints out source coordinates, e. g., in file A, line I, column I with the unspecified parts omitted.

apply(+X +Ys)

represents an Oz application of X to Ys. Output uses the usual brace notation.

list(+Xs +ExtendedVirtualString)

outputs the values in Xs as if each was wrapped inside oz(...), inserting ExtendedVirtualString between every pair of elements.

24.2 The Error Registry

The error registry has the purpose of storing so-called error formatters under specific keys of type ``literal''. An error formatter P is a procedure with the signature

{P +ExceptionR ?MessageR}

P must be capable of processing any exception having as label any of the keys under which P has been entered into the error registry and must return a <message> as defined above describing the condition. In the case of error or system exceptions, only the record found in the dispatch field of the exception is handed to the formatter.

Error formatters may be invoked by the default exception handler installed at boot time. Possibly dealing with serious conditions, formatters are required to be robust. In particular, the handler flags the thread executing the formatter to be non-blocking, i. e., if it ever blocks on a logic variable, an exception is raised in this thread. This increases chances of any message being output at all. Note that it is allowed to block on futures though and that this flag is not inherited by any created child thread.

24.3 Example Error Formatter

The following piece of code illustrates how an error formatter might be registered and how it could behave. Assume a system component called compiler, which is given ``queries'' to process. If any query is ill-typed, an exception is raised, containing the query, the number of the ill-typed argument, and the expected argument type. Furthermore, an internal exception is raised when an internal programming assertion is violated. For robustness, an else case is included to handle any other exceptions. The formatter simply prints out the exception record, since this might help more than no output at all.

{Error.registerFormatter compiler
 fun {$ E}
    T = 'compiler engine error' 
    BugReport = 'please send a bug report to mozart-bugs@ps.uni-sb.de' 
 in 
    case E of compiler(internal X) then 
       error(kind: T
             msg: 'Internal compiler error' 
             items: [hint(l: 'Additional information' m: oz(X))
                     line(BugReport)])
    elseof compiler(invalidQuery M I A) then 
       error(kind: T
             msg: 'Ill-typed query argument' 
             items: [hint(l: 'Query' m: oz(M))
                     hint(l: 'At argument' m: I)
                     hint(l: 'Expected type' m: A)])
    
... 
    else 
       error(kind: T
             items: [line(oz(E))])
    end 
 end}

24.4 The Module

exceptionToMessage

{Error.exceptionToMessage +Exception ?Message}

constructs a <message> from an exception, using the formatters defined in the error registry or a generic formatter if none is defined for the exception. The message returned by the formatter is enriched with additional fields copied from the exception.

messageToVirtualString

{Error.messageToVirtualString +Message ?V}

converts a <message> to a virtual string using the standard layout. This can span several lines and includes the final newline.

extendedVSToVS

{Error.extendedVSToVS +ExtendedVirtualString ?V}

converts an <extended virtual string> to a <virtual string>.

printException

{Error.printException +Exception}

converts an exception to a message and this to a virtual string, printing the result on standard error (using System.printError).

registerFormatter

{Error.registerFormatter +L +P}

enters a formatter for exceptions with label L into the error registry, quietly replacing a possibly existing formatter for L.


Denys Duchier, Leif Kornstaedt, Martin Homik, Tobias Müller, Christian Schulte and Peter Van Roy
Version 1.4.0 (20080702)