TclOO 日志混合组件

huangapple go评论75阅读模式
英文:

TclOO Logging Mixin

问题

I wrote this code (below) trying to figure out more about TclOO mixin classes.

a. is there a more direct way to write the concrete class name from the mixin constructor?

使用mixin构造函数更直接的方法是否存在?

b. is there a way to simplify the mixin class using self requiring no additional calls (e.g. init) other than mixin (as below) when using it? (cross checked with https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin , not clear)

在使用mixin时,是否有一种方式可以简化mixin类,只使用self,而无需额外调用(例如init),如下所示?(与https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin进行了交叉检查,不清楚)

package require TclOO
package require try
package require logger

namespace eval ::demo::utils {}
namespace eval ::demo::naming {}

::oo::class create ::demo::utils::LoggingMixin {
    variable log

    constructor {args} {
        set concls [lindex [self call] 0 end end-1]
        try {
            set log [::logger::servicecmd $concls]
        } trap {LOGGER NO_SUCH_SERVICE} {} {
            set log [::logger::init $concls]
        }

        # check for next
        if {[llength [self next]] > 0} {
            next {*}$args
        }
    }
}

::oo::class create ::demo::naming::GenericNaming {
    mixin ::demo::utils::LoggingMixin

    variable log

    constructor {args} {
        ${log}::info "hello world"
    }
}

::oo::class create ::demo::naming::NewNaming {
    mixin ::demo::utils::LoggingMixin

    variable log

    constructor {args} {
        ${log}::info "new world"
    }
}

::demo::naming::GenericNaming new

::demo::naming::NewNaming new

expected output:

$ tclsh ./mixin.tcl  
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::NewNaming] [info] 'new world'
英文:

I wrote this code (below) trying to figure out more about TclOO mixin classes.

a. is there a more direct way to write the concrete class name from the mixin constructor?

> set concls [lindex [self call] 0 end end-1]

b. is there a way to simplify the mixin class using self requiring no additional calls (e.g. init) other than mixin (as below) when using it? (cross checked with https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin , not clear)

> mixin ::demo::utils::LoggingMixin

package require TclOO
package require try
package require logger

namespace eval ::demo::utils {}
namespace eval ::demo::naming {}

::oo::class create ::demo::utils::LoggingMixin {
    variable log
    
    constructor {args} {
        set concls [lindex [self call] 0 end end-1]
        try {
            set log [::logger::servicecmd $concls]
        } trap {LOGGER NO_SUCH_SERVICE} {} {
            set log [::logger::init $concls]
        }

        # check for next
        if {[llength [self next]] > 0} {
            next {*}$args
        }
    }
}

::oo::class create ::demo::naming::GenericNaming {
    mixin ::demo::utils::LoggingMixin
    
    variable log
    
    constructor {args} {
        ${log}::info "hello world"
    }
}

::oo::class create ::demo::naming::NewNaming {
    mixin ::demo::utils::LoggingMixin
    
    variable log
    
    constructor {args} {
        ${log}::info "new world"
    }

}


::demo::naming::GenericNaming new

::demo::naming::NewNaming new

expected output:

$ tclsh ./mixin.tcl  
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::NewNaming] [info] 'new world'

答案1

得分: 3

获取正在创建的对象的类,只需在构造函数中执行以下操作:

set concls [info object class [self]]

无需遍历参数(这很好,因为在存在复杂的转发或已覆盖new和/或create时,这样做会更容易出错)。

Tcl 8.6不需要您执行package require TclOOpackage require try;这些都是内置功能。

我倾向于这样设置日志混合类:

oo::class create ::demo::utils::LoggingMixin {
    variable log
    
    constructor {args} {
        set concls [info object class [self]]
        try {
            set log [::logger::servicecmd $concls]
        } trap {LOGGER NO_SUCH_SERVICE} {} {
            set log [::logger::init $concls]
        }
        next {*}$args
    }

    # TODO: 为其他所需的日志级别添加方法
    method LogInfo args {
        tailcall ${log}::info {*}$args
    }
}

因为然后子类可以只是这样的:

