How to wrap into interface (trait) and use macros?

I have a function which returns an slog::Logger as follows:

pub fn create_logger(level: Level) -> Arc<slog::Logger> {...}

inside a module in a crate A.

I would like to wrap this log type slog::Logger such that the user of this module does not know about the internal used log type.

I have a macro inside a module which I use in other modules which want to log an info as:

/// Log info level record
macro_rules! log_info(
    ($log:expr, #$tag:expr, $($args:tt)+) => {
        slog::log!($log, slog::Level::Info, $tag, $($args)+)
    ($log:expr, $($args:tt)+) => {
        slog::log!($log, slog::Level::Info, "", $($args)+)

pub use log_info as info;

used as log::info!(log, "bla");

If I wrap the return of create_logger into a trait (or struct with private member log).

pub fn create_logger() -> Arc<MyLog> {...}

struct MyLog {
  log: slog::Logger

and adjust the macro to use $log.log instead, will I be able to use the macro in other modules which have no access to the internal log private member?


Expanding a macro is (for the most part) equivalent to just writing out the code it expands to manually[1]. If external code can't access that field, neither can your macro when expanded in that same context.

The only way I can think of to completely hide the use of slog here would be to have your macro expand to code that does the same work slog::log does, but maintains the MyLog type until it passes all the accumulated information to your crate's log_impl function, which is what then unpacks MyLog and forwards that to whatever internal interface slog is using, assuming that interface is public and stable.

Or you could slap a #[doc(hidden)] on the log field and ask people to not mess with it?

  1. With the exception of some identifier hygiene shenanigans, but that's not relevant or useful here. ↩ī¸Ž


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.