mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Add terminal color detection to core:log
This commit is contained in:
@@ -2,10 +2,12 @@
|
|||||||
#+build !orca
|
#+build !orca
|
||||||
package log
|
package log
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:encoding/ansi"
|
import "core:encoding/ansi"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
import "core:terminal"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
Level_Headers := [?]string{
|
Level_Headers := [?]string{
|
||||||
@@ -37,11 +39,36 @@ File_Console_Logger_Data :: struct {
|
|||||||
ident: string,
|
ident: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private) global_subtract_stdout_options: Options
|
||||||
|
@(private) global_subtract_stderr_options: Options
|
||||||
|
|
||||||
|
@(init, private)
|
||||||
|
init_standard_stream_status :: proc() {
|
||||||
|
// NOTE(Feoramund): While it is technically possible for these streams to
|
||||||
|
// be redirected during the runtime of the program, the cost of checking on
|
||||||
|
// every single log message is not worth it to support such an
|
||||||
|
// uncommonly-used feature.
|
||||||
|
if terminal.color_enabled {
|
||||||
|
// This is done this way because it's possible that only one of these
|
||||||
|
// streams could be redirected to a file.
|
||||||
|
if !terminal.is_terminal(os.stdout) {
|
||||||
|
global_subtract_stdout_options = {.Terminal_Color}
|
||||||
|
}
|
||||||
|
if !terminal.is_terminal(os.stderr) {
|
||||||
|
global_subtract_stderr_options = {.Terminal_Color}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Override any terminal coloring.
|
||||||
|
global_subtract_stdout_options = {.Terminal_Color}
|
||||||
|
global_subtract_stderr_options = {.Terminal_Color}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||||
data := new(File_Console_Logger_Data, allocator)
|
data := new(File_Console_Logger_Data, allocator)
|
||||||
data.file_handle = h
|
data.file_handle = h
|
||||||
data.ident = ident
|
data.ident = ident
|
||||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
return Logger{file_logger_proc, data, lowest, opt}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
|
destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||||
@@ -56,19 +83,15 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
|
|||||||
data := new(File_Console_Logger_Data, allocator)
|
data := new(File_Console_Logger_Data, allocator)
|
||||||
data.file_handle = os.INVALID_HANDLE
|
data.file_handle = os.INVALID_HANDLE
|
||||||
data.ident = ident
|
data.ident = ident
|
||||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
return Logger{console_logger_proc, data, lowest, opt}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
|
destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||||
free(log.data, allocator)
|
free(log.data, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
@(private)
|
||||||
data := cast(^File_Console_Logger_Data)logger_data
|
_file_console_logger_proc :: proc(h: os.Handle, ident: string, level: Level, text: string, options: Options, location: runtime.Source_Code_Location) {
|
||||||
h: os.Handle = os.stdout if level <= Level.Error else os.stderr
|
|
||||||
if data.file_handle != os.INVALID_HANDLE {
|
|
||||||
h = data.file_handle
|
|
||||||
}
|
|
||||||
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||||
buf := strings.builder_from_bytes(backing[:])
|
buf := strings.builder_from_bytes(backing[:])
|
||||||
|
|
||||||
@@ -86,13 +109,32 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
|||||||
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id())
|
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.ident != "" {
|
if ident != "" {
|
||||||
fmt.sbprintf(&buf, "[%s] ", data.ident)
|
fmt.sbprintf(&buf, "[%s] ", ident)
|
||||||
}
|
}
|
||||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||||
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||||
|
data := cast(^File_Console_Logger_Data)logger_data
|
||||||
|
_file_console_logger_proc(data.file_handle, data.ident, level, text, options, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||||
|
options := options
|
||||||
|
data := cast(^File_Console_Logger_Data)logger_data
|
||||||
|
h: os.Handle = ---
|
||||||
|
if level < Level.Error {
|
||||||
|
h = os.stdout
|
||||||
|
options -= global_subtract_stdout_options
|
||||||
|
} else {
|
||||||
|
h = os.stderr
|
||||||
|
options -= global_subtract_stderr_options
|
||||||
|
}
|
||||||
|
_file_console_logger_proc(h, data.ident, level, text, options, location)
|
||||||
|
}
|
||||||
|
|
||||||
do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) {
|
do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) {
|
||||||
|
|
||||||
RESET :: ansi.CSI + ansi.RESET + ansi.SGR
|
RESET :: ansi.CSI + ansi.RESET + ansi.SGR
|
||||||
|
|||||||
Reference in New Issue
Block a user