2

I want to use log module from tcllib to log to console and a file. Here's some working code to redirect the log output to a file.

tcl;

package require logger;
package require logger::utils;

global log;
set log [logger::init Main];

set progName "Main"
set progVersion "1.0"
set workDir "C:/Temp/$progName"
set timestamp [clock format [clock seconds] -format "%Y%m%d_%H%M%S"]

if { [file isdirectory $workDir] == 0 } {
    file mkdir "$workDir"
}
set outputFilePath "$workDir/${progName}_$timestamp.txt"
set outputFile [ open "$outputFilePath" "w" ]
logger::utils::applyAppender \
    -appender fileAppend \
    -appenderArgs "-outputChannel $outputFile" \
    -serviceCmd $log

if {[catch {
            ${log}::debug "debug log"
            ${log}::info "info log"
        } err ]} {
    ${log}::critical "$err"
}
${log}::delete

This is the console output from the above script...

0.9.4
1.3.1
::logger::tree::Main
Main
1.0
C:/Temp/Main
20190830_175510
C:/Temp/Main/Main_20190830_175510.txt
file74067dc0

The content of C:/Temp/Main/Main_20190830_175510.txt is...

[2019/08/30 17:54:08] [Main] [global] [debug] debug log
[2019/08/30 17:54:08] [Main] [global] [info] info log

When I add another logger the output is no longer redirected to the file, but only to console

logger::utils::applyAppender \
    -appender console \
    -serviceCmd $log

How can I initialize the logging that the logs are sent to console and the file?

Blackhawk
  • 21
  • 2
  • Looks like the [`logger::utils` code](https://core.tcl-lang.org/tcllib/artifact/de6cda62df4155e4) only supports one appender at a time. Which means you'd have to do some sort of splicing (“tee”) appender… – Donal Fellows Sep 01 '19 at 12:15
  • As for splicing: you may wrap (for the `fileAppend` appender) the channel behind `outputFile` using channel transforms: see e.g. https://stackoverflow.com/questions/57067897/how-to-give-output-of-puts-to-a-proc-as-input/57073137#57073137 – mrcalvin Sep 02 '19 at 08:15

1 Answers1

0

A workaround would be to create two logger instances and add them to an array or dictionary, then loop through the collection:

package require logger
package require logger::utils
package require logger::appender

set consoleLogger [logger::init console]
set fileLogger    [logger::init file]

#// ... logger::utils::applyAppender ...

#/* Register loggers */
set loggers [dict create console $consoleLogger file $fileLogger]

#/* Invoke both loggers */
proc log {cmd args} {
    variable loggers
    foreach logger [dict values $loggers] {
        uplevel ${logger}::${cmd} $args
    }
}

#/* Log something */
log info "hello, world"

Alternatively, modify loggerUtils.tcl to remove the hard-coded channel:

$ grep -n -C 10 "\$outputCommand \$outputChannel" lib/tcllib1.20/log/loggerUtils.tcl
301-    } else {
302-    set outputChannel stdout
303-    }
304-
305-    set formatText $text
306-    set outputCommand puts
307-
308-    set procText {
309-    proc $opt(-procName) {text} {
310-        $methodText
311:        $outputCommand $outputChannel \"$formatText\"
312-    }
313-    }
314-
315-    set procText [subst $procText]
316-    return $procText
317-}
318-
319-
320-##Procedure Header
321-# Copyright (c) 2005 Cisco Systems, Inc.
$
$
$ sed -iE 's/\$outputCommand \$outputChannel/return/' lib/tcllib1.20/log/loggerUtils.tcl
$

To clarify, the above sed substitution changes line 311:

Before:

309-    proc $opt(-procName) {text} {
310-        $methodText
311:        $outputCommand $outputChannel \"$formatText\"
312-    }

After:

309-    proc $opt(-procName) {text} {
310-        $methodText
311:        return \"$formatText\"
312-    }

Making the above change will allow you to apply an appender to any service command and retrieve only the text, which can then be sent to any channel you want eg:

package require logger
package require logger::utils

set log [logger::init myservice]

logger::utils::applyAppender -appender console -serviceCmd $log

set text [${log}::info "hello, world"]

set logfile [open myfile.log ab]    

#/* Send text to any channel */
puts stdout   $text
puts $logfile $text
mvanle
  • 1,847
  • 23
  • 19