@@ -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.
7893pub 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 ) {
0 commit comments