diff --git a/inc/liblektor-rs/loging.h b/inc/liblektor-rs/loging.h new file mode 100644 index 0000000000000000000000000000000000000000..7bc84a6affb48ea3f7dc2c7c1a08fad4708a2e8f --- /dev/null +++ b/inc/liblektor-rs/loging.h @@ -0,0 +1,14 @@ +#if !defined(LIBLEKTOR_RS_LOGING___) +#define LIBLEKTOR_RS_LOGING___ + +#if defined(__cplusplus) +extern "C" { +#endif + +void lektor_init_rust_logging(void); + +#if defined(__cplusplus) +} +#endif + +#endif // LIBLEKTOR_RS_LOGING___ diff --git a/src/main/server.c b/src/main/server.c index e36d292568e4b4a2818075d628ea3cbffb589943..865cdea43f3bd1939807eb771d070d53262f198b 100644 --- a/src/main/server.c +++ b/src/main/server.c @@ -10,6 +10,7 @@ #include <lektor/logfile.h> #include <liblektor-rs/database.h> +#include <liblektor-rs/loging.h> #include <wait.h> #include <spawn.h> @@ -105,6 +106,7 @@ main(int argc, char *argv[]) struct lkt_logfile *logfile = lkt_logfile_new(srv.db); lkt_set_log_logfile(logfile); + lektor_init_rust_logging(); /* Read the configuration. We already know that the config is valid */ database_config_get_int(srv.db, "player", "autoclear", &autoclear); diff --git a/src/rust/liblektor-rs/lektor_unsafe/Cargo.toml b/src/rust/liblektor-rs/lektor_unsafe/Cargo.toml index fd41d724398af083e0efbfcfd91ad2ab8db16f7b..5bbff867e3aff47a096a4c0fc97a250b27a4d3ec 100644 --- a/src/rust/liblektor-rs/lektor_unsafe/Cargo.toml +++ b/src/rust/liblektor-rs/lektor_unsafe/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["staticlib"] [dependencies] log.workspace = true +lazy_static.workspace = true lektor_c_compat = { path = "../lektor_c_compat", features = ["c_types"] } lektor_repo = { path = "../lektor_repo" } diff --git a/src/rust/liblektor-rs/lektor_unsafe/src/lib.rs b/src/rust/liblektor-rs/lektor_unsafe/src/lib.rs index b99c1a476084ed7c7fa9c8ffa79c1628365d77a9..7df01add86dfd15231e490be4e9abf2919f5ae26 100644 --- a/src/rust/liblektor-rs/lektor_unsafe/src/lib.rs +++ b/src/rust/liblektor-rs/lektor_unsafe/src/lib.rs @@ -4,6 +4,7 @@ #![allow(unused_variables)] pub mod db; +pub mod loging; pub mod repo; pub(crate) use lektor_c_compat::{c::*, *}; diff --git a/src/rust/liblektor-rs/lektor_unsafe/src/loging.rs b/src/rust/liblektor-rs/lektor_unsafe/src/loging.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef1682e668fa9c7f7b7fce6b38eb9b7bdefb7e31 --- /dev/null +++ b/src/rust/liblektor-rs/lektor_unsafe/src/loging.rs @@ -0,0 +1,139 @@ +use lektor_c_compat::*; +use log::{Level, Metadata, Record}; +use std::{convert::Into, fmt::format, sync::atomic::AtomicU8}; + +enum LogLevel { + Debug, + Info, + Warning, + Error, +} + +struct CLogger { + level: AtomicU8, +} + +lazy_static::lazy_static! { + static ref LOGGER: CLogger = CLogger { + level: AtomicU8::new(Into::<c_int>::into(LogLevel::Warning) as u8), + }; +} + +impl log::Log for CLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + use LogLevel::*; + let lvl = LogLevel::try_from(LOGGER.level.load(std::sync::atomic::Ordering::SeqCst)) + .unwrap_or_default(); + matches!( + (lvl, LogLevel::from(metadata.level())), + (Debug, _) + | (Info, Info | Warning | Error) + | (Warning, Warning | Error) + | (Error, Error) + ) + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + unsafe { + ___lkt_log( + LogLevel::from(record.metadata().level()).into(), + (format!("{}\0", record.target())).as_ptr() as *const _, + b"*unknown-func*\0".as_ptr() as *const _, + (format!("{}\0", record.file_static().unwrap_or("..."))).as_ptr() as *const _, + record.line().unwrap_or_default() as i64, + b"%s\0".as_ptr() as *const _, + match record.args().as_str() { + Some(str) => format!("{str}\0"), + None => format!("{}\0", format(*record.args())), + }, + ) + } + } + } + + fn flush(&self) {} +} + +impl Default for LogLevel { + fn default() -> Self { + LogLevel::Info + } +} + +impl From<Level> for LogLevel { + fn from(lvl: Level) -> Self { + use LogLevel::*; + match lvl { + Level::Error => Error, + Level::Warn => Warning, + Level::Info => Info, + Level::Debug | Level::Trace => Debug, + } + } +} + +impl From<LogLevel> for u8 { + fn from(lvl: LogLevel) -> Self { + match lvl { + LogLevel::Debug => 4, + LogLevel::Info => 3, + LogLevel::Warning => 2, + LogLevel::Error => 1, + } + } +} + +impl From<LogLevel> for c_int { + fn from(lvl: LogLevel) -> Self { + (lvl as u8) as c_int + } +} + +impl TryFrom<c_int> for LogLevel { + type Error = (); + + fn try_from(value: c_int) -> Result<Self, <LogLevel as TryFrom<c_int>>::Error> { + use LogLevel::*; + match value { + 1 => Ok(Error), + 2 => Ok(Warning), + 3 => Ok(Info), + 4 => Ok(Debug), + _ => Err(()), + } + } +} + +impl TryFrom<u8> for LogLevel { + type Error = (); + + fn try_from(value: u8) -> Result<Self, <LogLevel as TryFrom<c_int>>::Error> { + TryFrom::<c_int>::try_from(value as c_int) + } +} + +extern "C" { + fn ___lkt_log( + log_level: c_int, + section: *const c_char, + function: *const c_char, + file: *const c_char, + line: i64, + fmt: *const c_char, + ... + ); + + fn lkt_get_log_level() -> c_int; +} + +#[no_mangle] +pub extern "C" fn lektor_init_rust_logging() { + LOGGER.level.store( + Into::<u8>::into(LogLevel::try_from(unsafe { lkt_get_log_level() }).unwrap_or_default()), + std::sync::atomic::Ordering::SeqCst, + ); + if let Err(err) = log::set_logger(&*LOGGER) { + panic!("failed to set logger: {err}") + } +}