'''errx''' The idea of errx came to me as I was enerved by debugging puts. It resembles a bit of the BSD ''errx/warnx'' pair, although it combines both functionalities into one function. Furthermore it offers something like a debuggers '''bt''' , i.e. backtrace. It is not yet finished, but I use it daily already, although the more fancy things (like the bt) are not yet fully finished or even nonexistant. ''Martin Weber'' [mailto:Ephaeton@gmx.net] ---- Make it simple, paste it here - code on a wiki page, with the ensuing discussions, is more attractive and pedagogical than a URL to download a tar.gz bundle... ([RS] ;-) ---- In fact I had exactly that in mind - errx itself is lean, but I am using it in a bundle with some other stuff - bgerror, and some other utility stuff which I've packed into ::Error ... gonna present errx here and offer the rest for download :) ----- And here we go. This is the factored out errx which I already use. I have cut out about anything which is not yet finished. ----- # When this is sourced, set errchann and debugging if not set # to sane values. if {![info exists ::errchann]} { set ::errchann stderr } if {![info exists ::debugging]} { set ::debugging 0 } proc errx { txt {flags {info debug}} } { global argv0 debugging errchann errorInfo lastargs if {![string length $errchann]} { return } if {![info exists debugging]} { set debugging 0 } # if !debugging immediate return when flags contain debug if {([set dbgpos [lsearch $flags debug]] != -1) && !$debugging} { return } else { set flags [lreplace $flags $dbgpos $dbgpos] } upvar _DEBUG _debug if {![info exists _debug]} { set _debug 0 } if {([set dbgpos [lsearch $flags DEBUG]] != -1) && !$_debug} { return } else { set flags [lreplace $flags $dbgpos $dbgpos] } if {[info level]==1} { ;# caller is global! set caller "*global*" set verbose "" } else { set caller [lindex [info level -1] 0] set verbose "$argv0:\[<$caller>\] Called as: [info level -1]" } if {[set flp [lsearch $flags time]]!= -1} { set intro "$argv0@([clock format [clock seconds]]):\[<$caller>\]" set flags [lreplace $flags $flp $flp] } else { set intro "$argv0:\[<$caller>\]" } switch -- $flags { sparse { puts $errchann "$txt" } "" - debug { ;# this shouldn't happen, but can if two debug flags are given. puts $errchann "$intro: $txt" } info { puts $errchann "$intro: $txt" } warning { puts $errchann "$intro: $txt"} error { puts $errchann "$intro: $txt\n$verbose" ; exit 1 } critical { puts $errchann "$intro: ** CRITICAL ERROR ! **" if {[info exists errorInfo]} { puts $errchann " -- $errorInfo" } if {[info exists errorCode]} { puts $errchann " -- $errorCode" } for {set lv [expr [info level] -1 ]} { $lv } { incr lv -1 } { puts $errchann "================================================================================" puts $errchann " Level $lv, [info level $lv]." puts $errchann " Local variables:" uplevel #$lv { foreach v [info locals] { upvar #[info level] $v _v; puts -nonewline $::errchann "$v=$_v\t" } } puts $errchann "\n" } exit 1 } default { errx "Invalid flags given to errx ($flags)!" error } } return } ----- Its strong points are mainly that you can switch messages on and off, and even use for non debugging, but warning or informational purposes. If the variable ''_DEBUG'' is set in the calling environment, and set to 1 (or true), then the '''DEBUG''' token works. If the global variable ''debugging'' is set, then the '''debug''' token works. (works in the sense like "the debug '''modifier''' works", see the first and third example) In general you'd use it like follows: # if {$_DEBUG} in calling environment, output msg errx "local-var-I-want-to-know is $bla" {info DEBUG} # output a unconditionally errx "I just deleted my harddisk!" warning # if {$::debugging}, output a msg errx "Hmm, environment doesn't seem sane..." debug # Output a error along with stack backtrace, inspection # of local variables up the stack and a timestamp, # exit the application. errx "YIKES! THE ALIENS ARE COMING!" {critical time} # Kill the application with your error message errx "hmm, something went wildly wrong." error What I like about it is that it displays the caller and the script for which it works, normally with 'puts' you do all the same all the time: puts stderr "this-proc, that-var is $bla @ [clock format [clock seconds]" Furthermore, when you switch your code to use a global routine like that, can easily say something like: set ::debugging 1 set ::errchann [open debug.log {WRONLY CREAT TRUNC}] and collect all information from it at a central place... ----- Once I finished errx to the like I want it, I'll put up the tar.gz I mentioned earlier. Included in this Error module is a facility for bgerror handling (installing/reinstalling of handlers etc.), setting of debug - levels (offers control of verbosity on a global level, too), Switching of errchann with reinstalling the old one, a predefined bgerror for use with event-driven '''tcl''' applications, and concise documentation. Things errx is going to learn still: * norecord, record Record error messages in an internal stack for use if errchann is unset * replay Get the messages from the stack * rdr "redirect", Report grandparent-caller, not parent-environment (For use with ''replay'') * ml or multiline Better handling for multiline messages [ probably automatic ] * stack Just display a stack trace * vars Display callers environment (info locals) * filtered stack trace with help of errorInfo Consider the following: % proc x1 {} { puts bla puts blah x2 puts blabla } % proc x2 {} { puts banzai error ayaken puts dead } % catch x1 zack % puts $zack ayaken At this place, in errorInfo a nice stacktrace is in. It contains line numbers, too, which we can re-transform into code by the following: % lindex [split [info body ] "\n"] [expr - 1] Or come up with a bit of environment in the calling function easily with % lrange [split [info body ] "\n"] [expr -3] [expr +2] I like the idea, gonna put it into the Error module I'll end up with... - these are to be combined, too ... if you have further suggestions, well, this is a wiki :) Go ahead. ''If you wonder what for all that junk, the ultimate goal is to create something you can distribute with your application and which hides debug messages from the user. If he has a serious problem, he can send a problem report which included all kinds of debug messages collected internally, along with stack traces, local variables etc. etc.'' ----- ''More to come, -Martin'' '''4/9/2002''': Nice! The one point I'd like to raise, is that the arg expansion of Tcl makes it unattractive to leave lots of errx calls in (just like various flavors of "assert"). Example: errx "time needed for 1000 calls is [time {mycall arg} 1000]" debug That statement is going to take time even when debugging is turned off. Slightly less convenient, but a way to solve this, is to use ''uplevel'' inside errx so calls can be rewritten as: errx {time needed for 1000 calls is [time {mycall arg} 1000]} debug It ain't perfect, but at least this way one can leave all the calls in and have them take relatively little time (even less if errx gets redefined to "prox errx args {}" when pressed for time). Would this approach be an idea worth considering for errx? -[jcw] ---- '''05/09/2002''' ''CET :)'' Yes, you got a point there. In fact, I have not used errx's arguments up to now to carry "expensive" operations like the one you mentioned, although I do think your point is valid. I tried to toy around with something like interp alias {} errx {} \;\# to turn it off when there is no debugging, but that didn't work. The solution you suggest is quite worth adding, and I will do it later - und update this page once I've changed errx. ''-Martin'' ----