Added the std::core::log for logging.

This commit is contained in:
Christoffer Lerno
2025-08-05 18:30:46 +02:00
parent abd3585c44
commit 0205ee8688
8 changed files with 304 additions and 2 deletions

View File

@@ -57,6 +57,10 @@ faultdef TYPE_MISMATCH @builtin;
Use `CAPACITY_EXCEEDED` when trying to add to a bounded list or similar.
*/
faultdef CAPACITY_EXCEEDED @builtin;
/*
Use `NOT_IMPLEMENTED` when something is conditionally available.
*/
faultdef NOT_IMPLEMENTED @builtin;
alias VoidFn = fn void();

217
lib/std/core/logging.c3 Normal file
View File

@@ -0,0 +1,217 @@
module std::core::log;
import std::io, std::thread, std::time, std::math::random;
const FULL_LOG = env::COMPILER_SAFE_MODE || $feature(FULL_LOG);
typedef LogCategory = inline char;
typedef LogTag = char[12];
const LogCategory CATEGORY_APPLICATION = 0;
const LogCategory CATEGORY_SYSTEM = 1;
const LogCategory CATEGORY_KERNEL = 2;
const LogCategory CATEGORY_AUDIO = 3;
const LogCategory CATEGORY_VIDEO = 4;
const LogCategory CATEGORY_RENDER = 5;
const LogCategory CATEGORY_INPUT = 6;
const LogCategory CATEGORY_NETWORK = 7;
const LogCategory CATEGORY_SOCKET = 8;
const LogCategory CATEGORY_SECURITY = 9;
const LogCategory CATEGORY_TEST = 10;
const LogCategory CATEGORY_ERROR = 11;
const LogCategory CATEGORY_ASSERT = 12;
const LogCategory CATEGORY_CRASH = 13;
const LogCategory CATEGORY_STATS = 14;
const LogCategory CATEGORY_CUSTOM_START = 100;
tlocal LogCategory default_category = CATEGORY_APPLICATION;
tlocal LogTag current_tag;
enum LogPriority : int
{
VERBOSE,
DEBUG,
INFO,
WARN,
ERROR,
CRITICAL,
}
interface Logger
{
fn void log(LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
}
macro void verbose(String fmt, ..., LogCategory category = default_category) => call_log(VERBOSE, category, fmt, $vasplat);
macro void debug(String fmt, ..., LogCategory category = default_category) => call_log(DEBUG, category, fmt, $vasplat);
macro void info(String fmt, ..., LogCategory category = default_category) => call_log(INFO, category, fmt, $vasplat);
macro void warn(String fmt, ..., LogCategory category = default_category) => call_log(WARN, category, fmt, $vasplat);
macro void error(String fmt, ..., LogCategory category = default_category) => call_log(ERROR, category, fmt, $vasplat);
macro void critical(String fmt, ..., LogCategory category = default_category) => call_log(CRITICAL, category, fmt, $vasplat);
macro void @category_scope(LogCategory new_category; @body)
{
LogCategory old = default_category;
default_category = new_category;
defer default_category = old;
@body();
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
macro void @tag_scope(String tag_prefix = ""; @body)
{
LogTag old = current_tag;
push_tag(tag_prefix);
defer current_tag = old;
@body();
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
macro void push_tag(String tag_prefix = "")
{
current_tag = create_tag(tag_prefix);
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
fn LogTag create_tag(String tag_prefix)
{
LogTag tag @noinit;
int start = 0;
foreach (int i, c : tag_prefix)
{
if (c == 0) break;
tag[start++] = c;
}
if (start > 0) tag[start++] = '_';
for (int i = start; i < tag.len; i++)
{
tag[i] = (char)rand_in_range('a', 'z');
}
return tag;
}
fn void set_priority_for_category(LogCategory category, LogPriority new_priority)
{
@atomic_store(config_priorities[category], new_priority, UNORDERED);
}
fn LogPriority get_priority_for_category(LogCategory category)
{
return @atomic_load(config_priorities[category], UNORDERED);
}
fn void set_priority_all(LogPriority new_priority)
{
for (int i = 0; i < config_priorities.len; i++)
{
@atomic_store(config_priorities[i], new_priority, UNORDERED);
}
}
fn void set_logger(Logger logger)
{
init();
if (!logger_mutex.is_initialized())
{
current_logger = logger;
current_logfn = &logger.log;
return;
}
logger_mutex.@in_lock()
{
current_logger = logger;
current_logfn = &logger.log;
};
}
macro void init()
{
log_init.call(fn () => (void)logger_mutex.init());
}
fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
{
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
if (priority < prio) return;
init();
bool locked = logger_mutex.is_initialized() && @ok(logger_mutex.lock());
Logger logger = current_logger;
LogFn logfn = current_logfn;
defer if (locked) (void)logger_mutex.unlock();
$if FULL_LOG:
logfn(logger.ptr, prio, category, current_tag, $$FILE, $$FUNC, $$LINE, fmt, args);
$else
logfn(logger.ptr, prio, category, current_tag, "", "", 0, fmt, args);
$endif
}
fn String? get_category_name(LogCategory category)
{
String val = category_names[category];
return val ?: NOT_FOUND?;
}
fn void set_category_name(LogCategory category, String name)
{
category_names[category] = name;
}
struct NullLogger (Logger)
{
void* dummy;
}
fn void NullLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic
{}
module std::core::log @private;
import std::io, std::thread, std::time;
struct StderrLogger (Logger) @if(env::LIBC)
{
void* dummy;
}
fn void StderrLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic @if(env::LIBC)
{
@stack_mem(256 + 64; Allocator mem)
{
DString str;
str.init(mem, 256);
str.appendf(fmt, ...args);
TzDateTime time = datetime::now().to_local();
io::eprintfn("[%02d:%02d:%02d:%04d] [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), priority, str);
};
}
alias LogFn = fn void(void*, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
LogFn current_logfn = @select(env::LIBC, (LogFn)&StderrLogger.log, (LogFn)&NullLogger.log);
OnceFlag log_init;
Mutex logger_mutex;
Logger current_logger = @select(env::LIBC, &stderr_logger, &null_logger);
StderrLogger stderr_logger @if (env::LIBC);
NullLogger null_logger;
LogPriority[256] config_priorities = { [0..255] = ERROR, [CATEGORY_APPLICATION] = INFO, [CATEGORY_TEST] = VERBOSE, [CATEGORY_ASSERT] = WARN};
String[256] category_names = {
[CATEGORY_APPLICATION] = "APP",
[CATEGORY_SYSTEM] = "SYSTEM",
[CATEGORY_KERNEL] = "KERNEL",
[CATEGORY_AUDIO] = "AUDIO",
[CATEGORY_VIDEO] = "VIDEO",
[CATEGORY_RENDER] = "RENDER",
[CATEGORY_INPUT] = "INPUT",
[CATEGORY_NETWORK] = "NETWORD",
[CATEGORY_SOCKET] = "SOCKET",
[CATEGORY_SECURITY] = "SECURITY",
[CATEGORY_TEST] = "TEST",
[CATEGORY_ERROR] = "ERROR",
[CATEGORY_ASSERT] = "ASSERT",
[CATEGORY_CRASH] = "CRASH",
[CATEGORY_STATS] = "STATS"
};

View File

@@ -254,7 +254,7 @@ macro void eprint(x)
@param x : "The value to print"
*>
macro void eprintn(x)
macro void eprintn(x = "")
{
(void)fprintn(io::stderr(), x);
}

View File

@@ -4,4 +4,24 @@ typedef NativeMutex = int;
typedef NativeTimedMutex = int;
typedef NativeConditionVariable = int;
typedef NativeOnceFlag = int;
typedef NativeThread = int;
typedef NativeThread = int;
fn void NativeOnceFlag.call_once(&flag, OnceFn func)
{
if (*flag == 0)
{
*flag = 1;
func();
}
}
fn void? NativeMutex.init(&mtx, MutexType type) => NOT_IMPLEMENTED?;
fn bool NativeMutex.is_initialized(&self)
{
return false;
}
macro void? NativeMutex.lock(&mutex) => NOT_IMPLEMENTED?;
macro bool NativeMutex.try_lock(&mutex) => NOT_IMPLEMENTED?;
macro void? NativeMutex.unlock(&mutex) => NOT_IMPLEMENTED?;

View File

@@ -15,6 +15,11 @@ struct NativeMutex
}
}
fn bool NativeMutex.is_initialized(&self)
{
return self.initialized;
}
struct NativeTimedMutex
{
Win32_SRWLOCK srw_lock;

View File

@@ -34,6 +34,7 @@ faultdef
CHANNEL_CLOSED;
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {});
macro bool Mutex.is_initialized(mutex) => ((NativeMutex*)&mutex).is_initialized();
macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive});
macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex);
macro void? Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex);

