medfall

A super great game engine
Log | Files | Refs

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 }