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

Trial integrating spantrace extraction with tracing-subscriber via generic member access #1861

Closed
wants to merge 1 commit into from
Closed
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
133 changes: 133 additions & 0 deletions examples/examples/fmt-error-chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! This example demonstrates using the `tracing-error` crate's `SpanTrace` type
//! to attach a trace context to a custom error type.
#![deny(rust_2018_idioms)]
#![feature(provide_any)]
use std::any::Requisition;
use std::fmt;
use std::{error::Error, path::Path};
use tracing::{error, info};
use tracing_error::{ErrorSubscriber, SpanTrace};
use tracing_subscriber::prelude::*;

#[derive(Debug)]
struct FileError {
context: SpanTrace,
}

impl FileError {
fn new() -> Self {
Self {
context: SpanTrace::capture(),
}
}
}

impl Error for FileError {
fn provide<'a>(&'a self, mut req: Requisition<'a, '_>) {
req.provide_ref(&self.context)
.provide_ref::<tracing::Span>(self.context.as_ref());
}
}

impl fmt::Display for FileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("file does not exist")
}
}

#[tracing::instrument]
fn read_file(path: &Path) -> Result<String, FileError> {
Err(FileError::new())
}

#[derive(Debug)]
struct ConfigError {
source: FileError,
}

impl From<FileError> for ConfigError {
fn from(source: FileError) -> Self {
Self { source }
}
}

impl Error for ConfigError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.source)
}
}

impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("config file cannot be loaded")
}
}

#[tracing::instrument]
fn load_config() -> Result<String, ConfigError> {
let path = Path::new("my_config");
let config = read_file(&path)?;
Ok(config)
}

struct App {
// Imagine this is actually something we deserialize with serde
config: String,
}

impl App {
fn run() -> Result<(), AppError> {
let this = Self::init()?;
this.start()
}

fn init() -> Result<Self, ConfigError> {
let config = load_config()?;
Ok(Self { config })
}

fn start(&self) -> Result<(), AppError> {
// Pretend our actual application logic all exists here
info!("Loaded config: {}", self.config);
Ok(())
}
}

#[derive(Debug)]
struct AppError {
source: ConfigError,
}

impl From<ConfigError> for AppError {
fn from(source: ConfigError) -> Self {
Self { source }
}
}

impl Error for AppError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.source)
}
}

impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("config invalid")
}
}

#[tracing::instrument]
fn main() {
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::subscriber())
// The `ErrorSubscriber` subscriber layer enables the use of `SpanTrace`.
.with(ErrorSubscriber::default())
.init();

if let Err(e) = App::run() {
error!(
error = &e as &(dyn Error + 'static),
"App exited unsuccessfully"
);
}
}
6 changes: 6 additions & 0 deletions tracing-error/src/backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ impl SpanTrace {
}
}

impl AsRef<Span> for SpanTrace {
fn as_ref(&self) -> &Span {
&self.span
}
}

/// The current status of a SpanTrace, indicating whether it was captured or
/// whether it is empty for some other reason.
#[derive(Debug, PartialEq, Eq)]
Expand Down
10 changes: 0 additions & 10 deletions tracing-error/src/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,6 @@ where
span.extensions_mut().insert(fields);
}
}

unsafe fn downcast_raw(&self, id: TypeId) -> Option<NonNull<()>> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this appears to be dead code but doesn't produce a lint????!?

match id {
id if id == TypeId::of::<Self>() => Some(NonNull::from(self).cast()),
id if id == TypeId::of::<WithContext>() => {
Some(NonNull::from(&self.get_context).cast())
}
_ => None,
}
}
}

impl<C, F> ErrorSubscriber<C, F>
Expand Down
52 changes: 37 additions & 15 deletions tracing-subscriber/src/fmt/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,21 +1122,43 @@ impl<'a> field::Visit for DefaultVisitor<'a> {
}

fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
if let Some(source) = value.source() {
let italic = self.writer.italic();
self.record_debug(
field,
&format_args!(
"{} {}{}{}{}",
value,
italic.paint(field.name()),
italic.paint(".sources"),
self.writer.dimmed().paint("="),
ErrorSourceList(source)
),
)
} else {
self.record_debug(field, &format_args!("{}", value))
let source = value.source();
let spantrace = value.chain().find_map(|value| value.request_ref::<tracing::Span>());
Copy link
Collaborator Author

@yaahc yaahc Jan 26, 2022

Choose a reason for hiding this comment

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

this would be tracing_error::SpanTrace if it weren't for the circular dependency issue that I will need to navigate later if I ever want this to actually be a thing.

match (source, spantrace) {
(Some(source), None) => {
let italic = self.writer.italic();
self.record_debug(
field,
&format_args!(
"{} {}{}{}{}",
value,
italic.paint(field.name()),
italic.paint(".sources"),
self.writer.dimmed().paint("="),
ErrorSourceList(source)
),
)
},
(None, None) => self.record_debug(field, &format_args!("{}", value)),
(Some(source), Some(spantrace)) => {
let italic = self.writer.italic();
self.record_debug(
field,
&format_args!(
"{} {}{}{}{} {}{}{}{:?}",
value,
italic.paint(field.name()),
italic.paint(".sources"),
self.writer.dimmed().paint("="),
ErrorSourceList(source),
italic.paint(field.name()),
italic.paint(".span_context"),
self.writer.dimmed().paint("="),
spantrace
),
)
},
(None, Some(spantrace)) => unimplemented!(),
}
}

Expand Down
2 changes: 2 additions & 0 deletions tracing-subscriber/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@
// "needless".
#![allow(clippy::needless_update)]
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(error_in_core)]
#![feature(error_iter)]

#[cfg(feature = "alloc")]
extern crate alloc;
Expand Down