View File

@@ -26,6 +26,7 @@
- Add `vm::mmap_file` to memory map a file.
- Updated hash functions in default hash methods.
- Added `FixedBlockPool` which is a memory pool for fixed size blocks.
- Added the experimental `std::core::log` for logging.
## 0.7.4 Change list

View File

@@ -0,0 +1,54 @@
// #target: macos-x64
module test;
import std;
fn void main()
{
log::@category_scope(log::CATEGORY_KERNEL)
{
log::@tag_scope("YO")
{
log::info("Hello");
};
};
}
/* #expect: test.ll
define void @test.main() #0 {
entry:
%old = alloca i8, align 1
%old1 = alloca [12 x i8], align 1
%result = alloca [12 x i8], align 1
%tempcoerce = alloca { i64, i32 }, align 8
%category = alloca i8, align 1
%0 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.default_category)
%1 = load i8, ptr %0, align 1
store i8 %1, ptr %old, align 1
%2 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.default_category)
store i8 2, ptr %2, align 1
call void @llvm.assume(i1 true)
%3 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.current_tag)
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %old1, ptr align 1 %3, i32 12, i1 false)
call void @llvm.assume(i1 true)
%4 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.current_tag)
%5 = call { i64, i32 } @std.core.log.create_tag(ptr @.str, i64 2)
store { i64, i32 } %5, ptr %tempcoerce, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %result, ptr align 8 %tempcoerce, i32 12, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %4, ptr align 1 %result, i32 12, i1 false)
%6 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.default_category)
%7 = load i8, ptr %6, align 1
store i8 %7, ptr %category, align 1
%8 = load i8, ptr %category, align 1
call void @std.core.log.call_log(i32 2, i8 zeroext %8, ptr @.str.1, i64 5, ptr null, i64 0)
%9 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.current_tag)
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %9, ptr align 1 %old1, i32 12, i1 false)
%10 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.log.default_category)
%11 = load i8, ptr %old, align 1
store i8 %11, ptr %10, align 1
ret void
}
declare extern_weak { i64, i32 } @std.core.log.create_tag(ptr, i64) #0
declare extern_weak void @std.core.log.call_log(i32, i8 zeroext, ptr, i64, ptr, i64) #0