Skip to content

Feature Request: downcasting Log, but for real #666

Open

Description

@1e1001

Seems #399 was already closed (4.5 years ago, for bad usecase & maybe backwards compatibility reasons?), but I have an actual use!

My logger writes a message on panic, but does so with a custom format different to anything you can write with the Log trait. So I need to be able to downcast the &dyn Log into my own logger from the panic handler. I could probably use a OnceLock to store if my logger's been initialized but log already does that with the (set_)logger methods, so it feels redundant.

For now my current way of doing this is this very cursed set of functions:

fn as_dyn_ref(logger: *const Logger) -> *const dyn Log {
  // split into one function to always attach the same metadata
  logger as *const dyn Log
}
fn upcast_log(logger: &'static Logger) -> &'static dyn Log {
  // SAFETY: as_dyn_ref returns a reference to the same object as passed in
  unsafe { &*as_dyn_ref(logger) }
}
fn downcast_log(log: &'static dyn Log) -> Option<&'static Logger> {
  // horribly cursed implementation to fetch a reference to the installed logger
  let (logger_ptr, logger_meta) = (&raw const *log).to_raw_parts();
  let (_, fake_logger_meta) = as_dyn_ref(ptr::null::<Logger>()).to_raw_parts();
  (logger_meta == fake_logger_meta).then(|| {
    // SAFETY: v-tables match so it's probably ours!
    unsafe { &*logger_ptr.cast::<Logger>() }
  })
}

but that's a lot of unsafe, and nightly-only.

To prevent needing to add a + 'static bound on Log (which probably breaks compatibility somehow), yandros suggested (in rplcs #dark-arts) adding something like:

fn type_id(&self) -> TypeId
  where Self: 'static
{
  TypeId::of<Self>()
}

as an automatic implementation on the Log trait.

Activity

  1. KodrAus commented on May 14, 2025

    @KodrAus
    Contributor

    Hi @1e1001 👋

    This is something I'd be open to exploring a bit more. We'd need to be careful adding a type_id method to the Log trait, because it would be unsound to override. The Error trait for instance defines its squirrel-type-id method like this to try get around that

  2. 1e1001 commented on May 21, 2025

    @1e1001
    Author

    Ah yeah that makes sense, in which case I'm not quite sure how one would implement that without some sort of + 'static or + Any bound on the Log trait itself (although I'm curious on how much real code that would actually break, given that the library only ever asks for &'static dyn Logs).

  3. djugei commented on Sep 19, 2026

    @djugei

    I am the author of indicatif-log-bridge and i would like the same feature, downcasting the &'static dyn Log to LogWrapper (the specific logger) to access the interal MultiProgress.

    i might be misinformed but i think this would be as simple as adding a simple function with a default implementation to Log

    trait Log {
     //...
      fn as_any(&'static self) -> &'static dyn Any {
        self
      }
    }
    

    due to the default implementation it would be backwards compatible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions