@@ -5,6 +5,7 @@ module log
55
66import os
77import time
8+ import io
89
910// TimeFormat define the log time string format, come from time/format.v
1011pub enum TimeFormat {
@@ -25,17 +26,22 @@ pub enum TimeFormat {
2526 tf_custom_format // 'MMMM Do YY N kk:mm:ss A' output like: January 1st 22 AD 13:45:33 PM
2627}
2728
29+ const stderr = os.stderr ()
30+
2831// Log represents a logging object
2932pub struct Log {
3033mut :
31- level Level
34+ level Level = .debug
3235 output_label string
3336 ofile os.File
3437 output_target LogTarget // output to console (stdout/stderr) or file or both.
3538 time_format TimeFormat = .tf_rfc3339_ micro
3639 custom_time_format string = 'MMMM Do YY N kk:mm:ss A' // timestamp with custom format
3740 short_tag bool
3841 always_flush bool // flush after every single .fatal(), .error(), .warn(), .info(), .debug() call
42+ output_stream io.Writer = stderr
43+ //
44+ show_notice_about_stdout_to_stderr_change bool = true // this field is temporary, and should be deleted after 2025-03-01
3945pub mut :
4046 output_file_name string // log output to this file
4147}
@@ -77,6 +83,12 @@ pub fn (mut l Log) set_output_path(output_file_path string) {
7783 l.ofile = ofile
7884}
7985
86+ // set_output_stream sets the output stream to write log e.g. stderr, stdout, etc.
87+ pub fn (mut l Log) set_output_stream (stream io.Writer) {
88+ l.show_notice_about_stdout_to_stderr_change = false
89+ l.output_stream = stream
90+ }
91+
8092// log_to_console_too turns on logging to the console too, in addition to logging to a file.
8193// You have to call it *after* calling .set_output_path(output_file_path).
8294pub fn (mut l Log) log_to_console_too () {
@@ -131,13 +143,30 @@ fn (mut l Log) log_file(s string, level Level) {
131143 }
132144}
133145
134- // log_cli writes log line `s` with `level` to stdout.
135- fn (l &Log) log_cli (s string , level Level) {
146+ // log_stream writes log line `s` with `level` to stderr or stderr depending on set output stream.
147+ fn (mut l Log) log_stream (s string , level Level) {
148+ if l.show_notice_about_stdout_to_stderr_change {
149+ l.show_notice_about_stdout_to_stderr_change = false
150+ // Show a warning at runtime, once, before the first logged message, that describes the stdout -> stderr change,
151+ // and how to opt in explicitly for the old behaviour:
152+ println (' NOTE: the `log.Log` output goes to stderr now by default, not to stdout.' )
153+ println (' Call `l.set_output_stream(os.stdout())` explicitly, to opt in for the previous behavior.' )
154+ println (' Call `l.set_output_stream(os.stderr())` explicitly, if you want to silence this message (it will be removed after 2025-03-01 .' )
155+ flush_stdout ()
156+ }
136157 timestamp := l.time_format (time.utc ())
137- e := tag_to_cli (level, l.short_tag)
138- println ('${timestamp } [${e }] ${s }' )
158+ tag := tag_to_console (level, l.short_tag)
159+ msg := '${timestamp } [${tag }] ${s }\n '
160+ arr := msg.bytes ()
161+ l.output_stream.write (arr) or {}
139162 if l.always_flush {
140- flush_stdout ()
163+ if mut l.output_stream is os.File {
164+ match l.output_stream.fd {
165+ 1 { flush_stdout () }
166+ 2 { flush_stderr () }
167+ else {}
168+ }
169+ }
141170 }
142171}
143172
@@ -148,7 +177,7 @@ pub fn (mut l Log) send_output(s &string, level Level) {
148177 l.log_file (s, level)
149178 }
150179 if l.output_target == .console || l.output_target == .both {
151- l.log_cli (s, level)
180+ l.log_stream (s, level)
152181 }
153182}
154183
0 commit comments