TclOO 日志混合组件

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

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进行了交叉检查,不清楚)

  1. package require TclOO
  2. package require try
  3. package require logger
  4. namespace eval ::demo::utils {}
  5. namespace eval ::demo::naming {}
  6. ::oo::class create ::demo::utils::LoggingMixin {
  7. variable log
  8. constructor {args} {
  9. set concls [lindex [self call] 0 end end-1]
  10. try {
  11. set log [::logger::servicecmd $concls]
  12. } trap {LOGGER NO_SUCH_SERVICE} {} {
  13. set log [::logger::init $concls]
  14. }
  15. # check for next
  16. if {[llength [self next]] > 0} {
  17. next {*}$args
  18. }
  19. }
  20. }
  21. ::oo::class create ::demo::naming::GenericNaming {
  22. mixin ::demo::utils::LoggingMixin
  23. variable log
  24. constructor {args} {
  25. ${log}::info "hello world"
  26. }
  27. }
  28. ::oo::class create ::demo::naming::NewNaming {
  29. mixin ::demo::utils::LoggingMixin
  30. variable log
  31. constructor {args} {
  32. ${log}::info "new world"
  33. }
  34. }
  35. ::demo::naming::GenericNaming new
  36. ::demo::naming::NewNaming new

expected output:

  1. $ tclsh ./mixin.tcl
  2. [Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
  3. [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

  1. package require TclOO
  2. package require try
  3. package require logger
  4. namespace eval ::demo::utils {}
  5. namespace eval ::demo::naming {}
  6. ::oo::class create ::demo::utils::LoggingMixin {
  7. variable log
  8. constructor {args} {
  9. set concls [lindex [self call] 0 end end-1]
  10. try {
  11. set log [::logger::servicecmd $concls]
  12. } trap {LOGGER NO_SUCH_SERVICE} {} {
  13. set log [::logger::init $concls]
  14. }
  15. # check for next
  16. if {[llength [self next]] > 0} {
  17. next {*}$args
  18. }
  19. }
  20. }
  21. ::oo::class create ::demo::naming::GenericNaming {
  22. mixin ::demo::utils::LoggingMixin
  23. variable log
  24. constructor {args} {
  25. ${log}::info "hello world"
  26. }
  27. }
  28. ::oo::class create ::demo::naming::NewNaming {
  29. mixin ::demo::utils::LoggingMixin
  30. variable log
  31. constructor {args} {
  32. ${log}::info "new world"
  33. }
  34. }
  35. ::demo::naming::GenericNaming new
  36. ::demo::naming::NewNaming new

expected output:

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

答案1

得分: 3

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

  1. set concls [info object class [self]]

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

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

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

  1. oo::class create ::demo::utils::LoggingMixin {
  2. variable log
  3. constructor {args} {
  4. set concls [info object class [self]]
  5. try {
  6. set log [::logger::servicecmd $concls]
  7. } trap {LOGGER NO_SUCH_SERVICE} {} {
  8. set log [::logger::init $concls]
  9. }
  10. next {*}$args
  11. }
  12. # TODO: 为其他所需的日志级别添加方法
  13. method LogInfo args {
  14. tailcall ${log}::info {*}$args
  15. }
  16. }

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

  1. oo::class create LoggingDemo {
  2. mixin ::demo::utils::LoggingMixin
  3. constructor {args} {
  4. my LogInfo "new world"
  5. }
  6. }

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

  1. constructor {args} {
  2. set concls [info object class [self]]
  3. try {
  4. set log [::logger::servicecmd $concls]
  5. } trap {LOGGER NO_SUCH_SERVICE} {} {
  6. set log [::logger::init $concls]
  7. }
  8. proc logInfo args {
  9. variable log; # 需要明确指定这一点
  10. tailcall ${log}::info {*}$args
  11. }
  12. next {*}$args
  13. }

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

  1. oo::class create ::demo::utils::LoggingMixin {
  2. variable LoggingMixinLog
  3. method GetLog {level} {
  4. if {![info exist LoggingMixinLog]} {
  5. set concls [info object class [self]]
  6. try {
  7. set LoggingMixinLog [::logger::servicecmd $concls]
  8. } trap {LOGGER NO_SUCH_SERVICE} {} {
  9. set LoggingMixinLog [::logger::init $concls]
  10. }
  11. }
  12. return ${LoggingMixinLog}::$level
  13. }
  14. method LogInfo args {
  15. tailcall [my GetLog info] {*}$args
  16. }
  17. }
  18. oo::class create LoggingDemo {
  19. mixin ::demo::utils::LoggingMixin
  20. constructor {args} {
  21. my LogInfo "new world"
  22. }
  23. }

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

英文:

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

  1. 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:

  1. oo::class create ::demo::utils::LoggingMixin {
  2. variable log
  3. constructor {args} {
  4. set concls [info object class [self]]
  5. try {
  6. set log [::logger::servicecmd $concls]
  7. } trap {LOGGER NO_SUCH_SERVICE} {} {
  8. set log [::logger::init $concls]
  9. }
  10. next {*}$args
  11. }
  12. # TODO: Add methods for other wanted logging levels
  13. method LogInfo args {
  14. tailcall ${log}::info {*}$args
  15. }
  16. }

Because then the subclass could be just this:

  1. oo::class create LoggingDemo {
  2. mixin ::demo::utils::LoggingMixin
  3. constructor {args} {
  4. my LogInfo "new world"
  5. }
  6. }

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

  1. constructor {args} {
  2. set concls [info object class [self]]
  3. try {
  4. set log [::logger::servicecmd $concls]
  5. } trap {LOGGER NO_SUCH_SERVICE} {} {
  6. set log [::logger::init $concls]
  7. }
  8. proc logInfo args {
  9. variable log; # Need to be explicit with this
  10. tailcall ${log}::info {*}$args
  11. }
  12. next {*}$args
  13. }

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

  1. oo::class create ::demo::utils::LoggingMixin {
  2. variable LoggingMixinLog
  3. method GetLog {level} {
  4. if {![info exist LoggingMixinLog]} {
  5. set concls [info object class [self]]
  6. try {
  7. set LoggingMixinLog [::logger::servicecmd $concls]
  8. } trap {LOGGER NO_SUCH_SERVICE} {} {
  9. set LoggingMixinLog [::logger::init $concls]
  10. }
  11. }
  12. return ${LoggingMixinLog}::$level
  13. }
  14. method LogInfo args {
  15. tailcall [my GetLog info] {*}$args
  16. }
  17. }
  18. oo::class create LoggingDemo {
  19. mixin ::demo::utils::LoggingMixin
  20. constructor {args} {
  21. my LogInfo "new world"
  22. }
  23. }

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:

确定