From 68bd9a9549751e82415ab864edfb4b6b2d29f927 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 1 Feb 2024 07:51:52 +0200 Subject: [PATCH] log: implement set_always_flush/1 for log.Log, log.ThreadSafeLog and log.Logger (#20698) --- vlib/log/default.v | 5 +++++ vlib/log/file_log_test.v | 24 +++++++++++++++++++++ vlib/log/log.v | 13 +++++++++++ vlib/log/logger_interface.v | 1 + vlib/log/safe_log.v | 8 +++++++ vlib/v/slow_tests/inout/dump_expression.out | 1 + 6 files changed, 52 insertions(+) diff --git a/vlib/log/default.v b/vlib/log/default.v index ddc47ff29213ca..d672afc6aa3b9f 100644 --- a/vlib/log/default.v +++ b/vlib/log/default.v @@ -53,3 +53,8 @@ pub fn info(s string) { pub fn debug(s string) { default_logger.debug(s) } + +// set_always_flush called with true, will make the log flush after every single .fatal(), .error(), .warn(), .info(), .debug() call. +pub fn set_always_flush(should_flush_on_every_message bool) { + default_logger.set_always_flush(should_flush_on_every_message) +} diff --git a/vlib/log/file_log_test.v b/vlib/log/file_log_test.v index 58ed6d0f5bde1d..434d89512ebf86 100644 --- a/vlib/log/file_log_test.v +++ b/vlib/log/file_log_test.v @@ -40,3 +40,27 @@ fn test_reopen() { os.rmdir_all(lfolder) or {} } + +fn test_set_always_flush() { + lfolder := os.join_path(os.vtmp_dir(), rand.ulid()) + lpath1 := os.join_path(lfolder, 'current.log') + os.mkdir_all(lfolder)! + defer { + os.rmdir_all(lfolder) or {} + } + dump(lfolder) + mut l := log.Log{ + level: .info + } + l.set_full_logpath(lpath1) + l.set_always_flush(true) + l.warn('one warning') + l.info('one info message') + l.error('one error') + l.close() + lcontent1 := os.read_file(lpath1)! + assert lcontent1.len > 0 + assert lcontent1.contains('one warning') + assert lcontent1.contains('one error') + assert lcontent1.contains('one info message') +} diff --git a/vlib/log/log.v b/vlib/log/log.v index 992908bd66c83d..fa85081b1caa81 100644 --- a/vlib/log/log.v +++ b/vlib/log/log.v @@ -34,6 +34,7 @@ mut: time_format TimeFormat custom_time_format string = 'MMMM Do YY N kk:mm:ss A' // timestamp with custom format short_tag bool + always_flush bool // flush after every single .fatal(), .error(), .warn(), .info(), .debug() call pub mut: output_file_name string // log output to this file } @@ -118,6 +119,9 @@ fn (mut l Log) log_file(s string, level Level) { timestamp := l.time_format(time.now()) e := tag_to_file(level, l.short_tag) l.ofile.writeln('${timestamp} [${e}] ${s}') or { panic(err) } + if l.always_flush { + l.flush() + } } // log_cli writes log line `s` with `level` to stdout. @@ -125,6 +129,9 @@ fn (l &Log) log_cli(s string, level Level) { timestamp := l.time_format(time.now()) e := tag_to_cli(level, l.short_tag) println('${timestamp} [${e}] ${s}') + if l.always_flush { + flush_stdout() + } } // send_output writes log line `s` with `level` to either the log file or the console @@ -244,6 +251,12 @@ pub fn (mut l Log) set_time_format(f TimeFormat) { l.time_format = f } +// set_always_flush called with true, will make the log flush after every single .fatal(), .error(), .warn(), .info(), .debug() call. +// That can be much slower, if you plan to do lots of frequent calls, but if your program exits early or crashes, your logs will be more complete. +pub fn (mut l Log) set_always_flush(should_flush_every_time bool) { + l.always_flush = should_flush_every_time +} + // get_time_format will get the log time format pub fn (l Log) get_time_format() TimeFormat { return l.time_format diff --git a/vlib/log/logger_interface.v b/vlib/log/logger_interface.v index d716a78ac21da1..5f285673862371 100644 --- a/vlib/log/logger_interface.v +++ b/vlib/log/logger_interface.v @@ -11,5 +11,6 @@ mut: debug(s string) // utility methods: set_level(level Level) + set_always_flush(should_flush bool) free() } diff --git a/vlib/log/safe_log.v b/vlib/log/safe_log.v index 356b0648d5c373..8f88d5f6f5c86a 100644 --- a/vlib/log/safe_log.v +++ b/vlib/log/safe_log.v @@ -37,6 +37,14 @@ pub fn (mut x ThreadSafeLog) set_level(level Level) { x.mu.unlock() } +// set_always_flush called with true, will make the log flush after every single .fatal(), .error(), .warn(), .info(), .debug() call. +// That can be much slower, if you plan to do lots of frequent calls, but if your program exits early or crashes, your logs will be more complete. +pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) { + x.mu.@lock() + x.Log.set_always_flush(should_flush) + x.mu.unlock() +} + // debug logs a debug message pub fn (mut x ThreadSafeLog) debug(s string) { x.mu.@lock() diff --git a/vlib/v/slow_tests/inout/dump_expression.out b/vlib/v/slow_tests/inout/dump_expression.out index c7280fcfc1fb8c..64fe20c6501c62 100644 --- a/vlib/v/slow_tests/inout/dump_expression.out +++ b/vlib/v/slow_tests/inout/dump_expression.out @@ -13,6 +13,7 @@ time_format: tf_ss_micro custom_time_format: 'MMMM Do YY N kk:mm:ss A' short_tag: false + always_flush: false output_file_name: '' }) }