Tcl Library Source Code

View Ticket
Login
Ticket UUID: e4d5ef01e7a5db5e6d3c460ec4165edfacd1b1bb
Title: TclOO: class logger mixin
Type: Bug Version: 8.6
Submitter: random-archer Created on: 2018-05-16 17:40:57
Subsystem: logger Assigned To: aku
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2019-06-24 19:34:18
Resolution: Accepted Closed By: aku
    Closed on: 2019-06-24 19:34:18
Description:
A bug confirmed by Donal Fellows:
[https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin]

-----
Here is an extract of the upper communication:

Andrej:

Logger included with tcllib 1.19 breaks when used as class logger:

#!/usr/bin/env tclsh
package require logger
package require logger::utils
::oo::class create Main {
    variable log
    constructor {} {
        set this_inst [namespace current]
        set this_klaz [info object class $this_inst]
        set log [::logger::init $this_klaz]
        ::logger::utils::applyAppender \
        -appender "console" \
        -appenderArgs {-conversionPattern {%d \[%p\] \[%M\] %m}} \
        -serviceCmd $log
    }
    method invoke {} {
        ${log}::info "hello"
    }
}
set main [Main new]
$main invoke

Producing object namespace:

2018/05/15 08:54:43 [info] [::oo::Obj12] hello

Instead of class/method namespace:

2018/05/15 08:54:43 [info] [::Main::invoke] hello

Donal:

This is a bug in ::logger::utils::createLogProc in loggerUtils.tcl in that the %M substitution is not aware of TclOO. If it was, it wouldn't be using the name of the object that the method was invoked on, but rather the name of the method. (It appears that it was designed to work with [incr Tcl], which does name methods like you appear to want.)

...

That's actually because it looks up the value for %M substitution like this:

    if {[info level] < 2} {
        set method "global"
    } else {
        set method [lindex [info level -1] 0]
    }

whereas it probably ought to do something more like this:

    if {[info level] < 2} {
        set method "global"
    } elseif {[uplevel 1 {namespace which self}] == "::oo::Helpers::self"} {
        set method [uplevel 1 {string cat [self class] "::" [self method]}]
    } else {
        set method [lindex [info level -1] 0]
    }
User Comments: aku added on 2019-06-24 19:34:18:
Fix integrated with commit [4955240acf].

oehhar added on 2018-05-17 06:43:32:

Here are some hints and thoughts, no help for implementation etc.

The work done for TIP490 "oo for msgcat" may be helpful here: http://core.tcl.tk/tips/doc/trunk/tip/490.md.

See the section "Implementation" of the TIP. It contains the following function to detect the oo-environment of the callers-caller:

proc ::msgcat::PackageNamespaceGet {} {
    uplevel 2 {
	# Check for no object
	switch -exact -- [namespace which self] {
	    {::oo::define::self} {
		# We are within a class definition
		return [namespace qualifiers [self]]
	    }
	    {::oo::Helpers::self} {
		# We are within an object
		set Class [info object class [self]]
		# Check for classless defined object
		if {$Class eq {::oo::object}} {
		    return [namespace qualifiers [self]]
		}
		# Class defined object
		return [namespace qualifiers $Class]
	    }
	    default {
		# Not in object environment
		return [namespace current]
	    }
	}
    }
}

This is for TCL8.7 and only tcloo. Additional work might be necessary for tcl 8.5-6 and IncrTCL.

Hope this may help for an implementation, Harald