2024-05-31 08:26:52 -07:00
package grime
2024-02-08 19:33:53 -08:00
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:os"
2024-02-27 04:50:57 -08:00
import str "core:strings"
2024-02-08 19:33:53 -08:00
import "core:time"
import core_log "core:log"
2024-05-21 20:35:36 -07:00
Max_Logger_Message_Width : : 180
2024-02-08 19:33:53 -08:00
LogLevel : : core_log . Level
Logger : : struct {
file_path : string ,
file : os . Handle ,
id : string ,
}
2024-02-23 06:36:23 -08:00
to_odin_logger : : proc ( logger : ^ Logger ) -> core_log . Logger {
2024-02-08 19:33:53 -08:00
return { logger_interface , logger , core_log . Level . Debug , core_log . Default_File_Logger_Opts }
}
2024-02-27 04:50:57 -08:00
logger_init : : proc ( logger : ^ Logger , id : string , file_path : string , file := os . INVALID_HANDLE )
2024-02-08 19:33:53 -08:00
{
if file = = os . INVALID_HANDLE
{
2024-02-27 04:50:57 -08:00
logger_file , result_code := file_open ( file_path , os . O_RDWR | os . O_CREATE )
2024-05-31 08:26:52 -07:00
assert ( result_code = = os . ERROR_NONE , "Log failures are fatal and must never occur at runtime (there is no logging)" )
2024-02-08 19:33:53 -08:00
logger . file = logger_file
2024-06-19 02:50:40 -07:00
os . truncate ( file_path , 0 )
2024-02-08 19:33:53 -08:00
}
else {
logger . file = file
}
logger . file_path = file_path
logger . id = id
context . logger = { logger_interface , logger , core_log . Level . Debug , core_log . Default_File_Logger_Opts }
log ( "Initialized Logger" )
when false {
2024-03-18 08:44:58 -07:00
log ( "This sentence is over 80 characters long on purpose to test the ability of this logger to properfly wrap long as logs with a new line and then at the end of that pad it with the appropraite signature." )
2024-02-08 19:33:53 -08:00
}
}
2024-02-23 06:36:23 -08:00
logger_interface : : proc (
2024-02-08 19:33:53 -08:00
logger_data : rawptr ,
level : core_log . Level ,
text : string ,
options : core_log . Options ,
location := #caller_location )
{
logger := cast ( ^ Logger ) logger_data
@static builder_backing : [ 16 * Kilobyte ] byte ; {
mem . set ( raw_data ( builder_backing [ : ] ) , 0 , len ( builder_backing ) )
}
2024-02-27 04:50:57 -08:00
builder := str . builder_from_bytes ( builder_backing [ : ] )
2024-02-08 19:33:53 -08:00
first_line_length := len ( text ) > Max_Logger_Message_Width ? Max_Logger_Message_Width : len ( text )
first_line := transmute ( string ) text [ 0 : first_line_length ]
2024-05-22 00:28:16 -07:00
// str_fmt_builder( & builder, "%-s ", Max_Logger_Message_Width, first_line )
str_fmt_builder ( & builder , "%-180s " , first_line )
2024-02-08 19:33:53 -08:00
// Signature
{
when time . IS_SUPPORTED
{
if core_log . Full_Timestamp_Opts & options != nil {
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "[" )
2024-02-08 19:33:53 -08:00
t := time . now ( )
2024-02-27 04:50:57 -08:00
year , month , day := time . date ( t )
hour , minute , second := time . clock ( t )
2024-02-08 19:33:53 -08:00
if . Date in options {
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "%d-%02d-%02d " , year , month , day )
2024-02-08 19:33:53 -08:00
}
if . Time in options {
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "%02d:%02d:%02d" , hour , minute , second )
2024-02-08 19:33:53 -08:00
}
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "] " )
2024-02-08 19:33:53 -08:00
}
}
2024-06-06 13:15:57 -07:00
core_log . do_level_header ( options , & builder , level )
2024-02-08 19:33:53 -08:00
if logger . id != "" {
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "[%s] " , logger . id )
2024-02-08 19:33:53 -08:00
}
core_log . do_location_header ( options , & builder , location )
}
// Oversized message handling
if len ( text ) > Max_Logger_Message_Width
{
offset := Max_Logger_Message_Width
2024-02-27 04:50:57 -08:00
bytes := transmute ( [ ] u8 ) text
2024-02-08 19:33:53 -08:00
for left := len ( bytes ) - Max_Logger_Message_Width ; left > 0 ; left -= Max_Logger_Message_Width
{
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "\n" )
2024-02-08 19:33:53 -08:00
subset_length := len ( text ) - offset
if subset_length > Max_Logger_Message_Width {
subset_length = Max_Logger_Message_Width
}
subset := slice_ptr ( ptr_offset ( raw_data ( bytes ) , offset ) , subset_length )
2024-02-27 04:50:57 -08:00
str_fmt_builder ( & builder , "%s" , transmute ( string ) subset )
2024-02-08 19:33:53 -08:00
offset += Max_Logger_Message_Width
}
}
2024-02-27 04:50:57 -08:00
str_to_file_ln ( logger . file , to_string ( builder ) )
2024-02-08 19:33:53 -08:00
}
2024-05-25 08:52:23 -07:00
// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators.
2024-05-31 10:04:52 -07:00
// This means a single line is limited to 32k buffer (increase naturally if this SOMEHOW becomes a bottleneck...)
2024-05-25 08:52:23 -07:00
Logger_Allocator_Buffer : [ 32 * Kilobyte ] u8
2024-05-20 19:05:52 -07:00
2024-02-23 06:36:23 -08:00
log : : proc ( msg : string , level := LogLevel . Info , loc := #caller_location ) {
2024-05-25 08:52:23 -07:00
temp_arena : Arena ; arena_init ( & temp_arena , Logger_Allocator_Buffer [ : ] )
context . allocator = arena_allocator ( & temp_arena )
context . temp_allocator = arena_allocator ( & temp_arena )
2024-05-14 08:47:44 -07:00
core_log . log ( level , msg , location = loc )
2024-02-08 19:33:53 -08:00
}
2024-02-23 06:36:23 -08:00
logf : : proc ( fmt : string , args : . . any , level := LogLevel . Info , loc := #caller_location ) {
2024-05-25 08:52:23 -07:00
temp_arena : Arena ; arena_init ( & temp_arena , Logger_Allocator_Buffer [ : ] )
context . allocator = arena_allocator ( & temp_arena )
context . temp_allocator = arena_allocator ( & temp_arena )
2024-05-21 20:35:36 -07:00
core_log . logf ( level , fmt , . . args , location = loc )
2024-02-08 19:33:53 -08:00
}