Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ErrorRef wrapper #327

Merged
merged 1 commit into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

* Add `ErrorRef` wrapper to enable logging error references.

### 2.8.0-beta.1 - 2023-09-09

* **BIG:** Updated to Rust 2018
Expand Down
30 changes: 30 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3357,6 +3357,8 @@ where
///
/// This struct is only available in `std` because the `Error` trait is not available
/// without `std`.
///
/// Use [`ErrorRef`] if you have an error reference.
#[cfg(feature = "std")]
pub struct ErrorValue<E: std::error::Error>(pub E);

Expand All @@ -3375,6 +3377,34 @@ where
}
}

/// A wrapper struct for serializing errors references.
///
/// This struct can be used to wrap types that don't implement `slog::Value` but
/// do implement `std::error::Error` so that they can be logged.
/// This is usually not used directly but using `#error` in the macros.
///
/// This struct is only available in `std` because the `Error` trait is not available
/// without `std`.
///
/// Use [`ErrorValue`] if you want to move ownership of the error value.
#[cfg(feature = "std")]
pub struct ErrorRef<'a, E: std::error::Error>(pub &'a E);

#[cfg(feature = "std")]
impl<'a, E> Value for ErrorRef<'a, E>
where
E: 'static + std::error::Error,
{
fn serialize(
&self,
_record: &Record<'_>,
key: Key,
serializer: &mut dyn Serializer,
) -> Result {
serializer.emit_error(key, self.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add an emit_error_ref method with a default implementation?

It is unfortunate we have the 'static bound, but that's probably a requirement for things like slog_async. An alternative would be using format_args! and emit_arguments, but that would loose information 😦.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, may be worth adding it in the future

}
}

#[cfg(feature = "nested-values")]
impl<T> Value for Serde<T>
where
Expand Down
56 changes: 56 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ mod no_imports {
mod std_only {
use super::super::*;

/// Error checking drain
///
/// This asserts that the message and formatted KV are the same.
Techcable marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone)]
struct CheckError;

Expand Down Expand Up @@ -56,6 +59,7 @@ mod std_only {

let mut serializer = ErrorSerializer(String::new());
values.serialize(record, &mut serializer).unwrap();
record.kv().serialize(record, &mut serializer).unwrap();
assert_eq!(serializer.0, format!("{}", record.msg()));
Ok(())
}
Expand Down Expand Up @@ -136,6 +140,15 @@ mod std_only {
slog_info!(logger, "foo");
}

#[test]
fn error_ref_fmt_no_source() {
let error = TestError::new("foo");
let error = &error;
let logger = Logger::root(CheckError, o!());
info!(logger, "foo"; "error" => ErrorRef(error));
slog_info!(logger, "foo"; "error" => ErrorRef(error));
}

#[test]
fn error_fmt_no_source_not_last() {
let logger = Logger::root(
Expand All @@ -146,6 +159,15 @@ mod std_only {
slog_info!(logger, "not-error: not-error; foo");
}

#[test]
fn error_ref_fmt_no_source_not_last() {
let error = TestError::new("foo");
let error = &error;
let logger = Logger::root(CheckError, o!());
info!(logger, "not-error: not-error; foo"; "error" => ErrorRef(error), "not-error" => "not-error");
slog_info!(logger, "not-error: not-error; foo"; "error" => ErrorRef(error), "not-error" => "not-error");
}

#[test]
fn error_fmt_no_source_last() {
let logger = Logger::root(
Expand All @@ -155,13 +177,33 @@ mod std_only {
info!(logger, "foonot-error: not-error; ");
slog_info!(logger, "foonot-error: not-error; ");
}

#[test]
fn error_ref_fmt_no_source_last() {
let error = TestError::new("foo");
let error = &error;
let logger = Logger::root(CheckError, o!());
info!(logger, "foonot-error: not-error; "; "not-error" => "not-error", "error" => ErrorRef(error));
slog_info!(logger, "foonot-error: not-error; "; "not-error" => "not-error", "error" => ErrorRef(error));
}

#[test]
fn error_fmt_single_source() {
let logger = Logger::root(
CheckError,
o!("error" => #TestError("foo", Some(TestError::new("bar")))),
);
info!(logger, "foo: bar");
slog_info!(logger, "foo: bar");
}

#[test]
fn error_ref_fmt_single_source() {
let error = TestError("foo", Some(TestError::new("bar")));
let error = &error;
let logger = Logger::root(CheckError, o!());
info!(logger, "foo: bar"; "error" => ErrorRef(error));
slog_info!(logger, "foo: bar"; "error" => ErrorRef(error));
}

#[test]
Expand All @@ -171,6 +213,19 @@ mod std_only {
o!("error" => #TestError("foo", Some(TestError("bar", Some(TestError::new("baz")))))),
);
info!(logger, "foo: bar: baz");
slog_info!(logger, "foo: bar: baz");
}

#[test]
fn error_ref_fmt_two_sources() {
let error = TestError(
"foo",
Some(TestError("bar", Some(TestError::new("baz")))),
);
let error = &error;
let logger = Logger::root(CheckError, o!());
info!(logger, "foo: bar: baz"; "error" => ErrorRef(error));
slog_info!(logger, "foo: bar: baz"; "error" => ErrorRef(error));
}

#[test]
Expand All @@ -179,6 +234,7 @@ mod std_only {
info!(logger, "not found"; "error" => std::io::Error::from(std::io::ErrorKind::NotFound));
// compiles?
info!(logger, "not found"; "error" => #std::io::Error::from(std::io::ErrorKind::NotFound));
info!(logger, "not found"; "error" => ErrorRef(&std::io::Error::from(std::io::ErrorKind::NotFound)));
}
}

Expand Down
Loading