core: add std::error::Error as a new primitive type (#277)

## Motivation

Currently, errors are typically recorded using their `fmt::Display` or
`fmt::Debug` implementations. This doesn't give the subscriber much
control over how the error is recorded --- in particular, it cannot
decide whether to format the error using `Display` or `Debug`, and it
cannot access the error's `source` or downcast it to another error type.
The `std::error::Error` type is implemented by a majority of errors in
both the standard library and in most crates, so its use is fairly
widespread.

## Solution

This commit adds `dyn std::error::Error + 'static` as a new primitive
type. The `'static` bound is included so that the error can be downcast.

Closes: #222

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
Eliza Weisman 2019-08-15 08:44:10 -07:00 committed by GitHub
parent 59b57ae7a7
commit f6a375e29d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -121,11 +121,11 @@ pub struct Iter {
/// to be printed or stored in some other data structure.
///
/// The `Visit` trait provides default implementations for `record_i64`,
/// `record_u64`, `record_bool`, and `record_str` which simply forward the
/// recorded value to `record_debug`. Thus, `record_debug` is the only method
/// which a `Visit` implementation *must* implement. However, visitors may
/// override the default implementations of these functions in order to
/// implement type-specific behavior.
/// `record_u64`, `record_bool`, `record_str`, and `record_error`, which simply
/// forward the recorded value to `record_debug`. Thus, `record_debug` is the
/// only method which a `Visit` implementation *must* implement. However,
/// visitors may override the default implementations of these functions in
/// order to implement type-specific behavior.
///
/// Additionally, when a visitor receives a value of a type it does not care
/// about, it is free to ignore those values completely. For example, a
@ -162,6 +162,9 @@ pub struct Iter {
/// `examples/counters.rs`, which demonstrates a very simple metrics system
/// implemented using `tracing`.
///
/// **Note:** the `record_error` trait method is only available when the Rust
/// standard library is present, as it requires the `std::error::Error` trait.
///
/// [`Value`]: trait.Value.html
/// [recorded]: trait.Value.html#method.record
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
@ -190,6 +193,15 @@ pub trait Visit {
self.record_debug(field, &value)
}
/// Records a type implementing `Error`.
///
/// **Note**: this is only enabled when the Rust standard library is
/// present.
#[cfg(feature = "std")]
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
self.record_debug(field, &format_args!("{}", value))
}
/// Visit a value implementing `fmt::Debug`.
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug);
}
@ -313,6 +325,16 @@ impl Value for str {
}
}
#[cfg(feature = "std")]
impl crate::sealed::Sealed for dyn std::error::Error + 'static {}
#[cfg(feature = "std")]
impl Value for dyn std::error::Error + 'static {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_error(key, self)
}
}
impl<'a, T: ?Sized> crate::sealed::Sealed for &'a T where T: Value + crate::sealed::Sealed + 'a {}
impl<'a, T: ?Sized> Value for &'a T