Browse Source

Merge branch 'log_pr'

Mikkel Hjortshoej 6 năm trước cách đây
mục cha
commit
03957cee64

+ 1 - 2
.gitignore

@@ -18,7 +18,7 @@ bld/
 [Bb]in/
 [Oo]bj/
 [Ll]og/
-
+![Cc]ore/[Ll]og/
 # Visual Studio 2015 cache/options directory
 .vs/
 # Uncomment if you have tasks that create the project's static files in wwwroot
@@ -264,7 +264,6 @@ bin/
 odin
 odin.dSYM
 
-
 # shared collection
 shared/
 

+ 142 - 0
core/log/file_console_logger.odin

@@ -0,0 +1,142 @@
+package log
+
+import "core:fmt";
+import "core:os";
+import "core:time";
+
+Level_Headers := []string{
+    "[DEBUG] --- ",
+    "[INFO ] --- ",
+    "[WARN ] --- ",
+    "[ERROR] --- ",
+    "[FATAL] --- ",
+};
+
+Default_Console_Logger_Opts :: Options{
+    Option.Level, 
+    Option.Terminal_Color,
+    Option.Short_File_Path,
+    Option.Line, 
+    Option.Procedure, 
+} | Full_Timestamp_Opts;
+
+Default_File_Logger_Opts :: Options{
+    Option.Level, 
+    Option.Short_File_Path,
+    Option.Line, 
+    Option.Procedure, 
+} | Full_Timestamp_Opts;
+
+
+File_Console_Logger_Data :: struct {
+    lowest_level: Level,
+    file_handle:  os.Handle,
+    ident : string,
+}
+
+create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
+    data := new(File_Console_Logger_Data);
+    data.lowest_level = lowest;
+    data.file_handle = h;
+    data.ident = ident;
+    return Logger{file_console_logger_proc, data, opt};
+}
+
+destroy_file_logger ::proc(log : ^Logger) {
+    data := cast(^File_Console_Logger_Data)log.data;
+    if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
+    free(data);
+    log^ = nil_logger();
+}
+ 
+create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
+    data := new(File_Console_Logger_Data);
+    data.lowest_level = lowest;
+    data.file_handle = os.INVALID_HANDLE;
+    data.ident = ident;
+    return Logger{file_console_logger_proc, data, opt};
+}
+
+destroy_console_logger ::proc(log : ^Logger) {
+    free(log.data);
+    log^ = nil_logger();
+}
+
+file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
+    data := cast(^File_Console_Logger_Data)logger_data;
+    if level < data.lowest_level do return;
+
+    h : os.Handle;
+    if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle;
+    else                                      do h = level <= Level.Error ? os.stdout : os.stderr;
+    backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
+    buf := fmt.string_buffer_from_slice(backing[:]);
+    
+    do_level_header(options, level, &buf);
+
+    when time.IS_SUPPORTED {
+        if Full_Timestamp_Opts & options != nil {
+            fmt.sbprint(&buf, "[");
+            t := time.now(); 
+            y, m, d := time.date(t);
+            h, min, s := time.clock(t);
+            if Option.Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
+            if Option.Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
+            fmt.sbprint(&buf, "] ");
+        }
+    }
+
+    do_location_header(options, &buf, location);
+
+    if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
+    //TODO(Hoej): When we have better atomics and such, make this thread-safe
+    fmt.fprintf(h, "%s %s\n", fmt.to_string(buf), text); 
+}
+
+do_level_header :: proc(opts : Options, level : Level, buf : ^fmt.String_Buffer) {
+
+    RESET     :: "\x1b[0m";
+    RED       :: "\x1b[31m";
+    YELLOW    :: "\x1b[33m";
+    DARK_GREY :: "\x1b[90m";
+
+    col := RESET;
+    switch level {
+    case Level.Debug              : col = DARK_GREY;
+    case Level.Info               : col = RESET;
+    case Level.Warning            : col = YELLOW;
+    case Level.Error, Level.Fatal : col = RED;
+    }
+
+    if Option.Level in opts {
+        if Option.Terminal_Color in opts do fmt.sbprint(buf, col);
+        fmt.sbprint(buf, Level_Headers[level]);
+        if Option.Terminal_Color in opts do fmt.sbprint(buf, RESET);
+    }
+}
+
+do_location_header :: proc(opts : Options, buf : ^fmt.String_Buffer, location := #caller_location) {
+    if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
+
+    file := location.file_path;
+    if Option.Short_File_Path in opts {
+        when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/'; 
+        last := 0;
+        for r, i in location.file_path do if r == delimiter do last = i+1;
+        file = location.file_path[last:];
+    }
+
+    if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
+
+    if Option.Procedure in opts {
+        if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
+        fmt.sbprintf(buf, "%s()", location.procedure);
+    }
+
+    if Option.Line in opts {
+        if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":");
+        fmt.sbprint(buf, location.line);
+    }
+
+    fmt.sbprint(buf, "] ");
+}

