mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
breaking,log: set stderr as default log output, add .set_output_stream() to allow for opting in the old default of stdout (#23444)
This commit is contained in:
parent
8654cb9885
commit
4e68a86025
5 changed files with 76 additions and 37 deletions
|
@ -52,3 +52,22 @@ fn main() {
|
||||||
l.fatal('fatal') // panic, marked as [noreturn]
|
l.fatal('fatal') // panic, marked as [noreturn]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Backwards compatibility
|
||||||
|
|
||||||
|
After 2025/01/21, the `log` module outputs to `stderr` by default.
|
||||||
|
Before that, it used `stdout` by default.
|
||||||
|
|
||||||
|
If you want to restore the previous behaviour, you have to explicitly call l.set_output_stream():
|
||||||
|
```v
|
||||||
|
import os
|
||||||
|
import log
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// log.info('this will be printed to stderr after 2025/01/21 by default')
|
||||||
|
mut l := log.ThreadSafeLog{}
|
||||||
|
l.set_output_stream(os.stdout())
|
||||||
|
log.set_logger(l)
|
||||||
|
log.info('this will be printed to stdout')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -19,8 +19,8 @@ pub enum LogTarget {
|
||||||
both
|
both
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag_to_cli returns the tag for log level `l` as a colored string.
|
// tag_to_console returns the tag for log level `l` as a colored string.
|
||||||
fn tag_to_cli(l Level, short_tag bool) string {
|
fn tag_to_console(l Level, short_tag bool) string {
|
||||||
if short_tag {
|
if short_tag {
|
||||||
return match l {
|
return match l {
|
||||||
.disabled { ' ' }
|
.disabled { ' ' }
|
||||||
|
|
|
@ -5,6 +5,7 @@ module log
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import io
|
||||||
|
|
||||||
// TimeFormat define the log time string format, come from time/format.v
|
// TimeFormat define the log time string format, come from time/format.v
|
||||||
pub enum TimeFormat {
|
pub enum TimeFormat {
|
||||||
|
@ -25,10 +26,12 @@ pub enum TimeFormat {
|
||||||
tf_custom_format // 'MMMM Do YY N kk:mm:ss A' output like: January 1st 22 AD 13:45:33 PM
|
tf_custom_format // 'MMMM Do YY N kk:mm:ss A' output like: January 1st 22 AD 13:45:33 PM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stderr = os.stderr()
|
||||||
|
|
||||||
// Log represents a logging object
|
// Log represents a logging object
|
||||||
pub struct Log {
|
pub struct Log {
|
||||||
mut:
|
mut:
|
||||||
level Level
|
level Level = .debug
|
||||||
output_label string
|
output_label string
|
||||||
ofile os.File
|
ofile os.File
|
||||||
output_target LogTarget // output to console (stdout/stderr) or file or both.
|
output_target LogTarget // output to console (stdout/stderr) or file or both.
|
||||||
|
@ -36,6 +39,9 @@ mut:
|
||||||
custom_time_format string = 'MMMM Do YY N kk:mm:ss A' // timestamp with custom format
|
custom_time_format string = 'MMMM Do YY N kk:mm:ss A' // timestamp with custom format
|
||||||
short_tag bool
|
short_tag bool
|
||||||
always_flush bool // flush after every single .fatal(), .error(), .warn(), .info(), .debug() call
|
always_flush bool // flush after every single .fatal(), .error(), .warn(), .info(), .debug() call
|
||||||
|
output_stream io.Writer = stderr
|
||||||
|
//
|
||||||
|
show_notice_about_stdout_to_stderr_change bool = true // this field is temporary, and should be deleted after 2025-03-01
|
||||||
pub mut:
|
pub mut:
|
||||||
output_file_name string // log output to this file
|
output_file_name string // log output to this file
|
||||||
}
|
}
|
||||||
|
@ -77,6 +83,12 @@ pub fn (mut l Log) set_output_path(output_file_path string) {
|
||||||
l.ofile = ofile
|
l.ofile = ofile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set_output_stream sets the output stream to write log e.g. stderr, stdout, etc.
|
||||||
|
pub fn (mut l Log) set_output_stream(stream io.Writer) {
|
||||||
|
l.show_notice_about_stdout_to_stderr_change = false
|
||||||
|
l.output_stream = stream
|
||||||
|
}
|
||||||
|
|
||||||
// log_to_console_too turns on logging to the console too, in addition to logging to a file.
|
// log_to_console_too turns on logging to the console too, in addition to logging to a file.
|
||||||
// You have to call it *after* calling .set_output_path(output_file_path).
|
// You have to call it *after* calling .set_output_path(output_file_path).
|
||||||
pub fn (mut l Log) log_to_console_too() {
|
pub fn (mut l Log) log_to_console_too() {
|
||||||
|
@ -131,14 +143,31 @@ fn (mut l Log) log_file(s string, level Level) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// log_cli writes log line `s` with `level` to stdout.
|
// log_stream writes log line `s` with `level` to stderr or stderr depending on set output stream.
|
||||||
fn (l &Log) log_cli(s string, level Level) {
|
fn (mut l Log) log_stream(s string, level Level) {
|
||||||
timestamp := l.time_format(time.utc())
|
if l.show_notice_about_stdout_to_stderr_change {
|
||||||
e := tag_to_cli(level, l.short_tag)
|
l.show_notice_about_stdout_to_stderr_change = false
|
||||||
println('${timestamp} [${e}] ${s}')
|
// Show a warning at runtime, once, before the first logged message, that describes the stdout -> stderr change,
|
||||||
if l.always_flush {
|
// and how to opt in explicitly for the old behaviour:
|
||||||
|
println(' NOTE: the `log.Log` output goes to stderr now by default, not to stdout.')
|
||||||
|
println(' Call `l.set_output_stream(os.stdout())` explicitly, to opt in for the previous behavior.')
|
||||||
|
println(' Call `l.set_output_stream(os.stderr())` explicitly, if you want to silence this message (it will be removed after 2025-03-01 .')
|
||||||
flush_stdout()
|
flush_stdout()
|
||||||
}
|
}
|
||||||
|
timestamp := l.time_format(time.utc())
|
||||||
|
tag := tag_to_console(level, l.short_tag)
|
||||||
|
msg := '${timestamp} [${tag}] ${s}\n'
|
||||||
|
arr := msg.bytes()
|
||||||
|
l.output_stream.write(arr) or {}
|
||||||
|
if l.always_flush {
|
||||||
|
if mut l.output_stream is os.File {
|
||||||
|
match l.output_stream.fd {
|
||||||
|
1 { flush_stdout() }
|
||||||
|
2 { flush_stderr() }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send_output writes log line `s` with `level` to either the log file or the console
|
// send_output writes log line `s` with `level` to either the log file or the console
|
||||||
|
@ -148,7 +177,7 @@ pub fn (mut l Log) send_output(s &string, level Level) {
|
||||||
l.log_file(s, level)
|
l.log_file(s, level)
|
||||||
}
|
}
|
||||||
if l.output_target == .console || l.output_target == .both {
|
if l.output_target == .console || l.output_target == .both {
|
||||||
l.log_cli(s, level)
|
l.log_stream(s, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,36 +1,28 @@
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:5] 1: 1
|
[vlib/v/slow_tests/inout/dump_expression.vv:4] 1: 1
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:10] 'a': a
|
[vlib/v/slow_tests/inout/dump_expression.vv:9] 'a': a
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:34] a: Aa{
|
[vlib/v/slow_tests/inout/dump_expression.vv:33] a: Aa{
|
||||||
log: &log.Logger(log.Log{
|
cmd: &os.Command{
|
||||||
level: disabled
|
f: 0
|
||||||
output_label: ''
|
eof: false
|
||||||
ofile: os.File{
|
exit_code: 0
|
||||||
cfile: 0
|
path: ''
|
||||||
fd: 0
|
redirect_stdout: false
|
||||||
is_opened: false
|
}
|
||||||
}
|
|
||||||
output_target: console
|
|
||||||
time_format: tf_rfc3339_micro
|
|
||||||
custom_time_format: 'MMMM Do YY N kk:mm:ss A'
|
|
||||||
short_tag: false
|
|
||||||
always_flush: false
|
|
||||||
output_file_name: ''
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:35] p: Point{
|
[vlib/v/slow_tests/inout/dump_expression.vv:34] p: Point{
|
||||||
x: 1
|
x: 1
|
||||||
y: 2
|
y: 2
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:36] p_mut: Point{
|
[vlib/v/slow_tests/inout/dump_expression.vv:35] p_mut: Point{
|
||||||
x: 1
|
x: 1
|
||||||
y: 2
|
y: 2
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:37] p_ptr: &Point{
|
[vlib/v/slow_tests/inout/dump_expression.vv:36] p_ptr: &Point{
|
||||||
x: 1
|
x: 1
|
||||||
y: 2
|
y: 2
|
||||||
z: 3
|
z: 3
|
||||||
}
|
}
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:48] os.file_name(vfile): dump_expression.vv
|
[vlib/v/slow_tests/inout/dump_expression.vv:47] os.file_name(vfile): dump_expression.vv
|
||||||
[vlib/v/slow_tests/inout/dump_expression.vv:51] f.read(mut buf): 10
|
[vlib/v/slow_tests/inout/dump_expression.vv:50] f.read(mut buf): 10
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
import log
|
|
||||||
|
|
||||||
fn dump_of_int() {
|
fn dump_of_int() {
|
||||||
x := dump(1) + 1
|
x := dump(1) + 1
|
||||||
|
@ -19,16 +18,16 @@ mut:
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Aa {
|
struct Aa {
|
||||||
log &log.Logger
|
cmd &os.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_of_struct() {
|
fn dump_of_struct() {
|
||||||
p := Point{1, 2, 3}
|
p := Point{1, 2, 3}
|
||||||
mut p_mut := Point{1, 2, 3}
|
mut p_mut := Point{1, 2, 3}
|
||||||
p_ptr := &Point{1, 2, 3}
|
p_ptr := &Point{1, 2, 3}
|
||||||
l := &log.Log{}
|
c := &os.Command{}
|
||||||
a := Aa{
|
a := Aa{
|
||||||
log: l
|
cmd: c
|
||||||
}
|
}
|
||||||
|
|
||||||
dump(a)
|
dump(a)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue