My goal
I'm trying to make my actix-web code more readable and came up with the idea that I want to extend Result with methods that transform errors into actix specific ones with status code etc. (so that I can hide some errors and give details to others depending on the used method).
I already had something similar in a previous project using an own declarative macro but it wasn't convenient in bigger chains as calling it using .macroname(args) isn't possible.
I already found out how I can use the location (filename and line number) from the calling code inside the method to include it in the message, but...
The log crate's macros (info, warn, ...) show the wrong module
The log messages include the source module of the log call what I consider very useful but when the auxiliary methods are used the module in which they're defined is shown in the log message instead.
I already tried to look at logs source but didn't find something that could help me.
I'll attach a slightly changed file that I'd like to use in my real actix project and a MWE with a Cargo.toml.
I completely understand why log is displaying the "wrong" module but I would still like to change it to make my extensions useful.
(I also don't think implementing From for actix errors is as useful, because I would like to give arguments to some methods.)
// Not my real error, I use [actix_web]
#[derive(Debug, thiserror::Error)]
pub enum FrameworkError {
#[error("400: {0}")]
Error400(Box<dyn std::error::Error>),
}
pub mod my_library {
pub mod extensions {
pub mod logging {
use crate::FrameworkError;
pub trait LogChangeErrForRespExt {
type R;
#[track_caller]
fn log_and_explain_400(self) -> Self::R;
}
impl<T, E: std::error::Error + 'static> LogChangeErrForRespExt for Result<T, E> {
type R = Result<T, FrameworkError>;
/// Logs the client error using [log::info] and returns the [Display] string
/// of the original error to the client
#[track_caller]
fn log_and_explain_400(self) -> Self::R {
// .map_err doesn't work as it is a function without #[track_caller]
match self {
Ok(ok) => Ok(ok),
Err(err) => {
let loc = std::panic::Location::caller();
// This log call *should* list [my_application_code::routes_example] as
// module
log::warn!(
"real location is {:?}:{}: Error: {err:?}",
loc.file(),
loc.line()
);
Err(FrameworkError::Error400(Box::new(err)))
}
}
}
}
}
}
}
use my_library::extensions::logging::LogChangeErrForRespExt;
pub mod my_application_code {
pub mod routes_example {
use crate::LogChangeErrForRespExt;
fn failing_function() -> Result<u32, std::num::ParseIntError> {
"abc".parse()
}
pub fn some_route() -> Result<u32, crate::FrameworkError> {
log::warn!("This log call is from the correct module");
let long_chain = failing_function().log_and_explain_400()? + 42; // line 61
Ok(long_chain)
}
}
}
fn main() {
let _ = dotenvy::dotenv();
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.init();
let failure = my_application_code::routes_example::some_route();
println!("{failure:?}");
}
[package]
name = "question-location-log"
version = "0.1.0"
edition = "2024"
[dependencies]
dotenvy = "0.15.7"
env_logger = "0.11.8"
log = "0.4.27"
thiserror = "2.0.14"
[2025-08-15T10:08:55Z WARN question_location_log::my_application_code::routes_example] This log call is from the correct module
[2025-08-15T10:08:55Z WARN question_location_log::my_library::extensions::logging] real location is "src/main.rs":61: Error: ParseIntError { kind: InvalidDigit }
Err(Error400(ParseIntError { kind: InvalidDigit }))
use std::fmt::{Debug, Display};
pub trait LogChangeErrForRespExt {
type R;
#[track_caller]
fn log_hide_res(self) -> Self::R;
#[track_caller]
fn log_and_explain_400(self) -> Self::R;
}
impl<T, E: Debug + Display + 'static> LogChangeErrForRespExt for Result<T, E> {
type R = Result<T, actix_web::error::Error>;
/// Logs the error using [log::warn] and replaces the error with
/// an [actix_web::error::ErrorInternalServerError] without details.
///
/// This is for security reasons so no private error information could be leaked.
#[track_caller]
fn log_hide_res(self) -> Self::R {
match self {
Ok(ok) => Ok(ok),
Err(err) => {
log::warn!("real location is {:?}: Msg: {err:?}", std::panic::Location::caller());
Err(actix_web::error::ErrorInternalServerError(
"Unexpected error occured",
))
}
}
}
/// Logs the client error using [log::info] and returns the [Display] string
/// of the original error to the client
#[track_caller]
fn log_and_explain_400(self) -> Self::R {
// .map_err doesn't work as it is a function without #[track_caller]
match self {
Ok(ok) => Ok(ok),
Err(err) => {
log::info!("real location is {:?}: Error: {err:?}", std::panic::Location::caller());
Err(actix_web::error::ErrorBadRequest(err))
}
}
}
}