log.cc (2631B)
1 #include <stdio.h> 2 #include <time.h> 3 4 #include "intrinsics.h" 5 #include "log.h" 6 #include "str.h" 7 #include "dynstr.h" 8 #include "patterns.h" 9 #include "platform_atomic.h" 10 #include "platform_thread.h" 11 #include "platform_mutex.h" 12 #include "platform_home.h" 13 14 static FILE * streams[ LOGLEVEL_COUNT + 1 ]; 15 static DynamicString logs_dir; 16 17 static const char * file_names[ LOGLEVEL_COUNT + 1 ] = { 18 "fatals", 19 "warnings", 20 "info", 21 "all", 22 }; 23 24 #define MAX_THREADS 32 25 26 struct LoggerThread { 27 u64 id; 28 str< 32 > name; 29 }; 30 31 static LoggerThread threads[ MAX_THREADS ]; 32 static atomic_u32 num_threads; 33 34 static Mutex mutex; 35 36 static void recursive_mkdir( const char * path ) { 37 DynamicString cur; 38 if( path[ 0 ] == '/' ) { 39 cur += '/'; 40 } 41 // TODO: test this with drives on windows! 42 // TODO: error checking 43 for( array< array< const char > > matches : gmatch( path, "([^/\\]+)" ) ) { 44 cur.append( matches[ 0 ].ptr(), matches[ 0 ].n ); 45 cur += '/'; 46 mkdir( cur.c_str(), 0755 ); 47 } 48 } 49 50 void logger_init() { 51 mutex_init( &mutex ); 52 53 #if RELEASE_BUILD 54 get_config_directory( logs_dir ); 55 recursive_mkdir( logs_dir.c_str() ); 56 #else 57 mkdir( "logs", 0755 ); 58 59 u64 now = checked_cast< u64 >( time( NULL ) ); 60 logs_dir.sprintf( "logs/{}", now ); 61 mkdir( logs_dir.c_str(), 0755 ); 62 #endif 63 64 for( u32 i = 0; i <= LOGLEVEL_COUNT; i++ ) { 65 const str< 256 > path( "{}/{}.log", logs_dir, file_names[ i ] ); 66 streams[ i ] = fopen( path.c_str(), "w" ); 67 ASSERT( streams[ i ] != NULL ); 68 } 69 } 70 71 void logger_term() { 72 for( u32 i = 0; i <= LOGLEVEL_COUNT; i++ ) { 73 fclose( streams[ i ] ); 74 } 75 } 76 77 // TODO: this should really use ggformat too 78 void logger_thread_name( const char * name ) { 79 u32 i = fetch_add_acqrel( &num_threads, 1 ); 80 ASSERT( i < MAX_THREADS ); 81 82 LoggerThread & thread = threads[ i ]; 83 thread.id = thread_getid(); 84 thread.name.sprintf( "[{}] ", name ); 85 } 86 87 // TODO: print time? 88 void logger_log( LogLevel level, const char * message ) { 89 str< 1024 > buf; 90 91 u64 thread_id = thread_getid(); 92 u32 n = load_acquire( &num_threads ); 93 for( u32 i = 0; i < n; i++ ) { 94 if( threads[ i ].id == thread_id ) { 95 buf += threads[ i ].name; 96 } 97 } 98 99 if( buf.len() == 0 ) { 100 buf.appendf( "[thread {}] ", thread_id ); 101 } 102 103 buf += message; 104 if( buf[ buf.len() - 1 ] != '\n' ) { 105 buf += "\n"; 106 } 107 108 FILE * console_stream = level == LOGLEVEL_INFO ? stdout : stderr; 109 { 110 SCOPED_MUTEX_LOCK( &mutex ); 111 fputs( buf.c_str(), console_stream ); 112 fputs( buf.c_str(), streams[ level ] ); 113 fputs( buf.c_str(), streams[ LOGLEVEL_COUNT ] ); 114 } 115 116 fflush( streams[ level ] ); 117 fflush( streams[ LOGLEVEL_COUNT ] ); 118 } 119 120 const char * logger_get_logs_dir() { 121 return logs_dir.c_str(); 122 }