Good Example of how to setup a logger in Odin

I want to setup a logger which can be safely accessed by other threads. Help is appreciated.

One thing that you can do is wrap an existing logger inside another logger (a sync logger) that would synchronize its logger and reroute all the log procedure calls into the child logger. This should help synchronize multiple buffer flushes that are done under the hood in packages os and fmt.

sync_logger :: proc(l: log.Logger) -> log.Logger {
    Sync_Logger_Data :: struct {
        wrapped_logger: log.Logger,
        mutex: sync.Mutex,
    }
    sync_logger_proc :: proc(data: rawptr, level: log.Level, text: string, options: log.Options, location := #caller_location) {
        data := cast(^Sync_Logger_Data) data
        sync.mutex_lock(&data.mutex)
        data.wrapped_logger.procedure(data.wrapped_logger.data, level, text, options, location)
        sync.mutex_unlock(&data.mutex)
    }
    logger_data := new(Sync_Logger_Data, context.allocator)
    logger_data.wrapped_logger = context.logger
    logger_data.mutex = {}
    return log.Logger {
        data = logger_data,
        lowest_level = logger_data.wrapped_logger.lowest_level,
        options = logger_data.wrapped_logger.options,
        procedure = sync_logger_proc,
    }
}

However the downside of that approach is that you have to make sure you don’t call logger multiple times to get multiple log lines on one thread. Or at least, when you do, you do not expect these lines to be in-order. If this is a concern, I suggest using a recursive mutex instead of a plain mutex and add special procedures when a log needs to be synchronized across multiple messages.

log_sync_begin :: proc() {
    // You can make that mutex global or
    // get it from context.logger.data
    sync.mutex_lock(g_log_mutex)
}

log_sync_end :: proc() {
    // Same as above
    sync.mutex_unlock(g_log_mutex)
}
1 Like