mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Added the std::core::log for logging.
This commit is contained in:
@@ -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
217
lib/std/core/logging.c3
Normal 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"
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
@@ -15,6 +15,11 @@ struct NativeMutex
|
||||
}
|
||||
}
|
||||
|
||||
fn bool NativeMutex.is_initialized(&self)
|
||||
{
|
||||
return self.initialized;
|
||||
}
|
||||
|
||||
struct NativeTimedMutex
|
||||
{
|
||||
Win32_SRWLOCK srw_lock;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
54
test/test_suite/stdlib/logging.c3t
Normal file
54
test/test_suite/stdlib/logging.c3t
Normal 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
|
||||
Reference in New Issue
Block a user