timestamp log issues
[tcl-tlc-base.git] / scripts / baselog.itcl
blobcfc87434eb24ec349a72074504f6f5604d2dae6c
1 # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4
3 class tlc::Baselog {
4 public {
5 variable baselog_threshold ""
6 variable baselog_instancename ""
9 protected {
10 variable debuglogmode 0
12 method log {lvl {msg ""} args}
13 method parray {args}
16 private {
17 common lvlmap
18 common classmap
19 common hotfuncs
20 common using_hotfuncs 0
21 common remote
22 common redir
23 common helpers
25 method c {args}
28 array set lvlmap {
29 trivia 5
30 debug 10
31 notify 20
32 notice 20
33 warning 30
34 warn 30
35 error 40
36 fatal 50
39 array set helpers {}
40 array set classmap {}
41 array set hotfuncs {}
43 proc remote_logging {ip port} {
44 set remote [socket $ip $port]
45 fconfigure $remote -translation binary -encoding binary -blocking 1 -buffering none
48 proc load_classmap {fn} {
49 if {[catch {set fp [open $fn r]} errmsg]} {
50 error "Problem opening classmap file: $errmsg"
52 set dat [read $fp]
53 close $fp
55 if {[catch {
56 array set tmp [tlc::decomment $dat]
57 } errmsg]} {
58 error "Error parsing classmap: should be a list of key value pairs, optionally commented"
61 foreach {key val} [array get tmp] {
62 if {[info exists lvlmap($val)]} {
63 set val $lvlmap($val)
65 if {![string is integer -strict $val]} {
66 error "Log threshold for $key is not an integer or a valid symbolic level ($val)"
68 set classmap($key) $val
72 proc load_hotfuncs {fn} {
73 if {[catch {set fp [open $fn r]} errmsg]} {
74 error "Problem opening hotfuncs file: $errmsg"
76 set dat [read $fp]
77 close $fp
79 if {[catch {
80 array set tmp [tlc::decomment $dat]
81 } errmsg]} {
82 error "Error parsing hotfuncs: should be a list of key value pairs, optionally commented"
85 foreach {key val} [array get tmp] {
86 if {[info exists lvlmap($val)]} {
87 set val $lvlmap($val)
89 if {![string is integer -strict $val]} {
90 error "Log threshold for $key is not an integer or a valid symbolic level ($val)"
92 set hotfuncs($key) $val
95 set using_hotfuncs 1
98 proc timestamp_function {func} {
99 array unset helpers timestamp
100 return
101 if {$func == ""} {
102 array unset helpers timestamp
103 } else {
104 set helpers(timestamp) $func
108 proc timestamp {} {
109 return [clock microseconds]
112 proc output_function {func} {
113 set helpers(output) $func
116 proc redir_output {cb} {
117 set redir $cb
122 body tlc::Baselog::log {lvl {msg ""} args} { #<<<1
123 if {$debuglogmode} {puts stderr "foo"}
124 # Get a timestamp <<<
125 set now [clock microseconds]
126 #if {[info exists helpers(timestamp)]} {
127 # set cmd [linsert [lrange $helpers(timestamp) 1 end] 0 \
128 # [lindex $helpers(timestamp) 0]]
129 # set now [uplevel #0 $cmd]
130 #} else {
131 # if {[package vsatisfies [info tclversion] 8.5]} {
132 # set now [clock microseconds]
133 # } else {
134 # set now [expr {[clock seconds] * 1000000}]
137 # Get a timestamp >>>
139 array set switches {
140 -suppressed {}
142 array set switches $args
143 if {[info exists switches(-suppress)]} {
144 set switches(-suppressed) $switches(-suppress)
146 if {![string is integer -strict $lvl]} {
147 if {![info exists lvlmap($lvl)]} {
148 set lvl 10
149 } else {
150 set lvl $lvlmap($lvl)
154 set caller_inf [info level -1]
155 set name [lindex $caller_inf 0]
156 set passed_args [lrange $caller_inf 1 end]
157 if {$name == {}} {
158 set fqname "<unknown>"
159 set caller_args_def {}
160 for {set i 1} {$i <= [llength $passed_args]} {incr i} {
161 lappend caller_args_def "<unknown$i>"
163 } else {
164 set fqname [uplevel 1 [list namespace origin $name]]
165 if {$name eq "namespace"} {
166 set caller_args_def {}
167 } else {
168 set caller_args_def [uplevel 1 [list info args $name]]
172 set logzone [namespace qualifiers $fqname]
174 if {$debuglogmode} {puts stderr "bar"}
175 if {[info exists helpers(output)]} {
176 set output_cmd [linsert [lrange $helpers(output) 1 end] 0 \
177 [lindex $helpers(output) 0]]
179 set ns [namespace qualifiers $logzone]
180 set class [namespace tail $logzone]
181 set method [namespace tail $fqname]
183 set substmap [list "\n" "%n" "\t" "%t" "%" "%%"]
184 set argdesc {}
185 set idx 0
186 foreach arg $caller_args_def {
187 if {$arg == "args"} {
188 set this_passed_arg [lrange $passed_args $idx end]
189 } else {
190 set this_passed_arg [lindex $passed_args $idx]
193 set alen [string length $this_passed_arg]
194 if {$arg in $switches(-suppressed)} {
195 set this_passed_arg "#<suppressed>"
196 set this_passed_arg_type [list info suppressed]
197 } elseif {$this_passed_arg eq ""} {
198 set this_passed_arg "{}"
199 set this_passed_arg_type [list info blank]
200 } elseif {$alen > 23} {
201 set this_passed_arg [string range $this_passed_arg 0 21]
202 set this_passed_arg [string map $substmap $this_passed_arg]
203 set this_passed_arg_type [list value_trunc $alen]
204 } elseif {![string is print $this_passed_arg]} {
205 set this_passed_arg "#<nonprint($alen)>"
206 set this_passed_arg_type [list info nonprint]
207 } else {
208 set this_passed_arg [string map $substmap $this_passed_arg]
209 set this_passed_arg_type [list value_whole $alen]
212 lappend argdesc [list $arg $this_passed_arg $this_passed_arg_type]
213 incr idx
216 #uplevel #0 [list {*}$helpers(output) $now $baselog_instancename $ns $class $method $argdesc $lvl $msg [tlc::build_stackdump]]
217 uplevel #0 [list {*}$helpers(output) $now $baselog_instancename $ns $class $method $argdesc $lvl $msg]
218 } else {
219 if {$baselog_threshold != ""} {
220 set threshold $baselog_threshold
221 } elseif {[info exists classmap($logzone)]} {
222 set threshold $classmap($logzone)
223 } else {
224 set threshold $::tlc::log(threshold)
227 if {$lvl < $threshold} {
228 if {!($using_hotfuncs)} return
230 # Check our callers for a hotfunc <<<
231 for {
232 set i [expr {[info level] - 1}]
233 set depth 1
235 $i > 0
237 incr i -1
238 incr depth
240 set stackname [lindex [info level $i] 0]
241 if {$stackname == {}} continue
242 if {[catch {
243 set fqname [uplevel $depth [list namespace origin $stackname]]
244 } errmsg]} {
245 set fqname "??$stackname"
248 if {[info exists hotfuncs($fqname)]} {
249 set threshold $hotfuncs($fqname)
250 if {$lvl >= $threshold} break
253 if {$lvl < $threshold} return
254 # Check our callers for a hotfunc >>>
257 if {$baselog_instancename != ""} {
258 set instance_prefix "[c red]$baselog_instancename[c norm] "
259 } else {
260 set instance_prefix ""
262 set ns [namespace qualifiers $logzone]
263 set class [namespace tail $logzone]
264 set method [namespace tail $fqname]
265 set fqname_coloured "${instance_prefix}${ns}::[c red]$class[c norm]::[c bright yellow]$method[c norm]"
267 set substmap [list "\n" "[c purple]\\n[c white]" "\t" "[c purple]\\t[c white]"]
268 set argdesc {}
269 set idx 0
270 foreach arg $caller_args_def {
271 if {$arg == "args"} {
272 set this_passed_arg [lrange $passed_args $idx end]
273 } else {
274 set this_passed_arg [lindex $passed_args $idx]
277 set alen [string length $this_passed_arg]
278 if {[lsearch $switches(-suppressed) $arg] != -1} {
279 set this_passed_arg "#<suppressed>"
280 } elseif {$this_passed_arg == ""} {
281 set this_passed_arg "{}"
282 } elseif {$alen > 23} {
283 set this_passed_arg [string range $this_passed_arg 0 21]
284 set this_passed_arg [string map $substmap $this_passed_arg]
285 set this_passed_arg "[c underline bright white]$this_passed_arg[c norm]/[c red]$alen[c norm]"
286 } elseif {![string is print $this_passed_arg]} {
287 set this_passed_arg "#<nonprint($alen)>"
288 } else {
289 set this_passed_arg [string map $substmap $this_passed_arg]
290 set this_passed_arg "[c underline bright white]$this_passed_arg[c norm]"
293 lappend argdesc "[c green]$arg[c norm]$this_passed_arg"
294 incr idx
297 set argtext [join $argdesc " "]
299 set outmsg "${fqname_coloured} $argtext"
300 if {$msg != ""} {
301 set fg "bright white"
302 if {$lvl >= 40} {
303 set bg "red"
304 } elseif {$lvl >= 30} {
305 set bg "purple"
306 } elseif {$lvl >= 20} {
307 #set bg "green"
308 #set fg "black"
309 set bg "blue"
310 } elseif {$lvl >= 10} {
311 set bg "blue"
312 set fg "yellow"
313 } else {
314 set bg "none"
316 append outmsg ": [c bg_$bg $fg]$msg[c norm]"
319 if {[info exists redir] && $redir != ""} {
320 uplevel #0 [list $redir $outmsg]
321 } elseif {[info exists remote]} {
322 puts $remote $outmsg
323 } else {
324 puts stderr $outmsg
327 if {$debuglogmode} {puts stderr "baz"}
331 body tlc::Baselog::c {args} { #<<<1
332 return [tlc::colour {*}$args]
336 body tlc::Baselog::parray {args} { #<<<1
337 switch -- [llength $args] {
339 uplevel [list ::parray [lindex $args 0]]
343 set lvl [lindex $args 0]
344 set arrname [lindex $args 1]
346 upvar $arrname a
347 set keys [array names a]
349 set maxlen -1
350 foreach key $keys {
351 set thislen [string length $key]
352 if {$thislen > $maxlen} {
353 set maxlen $thislen
357 incr maxlen [string length $arrname]
358 incr maxlen 2
360 set msg "\n"
361 foreach key [lsort $keys] {
362 append msg [format "%-${maxlen}s = \"%s\"\n" "${arrname}($key)" $a($key)]
365 uplevel [list log $lvl $msg]
368 default {
369 error "Invalid number of arguments, expecting lvl arrayvar" "" \
370 [list syntax_error]