+ 64 - 8
core/log/log.odin

@@ -1,5 +1,8 @@
 package log
 
+import "core:fmt";
+import "core:runtime";
+
 Level :: enum {
 	Debug,
 	Info,
@@ -9,26 +12,79 @@ Level :: enum {
 }
 
 Option :: enum {
-	Level,
-	Time,
-	File,
-	Line,
-	Procedure,
+    Level,
+    Date,
+    Time,
+    Short_File_Path,
+    Long_File_Path,
+    Line,
+    Procedure,
+    Terminal_Color
 }
+
 Options :: bit_set[Option];
+Full_Timestamp_Opts :: Options{
+    Option.Date,
+    Option.Time
+};
+Location_Header_Opts :: Options{
+    Option.Short_File_Path,
+    Option.Long_File_Path,
+    Option.Line,
+    Option.Procedure,
+};
+Location_File_Opts :: Options{
+    Option.Short_File_Path,
+    Option.Long_File_Path
+};
 
-Logger_Proc :: #type proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location);
+Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
 
 Logger :: struct {
 	procedure: Logger_Proc,
 	data:      rawptr,
+    options:   Options,
+}
+
+Multi_Logger_Data :: struct {
+    loggers : []Logger,
 }
 
+create_multi_logger :: proc(logs: ..Logger) -> Logger {
+    data := new(Multi_Logger_Data);
+    data.loggers = make([]Logger, len(logs));
+    copy(data.loggers, logs);
+    return Logger{multi_logger_proc, data, nil};
+}
 
-nil_logger_proc :: proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location) {
+destroy_multi_logger ::proc(log : ^Logger) {
+    free(log.data);
+    log^ = nil_logger();
+}
+
+multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, 
+                          options: Options, location := #caller_location) {
+    data := cast(^Multi_Logger_Data)logger_data;
+    if data.loggers == nil || len(data.loggers) == 0 do return;
+    for log in data.loggers do log.procedure(log.data, level, text, log.options, location);
+}
+
+nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
 	// Do nothing
 }
 
 nil_logger :: proc() -> Logger {
-	return Logger{nil_logger_proc, nil};
+	return Logger{nil_logger_proc, nil, nil};
 }
+
+debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug,   fmt_str=fmt_str, args=args, location=location);
+info  :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info,    fmt_str=fmt_str, args=args, location=location);
+warn  :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
+error :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error,   fmt_str=fmt_str, args=args, location=location);
+fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal,   fmt_str=fmt_str, args=args, location=location);
+
+logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
+    logger := context.logger;
+    str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
+    logger.procedure(logger.data, level, str, logger.options, location);
+}

+ 1 - 0
core/time/time_linux.odin

@@ -4,6 +4,7 @@ import "core:os";
 import "core:fmt";
 
 // NOTE(Jeroen): The times returned are in UTC
+IS_SUPPORTED :: false;
 
 now :: proc() -> Time {
 

+ 3 - 0
core/time/time_osx.odin

@@ -0,0 +1,3 @@
+package time
+
+IS_SUPPORTED :: false;

+ 3 - 1
core/time/time_windows.odin

@@ -2,6 +2,8 @@ package time
 
 import "core:sys/win32"
 
+IS_SUPPORTED :: true;
+
 now :: proc() -> Time {
 	file_time: win32.Filetime;
 
@@ -18,5 +20,5 @@ now :: proc() -> Time {
 
 
 sleep :: proc(d: Duration) {
-	win32.Sleep(u32(d/Millisecond));
+	win32.sleep(i32(d/Millisecond));
 }