| 1 | module log |
| 2 | |
| 3 | import sync |
| 4 | |
| 5 | // ThreadSafeLog embeds Log, and adds a mutex field. |
| 6 | // It uses the mutex to synchronise accesses to the embedded Log. |
| 7 | pub struct ThreadSafeLog { |
| 8 | Log |
| 9 | pub mut: |
| 10 | mu &sync.Mutex = sync.new_mutex() |
| 11 | } |
| 12 | |
| 13 | // new_thread_safe_log returns a new log structure, whose methods are safe |
| 14 | // to call by multiple threads. |
| 15 | pub fn new_thread_safe_log() &ThreadSafeLog { |
| 16 | slevel := $d('log_default_level', 'info') |
| 17 | level := level_from_tag(slevel.to_upper()) or { panic('invalid log_default_level: ${slevel}') } |
| 18 | mut x := &ThreadSafeLog{ |
| 19 | level: level |
| 20 | } |
| 21 | return x |
| 22 | } |
| 23 | |
| 24 | // free frees the given ThreadSafeLog instance. |
| 25 | @[unsafe] |
| 26 | pub fn (mut x ThreadSafeLog) free() { |
| 27 | unsafe { |
| 28 | // make sure other threads are not in the blocks protected by the mutex: |
| 29 | if x.mu.try_lock() { |
| 30 | x.mu.unlock() |
| 31 | } |
| 32 | x.mu.destroy() |
| 33 | free(x.mu) |
| 34 | x.mu = nil |
| 35 | x.Log.free() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // set_level changes the log level |
| 40 | pub fn (mut x ThreadSafeLog) set_level(level Level) { |
| 41 | if unsafe { x.mu == 0 } { |
| 42 | return |
| 43 | } |
| 44 | x.mu.lock() |
| 45 | x.Log.set_level(level) |
| 46 | x.mu.unlock() |
| 47 | } |
| 48 | |
| 49 | // set_always_flush called with true, will make the log flush after every single .fatal(), .error(), .warn(), .info(), .debug() call. |
| 50 | // 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. |
| 51 | pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) { |
| 52 | if unsafe { x.mu == 0 } { |
| 53 | return |
| 54 | } |
| 55 | x.mu.lock() |
| 56 | x.Log.set_always_flush(should_flush) |
| 57 | x.mu.unlock() |
| 58 | } |
| 59 | |
| 60 | // debug logs a debug message |
| 61 | pub fn (mut x ThreadSafeLog) debug(s string) { |
| 62 | if unsafe { x.mu == 0 } { |
| 63 | return |
| 64 | } |
| 65 | x.mu.lock() |
| 66 | x.Log.debug(s) |
| 67 | x.mu.unlock() |
| 68 | } |
| 69 | |
| 70 | // info logs an info messagep |
| 71 | pub fn (mut x ThreadSafeLog) info(s string) { |
| 72 | if unsafe { x.mu == 0 } { |
| 73 | return |
| 74 | } |
| 75 | x.mu.lock() |
| 76 | x.Log.info(s) |
| 77 | x.mu.unlock() |
| 78 | } |
| 79 | |
| 80 | // warn logs a warning message |
| 81 | pub fn (mut x ThreadSafeLog) warn(s string) { |
| 82 | if unsafe { x.mu == 0 } { |
| 83 | return |
| 84 | } |
| 85 | x.mu.lock() |
| 86 | x.Log.warn(s) |
| 87 | x.mu.unlock() |
| 88 | } |
| 89 | |
| 90 | // error logs an error message |
| 91 | pub fn (mut x ThreadSafeLog) error(s string) { |
| 92 | if unsafe { x.mu == 0 } { |
| 93 | return |
| 94 | } |
| 95 | x.mu.lock() |
| 96 | x.Log.error(s) |
| 97 | x.mu.unlock() |
| 98 | } |
| 99 | |
| 100 | // fatal logs a fatal message, and panics |
| 101 | @[noreturn] |
| 102 | pub fn (mut x ThreadSafeLog) fatal(s string) { |
| 103 | if unsafe { x.mu == 0 } { |
| 104 | panic(s) |
| 105 | } |
| 106 | x.mu.lock() |
| 107 | defer { |
| 108 | // TODO: Log.fatal() is marked as noreturn, but this defer is allowed. |
| 109 | // Think whether it should be, or if it should be a compiler notice at least, |
| 110 | // since it would not be reached at all (.fatal() panics). |
| 111 | x.mu.unlock() |
| 112 | } |
| 113 | x.Log.fatal(s) |
| 114 | } |
| 115 | |