Skip to content

Commit 4e68a86

Browse files
authored
breaking,log: set stderr as default log output, add .set_output_stream() to allow for opting in the old default of stdout (#23444)
1 parent 8654cb9 commit 4e68a86

5 files changed

Lines changed: 75 additions & 36 deletions

File tree

‎vlib/log/README.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,22 @@ fn main() {
5252
l.fatal('fatal') // panic, marked as [noreturn]
5353
}
5454
```
55+
56+
## Backwards compatibility
57+
58+
After 2025/01/21, the `log` module outputs to `stderr` by default.
59+
Before that, it used `stdout` by default.
60+
61+
If you want to restore the previous behaviour, you have to explicitly call l.set_output_stream():
62+
```v
63+
import os
64+
import log
65+
66+
fn main() {
67+
// log.info('this will be printed to stderr after 2025/01/21 by default')
68+
mut l := log.ThreadSafeLog{}
69+
l.set_output_stream(os.stdout())
70+
log.set_logger(l)
71+
log.info('this will be printed to stdout')
72+
}
73+
```

‎vlib/log/common.v‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ pub enum LogTarget {
1919
both
2020
}
2121

22-
// tag_to_cli returns the tag for log level `l` as a colored string.
23-
fn tag_to_cli(l Level, short_tag bool) string {
22+
// tag_to_console returns the tag for log level `l` as a colored string.
23+
fn tag_to_console(l Level, short_tag bool) string {
2424
if short_tag {
2525
return match l {
2626
.disabled { ' ' }

‎vlib/log/log.v‎

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module log
55

66
import os
77
import time
8+
import io
89

910
// TimeFormat define the log time string format, come from time/format.v
1011
pub 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
2932
pub struct Log {
3033
mut:
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
3945
pub 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).
8294
pub 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

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
1-
[vlib/v/slow_tests/inout/dump_expression.vv:5] 1: 1
2-
[vlib/v/slow_tests/inout/dump_expression.vv:10] 'a': a
3-
[vlib/v/slow_tests/inout/dump_expression.vv:34] a: Aa{
4-
log: &log.Logger(log.Log{
5-
level: disabled
6-
output_label: ''
7-
ofile: os.File{
8-
cfile: 0
9-
fd: 0
10-
is_opened: false
11-
}
12-
output_target: console
13-
time_format: tf_rfc3339_micro
14-
custom_time_format: 'MMMM Do YY N kk:mm:ss A'
15-
short_tag: false
16-
always_flush: false
17-
output_file_name: ''
18-
})
1+
[vlib/v/slow_tests/inout/dump_expression.vv:4] 1: 1
2+
[vlib/v/slow_tests/inout/dump_expression.vv:9] 'a': a
3+
[vlib/v/slow_tests/inout/dump_expression.vv:33] a: Aa{
4+
cmd: &os.Command{
5+
f: 0
6+
eof: false
7+
exit_code: 0
8+
path: ''
9+
redirect_stdout: false
10+
}
1911
}
20-
[vlib/v/slow_tests/inout/dump_expression.vv:35] p: Point{
12+
[vlib/v/slow_tests/inout/dump_expression.vv:34] p: Point{
2113
x: 1
2214
y: 2
2315
z: 3
2416
}
25-
[vlib/v/slow_tests/inout/dump_expression.vv:36] p_mut: Point{
17+
[vlib/v/slow_tests/inout/dump_expression.vv:35] p_mut: Point{
2618
x: 1
2719
y: 2
2820
z: 3
2921
}
30-
[vlib/v/slow_tests/inout/dump_expression.vv:37] p_ptr: &Point{
22+
[vlib/v/slow_tests/inout/dump_expression.vv:36] p_ptr: &Point{
3123
x: 1
3224
y: 2
3325
z: 3
3426
}
35-
[vlib/v/slow_tests/inout/dump_expression.vv:48] os.file_name(vfile): dump_expression.vv
36-
[vlib/v/slow_tests/inout/dump_expression.vv:51] f.read(mut buf): 10
27+
[vlib/v/slow_tests/inout/dump_expression.vv:47] os.file_name(vfile): dump_expression.vv
28+
[vlib/v/slow_tests/inout/dump_expression.vv:50] f.read(mut buf): 10

‎vlib/v/slow_tests/inout/dump_expression.vv‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os
2-
import log
32

43
fn dump_of_int() {
54
x := dump(1) + 1
@@ -19,16 +18,16 @@ mut:
1918
}
2019

2120
struct Aa {
22-
log &log.Logger
21+
cmd &os.Command
2322
}
2423

2524
fn dump_of_struct() {
2625
p := Point{1, 2, 3}
2726
mut p_mut := Point{1, 2, 3}
2827
p_ptr := &Point{1, 2, 3}
29-
l := &log.Log{}
28+
c := &os.Command{}
3029
a := Aa{
31-
log: l
30+
cmd: c
3231
}
3332

3433
dump(a)

0 commit comments

Comments
 (0)