oo::class create LoggingDemo {
    mixin ::demo::utils::LoggingMixin
    constructor {args} {
        my LogInfo "new world"
    }
}

或者甚至可以制作一些过程来进一步改进语法:

constructor {args} {
    set concls [info object class [self]]
    try {
        set log [::logger::servicecmd $concls]
    } trap {LOGGER NO_SUCH_SERVICE} {} {
        set log [::logger::init $concls]
    }
    proc logInfo args {
        variable log; # 需要明确指定这一点
        tailcall ${log}::info {*}$args
    }
    next {*}$args
}

另一种方法是使混合类提供自我初始化的日志方法:

oo::class create ::demo::utils::LoggingMixin {
    variable LoggingMixinLog
    
    method GetLog {level} {
        if {![info exist LoggingMixinLog]} {
            set concls [info object class [self]]
            try {
                set LoggingMixinLog [::logger::servicecmd $concls]
            } trap {LOGGER NO_SUCH_SERVICE} {} {
                set LoggingMixinLog [::logger::init $concls]
            }
        }
        return ${LoggingMixinLog}::$level
    }

    method LogInfo args {
        tailcall [my GetLog info] {*}$args
    }
}

oo::class create LoggingDemo {
    mixin ::demo::utils::LoggingMixin
    constructor {args} {
        my LogInfo "new world"
    }
}

这有助于在事后混合到对象或类中的支持。如果要创建日志包装器,建议使用tailcalluplevel 1来调用它们,以便当前的堆栈帧是想要创建日志条目的堆栈帧,而不是小型代理帮助方法/过程。

英文:

To get the class of the object being created, it is sufficient to do this in the constructor:

set concls [info object class [self]]

You don't need to pick through the arguments (good, as that's more error-prone when there are complex forwardings in place or new and/or create have been overridden).

Tcl 8.6 doesn't need you to package require TclOO or package require try; both are built-in features these days.


I'd be inclined to set up the logging mixin like this:

oo::class create ::demo::utils::LoggingMixin {
    variable log
    
    constructor {args} {
        set concls [info object class [self]]
        try {
            set log [::logger::servicecmd $concls]
        } trap {LOGGER NO_SUCH_SERVICE} {} {
            set log [::logger::init $concls]
        }
        next {*}$args
    }

    # TODO: Add methods for other wanted logging levels
    method LogInfo args {
        tailcall ${log}::info {*}$args
    }
}

Because then the subclass could be just this:

oo::class create LoggingDemo {
    mixin ::demo::utils::LoggingMixin
    constructor {args} {
        my LogInfo "new world"
    }
}

Or I might even make some procedures to improve the syntax further:

    constructor {args} {
        set concls [info object class [self]]
        try {
            set log [::logger::servicecmd $concls]
        } trap {LOGGER NO_SUCH_SERVICE} {} {
            set log [::logger::init $concls]
        }
        proc logInfo args {
            variable log; # Need to be explicit with this
            tailcall ${log}::info {*}$args
        }
        next {*}$args
    }

Another approach is to make the mixin provide logging methods that are self-initializing:

oo::class create ::demo::utils::LoggingMixin {
    variable LoggingMixinLog
    
    method GetLog {level} {
        if {![info exist LoggingMixinLog]} {
            set concls [info object class [self]]
            try {
                set LoggingMixinLog [::logger::servicecmd $concls]
            } trap {LOGGER NO_SUCH_SERVICE} {} {
                set LoggingMixinLog [::logger::init $concls]
            }
        }
        return ${LoggingMixinLog}::$level
    }

    method LogInfo args {
        tailcall [my GetLog info] {*}$args
    }
}

oo::class create LoggingDemo {
    mixin ::demo::utils::LoggingMixin
    constructor {args} {
        my LogInfo "new world"
    }
}

That has the advantage of supporting mixing into objects or classes after the fact.


If you make logging wrappers, calling them with either tailcall or uplevel 1 is advisable so that the current stack frame is the one that wants to make the log entry, not the little proxy helper method/procedure.

huangapple
  • 本文由 发表于 2023年6月5日 05:07:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76402415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定