use std::ffi::{CStr, FromBytesUntilNulError};
use std::io::Write;
pub struct LogBuffer {
buffer: [u8; 1024],
buffer_len: usize,
}
impl Default for LogBuffer {
fn default() -> Self {
Self::new()
}
}
impl LogBuffer {
pub fn new() -> Self {
LogBuffer {
buffer: [0; 1024],
buffer_len: 0,
}
}
pub fn get_log(&mut self) -> Result<&CStr, FromBytesUntilNulError> {
self.buffer[self.buffer_len] = 0;
std::ffi::CStr::from_bytes_until_nul(self.buffer.as_slice())
}
pub fn clear(&mut self) {
self.buffer_len = 0;
}
}
impl Write for LogBuffer {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if self.buffer_len + buf.len() > self.buffer.len() {
return Err(std::io::Error::other("A single log message cannot exceed 1024 bytes"));
}
let mut i = 0;
for &byte in buf {
self.buffer[self.buffer_len + i] = byte;
i += 1;
}
self.buffer_len += i;
Ok(i)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[macro_export]
macro_rules! log {
($target:expr_2021, $lvl:expr_2021, $($arg:tt)+) => ({
use std::io::Write;
let interface = $target;
let level = $lvl;
if level <= interface.get_raw_log_level() {
let mut buffer = $crate::LogBuffer::new();
let result = write!(&mut buffer, $($arg)*).ok();
let log_line = match result {
Some(_) => buffer.get_log().expect("An empty log message was provided without a null terminating character"),
None => {
buffer.clear();
write!(&mut buffer, "Failed to write log message, log messages with more then 1024 bytes are not supported").unwrap();
buffer.get_log().unwrap()
}
};
interface.log(level, log_line);
}
});
}
#[macro_export]
macro_rules! audit {
($target:expr_2021, $($arg:tt)+) => ({
use std::io::Write;
let interface = $target;
let mut buffer = $crate::LogBuffer::new();
let result = write!(&mut buffer, $($arg)*).ok();
let log_line = match result {
Some(_) => buffer.get_log().expect("An empty log message was provided without a null terminating character"),
None => {
buffer.clear();
write!(&mut buffer, "Failed to write log message, log messages with more then 1024 bytes are not supported").unwrap();
buffer.get_log().unwrap()
}
};
interface.log_audit(log_line);
});
}
#[macro_export]
macro_rules! info {
($target:expr_2021, $($arg:tt)+) => ({
$crate::log!($target, gc_plugin_abi::raw::eGCLogLevel_INFO, $($arg)+)
});
}
#[macro_export]
macro_rules! warn {
($target:expr_2021, $($arg:tt)+) => ({
$crate::log!($target, gc_plugin_abi::raw::eGCLogLevel_WARNING, $($arg)+)
});
}
#[macro_export]
macro_rules! error {
($target:expr_2021, $($arg:tt)+) => ({
$crate::log!($target, gc_plugin_abi::raw::eGCLogLevel_ERROR, $($arg)+)
});
}
#[macro_export]
macro_rules! debug {
($target:expr_2021, $($arg:tt)+) => ({
$crate::log!($target, gc_plugin_abi::raw::eGCLogLevel_DEBUG, $($arg)+)
});
}
#[macro_export]
macro_rules! critical {
($target:expr_2021, $($arg:tt)+) => ({
$crate::log!($target, gc_plugin_abi::raw::eGCLogLevel_CRITICAL, $($arg)+)
});
}
#[doc(hidden)]
#[macro_export]
macro_rules! log_rate_limit_internal {
($target:expr_2021, $level:expr, $interval_secs:expr, $($arg:tt)*) => {{
use std::io::Write;
let level = $level;
let interface = $target;
if level <= interface.get_raw_log_level() {
static LAST_LOG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs();
let last = LAST_LOG.load(std::sync::atomic::Ordering::Relaxed);
#[allow(clippy::int_plus_one)]
if now >= last + $interval_secs {
LAST_LOG.store(now, std::sync::atomic::Ordering::Relaxed);
let mut buffer = $crate::LogBuffer::new();
let result = write!(&mut buffer, $($arg)*).ok();
let log_line = match result {
Some(_) => buffer.get_log().expect("An empty log message was provided without a null terminating character"),
None => {
buffer.clear();
write!(&mut buffer, "Failed to write log message, log messages with more then 1024 bytes are not supported").unwrap();
buffer.get_log().unwrap()
}
};
interface.log(level, log_line);
}
}
}};
}
#[macro_export]
macro_rules! error_limit {
($target:expr_2021, $interval_secs:expr, $($arg:tt)*) => {
$crate::log_rate_limit_internal!($target, gc_plugin_abi::raw::eGCLogLevel_ERROR, $interval_secs, $($arg)*)
};
}
#[macro_export]
macro_rules! warn_limit {
($target:expr_2021, $interval_secs:expr, $($arg:tt)*) => {
$crate::log_rate_limit_internal!($target, gc_plugin_abi::raw::eGCLogLevel_WARNING, $interval_secs, $($arg)*)
};
}
#[macro_export]
macro_rules! info_limit {
($target:expr_2021, $interval_secs:expr, $($arg:tt)*) => {
$crate::log_rate_limit_internal!($target, gc_plugin_abi::raw::eGCLogLevel_INFO, $interval_secs, $($arg)*)
};
}
#[macro_export]
macro_rules! debug_limit {
($target:expr_2021, $interval_secs:expr, $($arg:tt)*) => {
$crate::log_rate_limit_internal!($target, gc_plugin_abi::raw::eGCLogLevel_DEBUG, $interval_secs, $($arg)*)
};
}
#[macro_export]
macro_rules! critical_limit {
($target:expr_2021, $interval_secs:expr, $($arg:tt)*) => {
$crate::log_rate_limit_internal!($target, gc_plugin_abi::raw::eGCLogLevel_CRITICAL, $interval_secs, $($arg)*)
};
}