00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 package require Tcl 8.2
00017 package provide logger 0.8
00018
00019 namespace ::logger {
00020 namespace tree {}
00021 namespace export init enable disable services servicecmd import
00022
00023
00024 variable services {}
00025
00026
00027 variable levels [list debug info notice warn error critical alert emergency]
00028
00029
00030 variable enabled "debug"
00031
00032
00033 variable RETURN_CODES [list "ok" "error" "return" "break" "continue"]
00034 }
00035
00036
00037
00038
00039
00040
00041 if {[package vcompare [package provide Tcl] 8.4] < 0} {
00042 ret ::logger::_nsExists (type ns) {
00043 expr {![catch {namespace parent $ns}]}
00044 }
00045 } else {
00046 ret ::logger::_nsExists (type ns) {
00047 namespace exists $ns
00048 }
00049 }
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 ret ::logger::_cmdPrefixExists (type prefix) {
00064 set cmd [lindex $prefix 0]
00065 set full [namespace eval :: namespace which [list $cmd]]
00066 if {[string equal $full ""]} {return 0} else {return 1}
00067 # normalize namespaces
00068 set ns [namespace qualifiers $cmd]
00069 set cmd ${ns}::[namespace tail $cmd]
00070 set matches [::info commands ${ns}::*]
00071 if {[lsearch -exact $matches $cmd] != -1} {return 1}
00072 return 0
00073 }
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 ret ::logger::walk ( type start , type code ) {
00091 set children [namespace children $start]
00092 foreach c $children {
00093 logger::walk $c $code
00094 namespace eval $c $code
00095 }
00096 }
00097
00098 ret ::logger::init (type service) {
00099 variable levels
00100 variable services
00101 variable enabled
00102
00103 # We create a 'tree' namespace to house all the services, so
00104 # they are in a 'safe' namespace sandbox, and won't overwrite
00105 # any commands.
00106 namespace eval tree::${service} {
00107 variable service
00108 variable levels
00109 variable oldname
00110 variable enabled
00111 }
00112
00113 lappend services $service
00114
00115 set [namespace current]::tree::${service}::service $service
00116 set [namespace current]::tree::${service}::levels $levels
00117 set [namespace current]::tree::${service}::oldname $service
00118 set [namespace current]::tree::${service}::enabled $enabled
00119
00120 namespace eval tree::${service} {
00121 # Callback to use when the service in question is shut down.
00122 variable delcallback [namespace current]::no-op
00123
00124 # Callback when the loglevel is changed
00125 variable levelchangecallback [namespace current]::no-op
00126
00127 # State variable to decide when to call levelcallback
00128 variable inSetLevel 0
00129
00130 # The currently configured levelcommands
00131 variable lvlcmds
00132 array set lvlcmds {}
00133
00134 # List of procedures registered via the trace command
00135 variable traceList ""
00136
00137 # Flag indicating whether or not tracing is currently enabled
00138 variable tracingEnabled 0
00139
00140 # We use this to disable a service completely. In Tcl 8.4
00141 # or greater, by using this, disabled log calls are a
00142 # no-op!
00143
00144 proc no-op args {}
00145
00146
00147 proc stdoutcmd {level text} {
00148 variable service
00149 puts "\[[clock format [clock seconds]]\] \[$service\] \[$level\] \'$text\'"
00150 }
00151
00152 proc stderrcmd {level text} {
00153 variable service
00154 puts stderr "\[[clock format [clock seconds]]\] \[$service\] \[$level\] \'$text\'"
00155 }
00156
00157
00158 # setlevel --
00159 #
00160 # This command differs from enable and disable in that
00161 # it disables all the levels below that selected, and
00162 # then enables all levels above it, which enable/disable
00163 # do not do.
00164 #
00165 # Arguments:
00166 # lv - the level, as defined in $levels.
00167 #
00168 # Side Effects:
00169 # Runs disable for the level, and then enable, in order
00170 # to ensure that all levels are set correctly.
00171 #
00172 # Results:
00173 # None.
00174
00175
00176 proc setlevel {lv} {
00177 variable inSetLevel 1
00178 set oldlvl [currentloglevel]
00179
00180 # do not allow enable and disable to do recursion
00181 if {[catch {
00182 disable $lv 0
00183 set newlvl [enable $lv 0]
00184 } msg] == 1} {
00185 return -code error -errorcode $::errorCode $msg
00186 }
00187 # do the recursion here
00188 logger::walk [namespace current] [list setlevel $lv]
00189
00190 set inSetLevel 0
00191 lvlchangewrapper $oldlvl $newlvl
00192 return
00193 }
00194
00195 # enable --
00196 #
00197 # Enable a particular 'level', and above, for the
00198 # service, and its 'children'.
00199 #
00200 # Arguments:
00201 # lv - the level, as defined in $levels.
00202 #
00203 # Side Effects:
00204 # Enables logging for the particular level, and all
00205 # above it (those more important). It also walks
00206 # through all services that are 'children' and enables
00207 # them at the same level or above.
00208 #
00209 # Results:
00210 # None.
00211
00212 proc enable {lv {recursion 1}} {
00213 variable levels
00214 set lvnum [lsearch -exact $levels $lv]
00215 if { $lvnum == -1 } {
00216 return -code error "Invalid level '$lv' - levels are $levels"
00217 }
00218
00219 variable enabled
00220 set newlevel $enabled
00221 set elnum [lsearch -exact $levels $enabled]
00222 if {($elnum == -1) || ($elnum > $lvnum)} {
00223 set newlevel $lv
00224 }
00225
00226 variable service
00227 while { $lvnum < [llength $levels] } {
00228 interp alias {} [namespace current]::[lindex $levels $lvnum] \
00229 {} [namespace current]::[lindex $levels $lvnum]cmd
00230 incr lvnum
00231 }
00232
00233 if {$recursion} {
00234 logger::walk [namespace current] [list enable $lv]
00235 }
00236 lvlchangewrapper $enabled $newlevel
00237 set enabled $newlevel
00238 }
00239
00240 # disable --
00241 #
00242 # Disable a particular 'level', and below, for the
00243 # service, and its 'children'.
00244 #
00245 # Arguments:
00246 # lv - the level, as defined in $levels.
00247 #
00248 # Side Effects:
00249 # Disables logging for the particular level, and all
00250 # below it (those less important). It also walks
00251 # through all services that are 'children' and disables
00252 # them at the same level or below.
00253 #
00254 # Results:
00255 # None.
00256
00257 proc disable {lv {recursion 1}} {
00258 variable levels
00259 set lvnum [lsearch -exact $levels $lv]
00260 if { $lvnum == -1 } {
00261 return -code error "Invalid level '$lv' - levels are $levels"
00262 }
00263
00264 variable enabled
00265 set newlevel $enabled
00266 set elnum [lsearch -exact $levels $enabled]
00267 if {($elnum > -1) && ($elnum <= $lvnum)} {
00268 if {$lvnum+1 >= [llength $levels]} {
00269 set newlevel "none"
00270 } else {
00271 set newlevel [lindex $levels [expr {$lvnum+1}]]
00272 }
00273 }
00274
00275 while { $lvnum >= 0 } {
00276
00277 interp alias {} [namespace current]::[lindex $levels $lvnum] {} \
00278 [namespace current]::no-op
00279 incr lvnum -1
00280 }
00281 if {$recursion} {
00282 logger::walk [namespace current] [list disable $lv]
00283 }
00284 lvlchangewrapper $enabled $newlevel
00285 set enabled $newlevel
00286 }
00287
00288 # currentloglevel --
00289 #
00290 # Get the currently enabled log level for this service.
00291 #
00292 # Arguments:
00293 # none
00294 #
00295 # Side Effects:
00296 # none
00297 #
00298 # Results:
00299 # current log level
00300 #
00301
00302 proc currentloglevel {} {
00303 variable enabled
00304 return $enabled
00305 }
00306
00307 # lvlchangeproc --
00308 #
00309 # Set or introspect a callback for when the logger instance
00310 # changes its loglevel.
00311 #
00312 # Arguments:
00313 # cmd - the Tcl command to call, it is called with two parameters, old and new log level.
00314 # or none for introspection
00315 #
00316 # Side Effects:
00317 # None.
00318 #
00319 # Results:
00320 # If no arguments are given return the current callback cmd.
00321
00322 proc lvlchangeproc {args} {
00323 variable levelchangecallback
00324
00325 switch -exact -- [llength [::info level 0]] {
00326 1 {return $levelchangecallback}
00327 2 {
00328 if {[::logger::_cmdPrefixExists [lindex $args 0]]} {
00329 set levelchangecallback [lindex $args 0]
00330 } else {
00331 return -code error "Invalid cmd '[lindex $args 0]' - does not exist"
00332 }
00333 }
00334 default {
00335 return -code error "Wrong # of arguments. Usage: \${log}::lvlchangeproc ?cmd?"
00336 }
00337 }
00338 }
00339
00340 proc lvlchangewrapper {old new} {
00341 variable inSetLevel
00342
00343 # we are called after disable and enable are finished
00344 if {$inSetLevel} {return}
00345
00346 # no action if level does not change
00347 if {[string equal $old $new]} {return}
00348
00349 variable levelchangecallback
00350 # no action if levelchangecallback isn't a valid command
00351 if {[::logger::_cmdPrefixExists $levelchangecallback]} {
00352 catch {
00353 uplevel \#0 [linsert $levelchangecallback end $old $new]
00354 }
00355 }
00356 }
00357
00358 # logproc --
00359 #
00360 # Command used to create a procedure that is executed to
00361 # perform the logging. This could write to disk, out to
00362 # the network, or something else.
00363 # If two arguments are given, use an existing command.
00364 # If three arguments are given, create a proc.
00365 #
00366 # Arguments:
00367 # lv - the level to log, which must be one of $levels.
00368 # args - either zero, one or two arguments.
00369 # if zero this returns the current command registered
00370 # if one, this is a cmd name that is called for this level
00371 # if two, these are an argument and proc body
00372 #
00373 # Side Effects:
00374 # Creates a logging command to take care of the details
00375 # of logging an event.
00376 #
00377 # Results:
00378 # If called with zero length args, returns the name of the currently
00379 # configured logging procedure.
00380 #
00381 #
00382
00383 proc logproc {lv args} {
00384 variable levels
00385 variable lvlcmds
00386
00387 set lvnum [lsearch -exact $levels $lv]
00388 if { ($lvnum == -1) && ($lv != "trace") } {
00389 return -code error "Invalid level '$lv' - levels are $levels"
00390 }
00391 switch -exact -- [llength $args] {
00392 0 {
00393 return $lvlcmds($lv)
00394 }
00395 1 {
00396 set cmd [lindex $args 0]
00397 if {[string equal "[namespace current]::${lv}cmd" $cmd]} {return}
00398 if {[llength [::info commands $cmd]]} {
00399 proc ${lv}cmd {args} "uplevel 1 \[list $cmd \[lindex \$args end\]\]"
00400 } else {
00401 return -code error "Invalid cmd '$cmd' - does not exist"
00402 }
00403 set lvlcmds($lv) $cmd
00404 }
00405 2 {
00406 foreach {arg body} $args {break}
00407 proc ${lv}cmd {args} "_setservicename \$args;
00408 set val \[${lv}customcmd \[lindex \$args end\]\] ;
00409 _restoreservice; set val"
00410 proc ${lv}customcmd $arg $body
00411 set lvlcmds($lv) [namespace current]::${lv}customcmd
00412 }
00413 default {
00414 return -code error "Usage: \${log}::logproc level ?cmd?\nor \${log}::logproc level argname body"
00415 }
00416 }
00417 }
00418
00419
00420 # delproc --
00421 #
00422 # Set or introspect a callback for when the logger instance
00423 # is deleted.
00424 #
00425 # Arguments:
00426 # cmd - the Tcl command to call.
00427 # or none for introspection
00428 #
00429 # Side Effects:
00430 # None.
00431 #
00432 # Results:
00433 # If no arguments are given return the current callback cmd.
00434
00435 proc delproc {args} {
00436 variable delcallback
00437
00438 switch -exact -- [llength [::info level 0]] {
00439 1 {return $delcallback}
00440 2 { if {[::logger::_cmdPrefixExists [lindex $args 0]]} {
00441 set delcallback [lindex $args 0]
00442 } else {
00443 return -code error "Invalid cmd '[lindex $args 0]' - does not exist"
00444 }
00445 }
00446 default {
00447 return -code error "Wrong # of arguments. Usage: \${log}::delproc ?cmd?"
00448 }
00449 }
00450 }
00451
00452
00453 # delete --
00454 #
00455 # Delete the namespace and its children.
00456
00457 proc delete {} {
00458 variable delcallback
00459 variable service
00460
00461 logger::walk [namespace current] delete
00462 if {[::logger::_cmdPrefixExists $delcallback]} {
00463 uplevel \#0 [lrange $delcallback 0 end]
00464 }
00465 # clean up the global services list
00466 set idx [lsearch -exact [logger::services] $service]
00467 if {$idx !=-1} {
00468 set ::logger::services [lreplace [logger::services] $idx $idx]
00469 }
00470
00471 namespace delete [namespace current]
00472
00473 }
00474
00475 # services --
00476 #
00477 # Return all child services
00478
00479 proc services {} {
00480 variable service
00481
00482 set children [list]
00483 foreach srv [logger::services] {
00484 if {[string match "${service}::*" $srv]} {
00485 lappend children $srv
00486 }
00487 }
00488 return $children
00489 }
00490
00491 # servicename --
00492 #
00493 # Return the name of the service
00494
00495 proc servicename {} {
00496 variable service
00497 return $service
00498 }
00499
00500 proc _setservicename {arg} {
00501 variable service
00502 variable oldname
00503 if {[llength $arg] <= 1} {
00504 return
00505 } else {
00506 set oldname $service
00507 set service [lindex $arg end-1]
00508 }
00509 }
00510
00511 proc _restoreservice {} {
00512 variable service
00513 variable oldname
00514 set service $oldname
00515 return
00516 }
00517
00518 proc trace { action args } {
00519 variable service
00520
00521 # Allow other boolean values (true, false, yes, no, 0, 1) to be used
00522 # as synonymns for "on" and "off".
00523
00524 if {[string is boolean $action]} {
00525 set xaction [expr {($action && 1) ? "on" : "off"}]
00526 } else {
00527 set xaction $action
00528 }
00529
00530 # Check for required arguments for actions/subcommands and dispatch
00531 # to the appropriate procedure.
00532
00533 switch -- $xaction {
00534 "status" {
00535 return [uplevel 1 [list logger::_trace_status $service $args]]
00536 }
00537 "on" {
00538 if {[llength $args]} {
00539 return -code error "wrong # args: should be \"trace on\""
00540 }
00541 return [logger::_trace_on $service]
00542 }
00543 "off" {
00544 if {[llength $args]} {
00545 return -code error "wrong # args: should be \"trace off\""
00546 }
00547 return [logger::_trace_off $service]
00548 }
00549 "add" {
00550 if {![llength $args]} {
00551 return -code error \
00552 "wrong # args: should be \"trace add ?-ns? <proc> ...\""
00553 }
00554 return [uplevel 1 [list ::logger::_trace_add $service $args]]
00555 }
00556 "remove" {
00557 if {![llength $args]} {
00558 return -code error \
00559 "wrong # args: should be \"trace remove ?-ns? <proc> ...\""
00560 }
00561 return [uplevel 1 [list ::logger::_trace_remove $service $args]]
00562 }
00563
00564 default {
00565 return -code error \
00566 "Invalid action \"$action\": must be status, add, remove,\
00567 on, or off"
00568 }
00569 }
00570 }
00571
00572 # Walk the parent service namespaces to see first, if they
00573 # exist, and if any are enabled, and then, as a
00574 # consequence, enable this one
00575 # too.
00576
00577 enable $enabled
00578 variable parent [namespace parent]
00579 while {[string compare $parent "::logger::tree"]} {
00580 # If the 'enabled' variable doesn't exist, create the
00581 # whole thing.
00582 if { ! [::info exists ${parent}::enabled] } {
00583
00584 logger::init [string range $parent 16 end]
00585 }
00586 set enabled [set ${parent}::enabled]
00587 enable $enabled
00588 set parent [namespace parent $parent]
00589 }
00590 }
00591
00592 # Now create the commands for different levels.
00593
00594 namespace eval tree::${service} {
00595 set parent [namespace parent]
00596
00597 # We 'inherit' the commands from the parents. This
00598 # means that, if you want to share the same methods with
00599 # children, they should be instantiated after the parent's
00600 # methods have been defined.
00601 if {[string compare $parent "::logger::tree"]} {
00602 foreach lvl [::logger::levels] {
00603 # OPTIMIZE: do not allow multiple aliases in the hierarchy
00604 # they can always be replaced by more efficient
00605 # direct aliases to the target procs.
00606 interp alias {} [namespace current]::${lvl}cmd {} ${parent}::${lvl}cmd $service
00607 }
00608 # inherit the starting loglevel of the parent service
00609 setlevel [${parent}::currentloglevel]
00610
00611 } else {
00612 foreach lvl [concat [::logger::levels] "trace"] {
00613 proc ${lvl}cmd {args} "_setservicename \$args ;
00614 set val \[stdoutcmd $lvl \[lindex \$args end\]\] ;
00615 _restoreservice; set val"
00616 set lvlcmds($lvl) [namespace current]::${lvl}cmd
00617 }
00618 }
00619 }
00620
00621
00622 return ::logger::tree::${service}
00623 }
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638 ret ::logger::services () {
00639 variable services
00640 return $services
00641 }
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659 ret ::logger::enable (type lv) {
00660 variable services
00661 if {[catch {
00662 foreach sv $services {
00663 ::logger::tree::${sv}::enable $lv
00664 }
00665 } msg] == 1} {
00666 return -code error -errorcode $::errorCode $msg
00667 }
00668 }
00669
00670 ret ::logger::disable (type lv) {
00671 variable services
00672 if {[catch {
00673 foreach sv $services {
00674 ::logger::tree::${sv}::disable $lv
00675 }
00676 } msg] == 1} {
00677 return -code error -errorcode $::errorCode $msg
00678 }
00679 }
00680
00681 ret ::logger::setlevel (type lv) {
00682 variable services
00683 variable enabled
00684 variable levels
00685 if {[lsearch -exact $levels $lv] == -1} {
00686 return -code error "Invalid level '$lv' - levels are $levels"
00687 }
00688 set enabled $lv
00689 if {[catch {
00690 foreach sv $services {
00691 ::logger::tree::${sv}::setlevel $lv
00692 }
00693 } msg] == 1} {
00694 return -code error -errorcode $::errorCode $msg
00695 }
00696 }
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 ret ::logger::levels () {
00714 variable levels
00715 return $levels
00716 }
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731 ret ::logger::servicecmd (type service) {
00732 variable services
00733 if {[lsearch -exact $services $service] == -1} {
00734 return -code error "Service \"$service\" does not exist."
00735 }
00736 return "::logger::tree::${service}"
00737 }
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752 ret ::logger::import (type args) {
00753 variable services
00754
00755 if {[llength $args] == 0 || [llength $args] > 7} {
00756 return -code error "Wrong # of arguments: \"logger::import ?-all?\
00757 ?-force?\
00758 ?-prefix prefix? ?-namespace namespace? service\""
00759 }
00760
00761 # process options
00762 #
00763 set import_all 0
00764 set force 0
00765 set prefix ""
00766 set ns [uplevel 1 namespace current]
00767 while {[llength $args] > 1} {
00768 set opt [lindex $args 0]
00769 set args [lrange $args 1 end]
00770 switch -exact -- $opt {
00771 -all { set import_all 1}
00772 -prefix { set prefix [lindex $args 0]
00773 set args [lrange $args 1 end]
00774 }
00775 -namespace {
00776 set ns [lindex $args 0]
00777 set args [lrange $args 1 end]
00778 }
00779 -force {
00780 set force 1
00781 }
00782 default {
00783 return -code error "Unknown argument: \"$opt\" :\nUsage:\
00784 \"logger::import ?-all? ?-force?\
00785 ?-prefix prefix? ?-namespace namespace? service\""
00786 }
00787 }
00788 }
00789
00790 #
00791 # build the list of commands to import
00792 #
00793
00794 set cmds [logger::levels]
00795 lappend cmds "trace"
00796 if {$import_all} {
00797 lappend cmds setlevel enable disable logproc delproc services
00798 lappend cmds servicename currentloglevel delete
00799 }
00800
00801 #
00802 # check the service argument
00803 #
00804
00805 set service [lindex $args 0]
00806 if {[lsearch -exact $services $service] == -1} {
00807 return -code error "Service \"$service\" does not exist."
00808 }
00809
00810 #
00811 # setup the namespace for the import
00812 #
00813
00814 set sourcens [logger::servicecmd $service]
00815 set localns [uplevel 1 namespace current]
00816
00817 if {[string match ::* $ns]} {
00818 set importns $ns
00819 } else {
00820 set importns ${localns}::$ns
00821 }
00822
00823 # fake namespace exists for Tcl 8.2 - 8.3
00824 if {![_nsExists $importns]} {
00825 namespace eval $importns {}
00826 }
00827
00828
00829 #
00830 # prepare the import
00831 #
00832
00833 set imports ""
00834 foreach cmd $cmds {
00835 set cmdname ${importns}::${prefix}$cmd
00836 set collision [llength [info commands $cmdname]]
00837 if {$collision && !$force} {
00838 return -code error "can't import command \"$cmdname\": already exists"
00839 }
00840 lappend imports ${importns}::${prefix}$cmd ${sourcens}::${cmd}
00841 }
00842
00843 #
00844 # and execute the aliasing after checking all is well
00845 #
00846
00847 foreach {target source} $imports {
00848 proc $target {args} "uplevel 1 \[linsert \$args 0 $source \]"
00849 }
00850 }
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868 ret ::logger::initNamespace (type ns , optional level =warn) {
00869 set service [string trimleft $ns :]
00870 namespace eval $ns [list ::logger::init $service]
00871 namespace eval $ns [list ::logger::import -force -all -namespace log $service]
00872 namespace eval $ns [list log::setlevel $level]
00873 return
00874 }
00875
00876
00877
00878
00879
00880
00881 ret ::logger::_trace_status ( type service , type procList ) {
00882 upvar #0 ::logger::tree::${service}::traceList traceList
00883
00884 # If no procedure names were given, just return the registered list
00885
00886 if {![llength $procList]} {
00887 return $traceList
00888 }
00889
00890 # Get caller's namespace for qualifying unqualified procedure names
00891
00892 set caller_ns [uplevel 1 namespace current]
00893 set caller_ns [string trimright $caller_ns ":"]
00894
00895 # Search for any specified proc names that are *not* registered
00896
00897 foreach procName $procList {
00898 # Make sure the procedure namespace is qualified
00899
00900 if {![string match "::*" $procName]} {
00901 set procName ${caller_ns}::$procName
00902 }
00903
00904 # Check if the procedure has been registered for tracing
00905
00906 if {[lsearch -exact $traceList $procName] == -1} {
00907 return 0
00908 }
00909 }
00910
00911 return 1
00912 }
00913
00914
00915
00916
00917
00918
00919 ret ::logger::_trace_on ( type service ) {
00920 set tcl_version [package provide Tcl]
00921
00922 if {[package vcompare $tcl_version "8.4"] < 0} {
00923 return -code error \
00924 "execution tracing is not available in Tcl $tcl_version"
00925 }
00926
00927 namespace eval ::logger::tree::${service} {
00928 if {!$tracingEnabled} {
00929 set tracingEnabled 1
00930 ::logger::_enable_traces $service $traceList
00931 }
00932 }
00933
00934 return 1
00935 }
00936
00937
00938
00939
00940
00941
00942
00943 ret ::logger::_trace_off ( type service ) {
00944 namespace eval ::logger::tree::${service} {
00945 if {$tracingEnabled} {
00946 ::logger::_disable_traces $service $traceList
00947 set tracingEnabled 0
00948 }
00949 }
00950
00951 return 1
00952 }
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963 ret ::logger::_trace_get_proclist ( type inputList ) {
00964 set procList ""
00965
00966 if {[string equal [lindex $inputList 0] "-ns"]} {
00967 # Verify that at least one target namespace was supplied
00968
00969 set inputList [lrange $inputList 1 end]
00970 if {![llength $inputList]} {
00971 return -code error "Must specify at least one namespace target"
00972 }
00973
00974 # Rebuild the argument list to contain namespace procedures
00975
00976 foreach namespace $inputList {
00977 # Don't allow tracing of the logger (or child) namespaces
00978
00979 if {![string match "::logger::*" $namespace]} {
00980 set nsProcList [::info procs ${namespace}::*]
00981 set procList [concat $procList $nsProcList]
00982 }
00983 }
00984 } else {
00985 # Search for procs or namespaces matching each of the specified
00986 # patterns.
00987
00988 foreach pattern $inputList {
00989 set matches [uplevel 1 ::info proc $pattern]
00990
00991 if {![llength $matches]} {
00992 if {[uplevel 1 namespace exists $pattern]} {
00993 set matches [::info procs ${pattern}::*]
00994 }
00995
00996 # Matched procs will be qualified due to above pattern
00997
00998 set procList [concat $procList $matches]
00999 } elseif {[string match "::*" $pattern]} {
01000 # Patterns were pre-qualified - add them directly
01001
01002 set procList [concat $procList $matches]
01003 } else {
01004 # Qualify each proc with the namespace it was in
01005
01006 set ns [uplevel 1 namespace current]
01007 if {$ns == "::"} {
01008 set ns ""
01009 }
01010 foreach proc $matches {
01011 lappend procList ${ns}::$proc
01012 }
01013 }
01014 }
01015 }
01016
01017 return $procList
01018 }
01019
01020
01021
01022
01023
01024
01025
01026 ret ::logger::_trace_add ( type service , type procList ) {
01027 upvar #0 ::logger::tree::${service}::traceList traceList
01028
01029 # Handle -ns switch and glob search patterns for procedure names
01030
01031 set procList [uplevel 1 [list logger::_trace_get_proclist $procList]]
01032
01033 # Enable tracing for each procedure that has not previously been
01034 # specified via logger::trace add. If tracing is off, this will just
01035 # store the name of the procedure for later when tracing is turned on.
01036
01037 foreach procName $procList {
01038 if {[lsearch -exact $traceList $procName] == -1} {
01039 lappend traceList $procName
01040 ::logger::_enable_traces $service [list $procName]
01041 }
01042 }
01043 }
01044
01045
01046
01047
01048
01049
01050
01051 ret ::logger::_trace_remove ( type service , type procList ) {
01052 upvar #0 ::logger::tree::${service}::traceList traceList
01053
01054 # Handle -ns switch and glob search patterns for procedure names
01055
01056 set procList [uplevel 1 [list logger::_trace_get_proclist $procList]]
01057
01058 # Disable tracing for each proc that previously had been specified
01059 # via logger::trace add. If tracing is off, this will just
01060 # remove the name of the procedure from the trace list so that it
01061 # will be excluded when tracing is turned on.
01062
01063 foreach procName $procList {
01064 set index [lsearch -exact $traceList $procName]
01065 if {$index != -1} {
01066 set traceList [lreplace $traceList $index $index]
01067 ::logger::_disable_traces $service [list $procName]
01068 }
01069 }
01070 }
01071
01072
01073
01074
01075
01076
01077 ret ::logger::_enable_traces ( type service , type procList ) {
01078 upvar #0 ::logger::tree::${service}::tracingEnabled tracingEnabled
01079
01080 if {$tracingEnabled} {
01081 foreach procName $procList {
01082 ::trace add execution $procName enter \
01083 [list ::logger::_trace_enter $service]
01084 ::trace add execution $procName leave \
01085 [list ::logger::_trace_leave $service]
01086 }
01087 }
01088 }
01089
01090
01091
01092
01093
01094
01095 ret ::logger::_disable_traces ( type service , type procList ) {
01096 upvar #0 ::logger::tree::${service}::tracingEnabled tracingEnabled
01097
01098 if {$tracingEnabled} {
01099 foreach procName $procList {
01100 ::trace remove execution $procName enter \
01101 [list ::logger::_trace_enter $service]
01102 ::trace remove execution $procName leave \
01103 [list ::logger::_trace_leave $service]
01104 }
01105 }
01106 }
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116 ret ::logger::_trace_enter ( type service , type cmd , type op ) {
01117 # Parse the command
01118 set procName [uplevel 1 namespace origin [lindex $cmd 0]]
01119 set args [lrange $cmd 1 end]
01120
01121 # Display the message prefix
01122 set callerLvl [expr {[::info level] - 1}]
01123 set calledLvl [::info level]
01124
01125 lappend message "proc" $procName
01126 lappend message "level" $calledLvl
01127 lappend message "script" [uplevel ::info script]
01128
01129 # Display the caller information
01130 set caller ""
01131 if {$callerLvl >= 1} {
01132 # Display the name of the caller proc w/prepended namespace
01133 catch {
01134 set callerProcName [lindex [::info level $callerLvl] 0]
01135 set caller [uplevel 2 namespace origin $callerProcName]
01136 }
01137 }
01138
01139 lappend message "caller" $caller
01140
01141 # Display the argument names and values
01142 set argSpec [uplevel 1 ::info args $procName]
01143 set argList ""
01144 if {[llength $argSpec]} {
01145 foreach argName $argSpec {
01146 lappend argList $argName
01147
01148 if {$argName == "args"} {
01149 lappend argList $args
01150 break
01151 } else {
01152 lappend argList [lindex $args 0]
01153 set args [lrange $args 1 end]
01154 }
01155 }
01156 }
01157
01158 lappend message "procargs" $argList
01159 set message [list $op $message]
01160
01161 ::logger::tree::${service}::tracecmd $message
01162 }
01163
01164
01165
01166
01167
01168 ret ::logger::_trace_leave ( type service , type cmd , type status , type rc , type op ) {
01169 variable RETURN_CODES
01170
01171 # Parse the command
01172 set procName [uplevel 1 namespace origin [lindex $cmd 0]]
01173
01174 # Gather the caller information
01175 set callerLvl [expr {[::info level] - 1}]
01176 set calledLvl [::info level]
01177
01178 lappend message "proc" $procName "level" $calledLvl
01179 lappend message "script" [uplevel ::info script]
01180
01181 # Get the name of the proc being returned to w/prepended namespace
01182 set caller ""
01183 catch {
01184 set callerProcName [lindex [::info level $callerLvl] 0]
01185 set caller [uplevel 2 namespace origin $callerProcName]
01186 }
01187
01188 lappend message "caller" $caller
01189
01190 # Convert the return code from numeric to verbal
01191
01192 if {$status < [llength $RETURN_CODES]} {
01193 set status [lindex $RETURN_CODES $status]
01194 }
01195
01196 lappend message "status" $status
01197 lappend message "result" $rc
01198
01199 # Display the leave message
01200
01201 set message [list $op $message]
01202 ::logger::tree::${service}::tracecmd $message
01203
01204 return 1
01205 }
01206
01207