Skip to content

Commit de65b4e

Browse files
committed
Use the module context for the standard logging.
1 parent 409e17e commit de65b4e

File tree

4 files changed

+97
-24
lines changed

4 files changed

+97
-24
lines changed

src/context/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,23 @@ impl CallOptionsBuilder {
124124
/// It is implemented `Send` and `Sync` so it can safely be used
125125
/// from within different threads.
126126
pub struct DetachedContext {
127-
ctx: AtomicPtr<raw::RedisModuleCtx>,
127+
pub(crate) ctx: AtomicPtr<raw::RedisModuleCtx>,
128128
}
129129

130-
impl Default for DetachedContext {
131-
fn default() -> Self {
130+
impl DetachedContext {
131+
pub const fn new() -> Self {
132132
DetachedContext {
133133
ctx: AtomicPtr::new(ptr::null_mut()),
134134
}
135135
}
136136
}
137137

138+
impl Default for DetachedContext {
139+
fn default() -> Self {
140+
Self::new()
141+
}
142+
}
143+
138144
impl DetachedContext {
139145
pub fn log(&self, level: RedisLogLevel, message: &str) {
140146
let c = self.ctx.load(Ordering::Relaxed);

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ pub use crate::raw::*;
4141
pub use crate::redismodule::*;
4242
use backtrace::Backtrace;
4343

44+
/// The detached Redis module context (the context of this module). It
45+
/// is only set to a proper value after the module is initialised via the
46+
/// provided [redis_module] macro.
47+
/// See [DetachedContext].
48+
pub static MODULE_CONTEXT: DetachedContext = DetachedContext::new();
49+
4450
pub fn base_info_func(
4551
ctx: &InfoContext,
4652
for_crash_report: bool,

src/logging.rs

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,27 @@ pub enum RedisLogLevel {
1717
Warning,
1818
}
1919

20-
pub(crate) fn log_internal(ctx: *mut raw::RedisModuleCtx, level: RedisLogLevel, message: &str) {
20+
impl From<log::Level> for RedisLogLevel {
21+
fn from(value: log::Level) -> Self {
22+
match value {
23+
log::Level::Error | log::Level::Warn => Self::Warning,
24+
log::Level::Info => Self::Notice,
25+
log::Level::Debug => Self::Debug,
26+
log::Level::Trace => Self::Verbose,
27+
}
28+
}
29+
}
30+
31+
pub(crate) fn log_internal<L: Into<RedisLogLevel>>(
32+
ctx: *mut raw::RedisModuleCtx,
33+
level: L,
34+
message: &str,
35+
) {
2136
if cfg!(test) {
2237
return;
2338
}
2439

25-
let level = CString::new(level.as_ref()).unwrap();
40+
let level = CString::new(level.into().as_ref()).unwrap();
2641
let fmt = CString::new(message).unwrap();
2742
unsafe {
2843
raw::RedisModule_Log.expect(NOT_INITIALISED_MESSAGE)(ctx, level.as_ptr(), fmt.as_ptr())
@@ -76,10 +91,14 @@ pub fn log_warning<T: AsRef<str>>(message: T) {
7691

7792
/// The [log] crate implementation of logging.
7893
pub mod standard_log_implementation {
94+
use std::sync::atomic::Ordering;
95+
96+
use crate::RedisError;
97+
7998
use super::*;
80-
use log::{Level, Metadata, Record, SetLoggerError};
99+
use log::{Metadata, Record, SetLoggerError};
81100

82-
static LOGGER: RedisGlobalLogger = RedisGlobalLogger;
101+
static mut LOGGER: RedisGlobalLogger = RedisGlobalLogger(ptr::null_mut());
83102

84103
/// The struct which has an implementation of the [log] crate's
85104
/// logging interface.
@@ -89,13 +108,32 @@ pub mod standard_log_implementation {
89108
/// Redis does not support logging at the [log::Level::Error] level,
90109
/// so logging at this level will be converted to logging at the
91110
/// [log::Level::Warn] level under the hood.
92-
struct RedisGlobalLogger;
111+
struct RedisGlobalLogger(*mut raw::RedisModuleCtx);
112+
113+
// The pointer of the Global logger can only be changed once during
114+
// the startup. Once one of the [std::sync::OnceLock] or
115+
// [std::sync::OnceCell] is stabilised, we can remove these unsafe
116+
// trait implementations in favour of using the aforementioned safe
117+
// types.
118+
unsafe impl Send for RedisGlobalLogger {}
119+
unsafe impl Sync for RedisGlobalLogger {}
93120

94121
/// Sets this logger as a global logger. Use this method to set
95122
/// up the logger. If this method is never called, the default
96123
/// logger is used which redirects the logging to the standard
97124
/// input/output streams.
98125
///
126+
/// # Note
127+
///
128+
/// The logging context (the module context [raw::RedisModuleCtx])
129+
/// is set by the [crate::redis_module] macro. If another context
130+
/// should be used, please consider using the [setup_for_context]
131+
/// method instead.
132+
///
133+
/// In case this function is invoked before the initialisation, and
134+
/// so without the redis module context, no context will be used for
135+
/// the logging, however, the logger will be set.
136+
///
99137
/// # Example
100138
///
101139
/// This function may be called on a module startup, within the
@@ -105,8 +143,22 @@ pub mod standard_log_implementation {
105143
/// [raw::Export_RedisModule_Init] function when loading the
106144
/// module).
107145
#[allow(dead_code)]
108-
pub fn setup() -> Result<(), SetLoggerError> {
109-
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Trace))
146+
pub fn setup() -> Result<(), RedisError> {
147+
let pointer = crate::MODULE_CONTEXT.ctx.load(Ordering::Relaxed);
148+
if pointer.is_null() {
149+
return Err(RedisError::Str(NOT_INITIALISED_MESSAGE));
150+
}
151+
setup_for_context(pointer)
152+
.map_err(|e| RedisError::String(format!("Couldn't set up the logger: {e}")))
153+
}
154+
155+
/// The same as [setup] but sets the custom module context.
156+
#[allow(dead_code)]
157+
pub fn setup_for_context(context: *mut raw::RedisModuleCtx) -> Result<(), SetLoggerError> {
158+
unsafe {
159+
LOGGER.0 = context;
160+
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Trace))
161+
}
110162
}
111163

112164
impl log::Log for RedisGlobalLogger {
@@ -119,20 +171,26 @@ pub mod standard_log_implementation {
119171
return;
120172
}
121173

122-
let message = format!(
123-
"'{}' {}:{}: {}",
124-
record.module_path().unwrap_or_default(),
125-
record.file().unwrap_or("Unknown"),
126-
record.line().unwrap_or(0),
127-
record.args()
128-
);
129-
130-
match record.level() {
131-
Level::Debug => log_debug(message),
132-
Level::Error | Level::Warn => log_warning(message),
133-
Level::Info => log_notice(message),
134-
Level::Trace => log_verbose(message),
135-
}
174+
let message = match record.level() {
175+
log::Level::Debug | log::Level::Trace => {
176+
format!(
177+
"'{}' {}:{}: {}",
178+
record.module_path().unwrap_or_default(),
179+
record.file().unwrap_or("Unknown"),
180+
record.line().unwrap_or(0),
181+
record.args()
182+
)
183+
}
184+
_ => {
185+
format!(
186+
"'{}' {}",
187+
record.module_path().unwrap_or_default(),
188+
record.args()
189+
)
190+
}
191+
};
192+
193+
log_internal(self.0, record.level(), &message);
136194
}
137195

138196
fn flush(&self) {

src/macros.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ macro_rules! redis_module {
212212
) } == raw::Status::Err as c_int { return raw::Status::Err as c_int; }
213213

214214
let context = $crate::Context::new(ctx);
215+
unsafe {
216+
let _ = $crate::MODULE_CONTEXT.set_context(&context);
217+
}
215218
let args = $crate::decode_args(ctx, argv, argc);
216219

217220
$(

0 commit comments

Comments
 (0)