failure_ext/
slogkv.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10use std::error::Error as StdError;
11use std::ops::Deref;
12
13use futures::future::SharedError;
14
15use super::Compat;
16use super::Error;
17
18/// Wrapper around [Error] that implements [slog::KV] trait, so it might be used in [slog] logging
19pub struct SlogKVError(pub Error);
20
21impl slog::KV for SlogKVError {
22    fn serialize(
23        &self,
24        _record: &slog::Record<'_>,
25        serializer: &mut dyn slog::Serializer,
26    ) -> slog::Result {
27        let err = &self.0;
28
29        serializer.emit_str(Error.into_str(), &format!("{err}"))?;
30        serializer.emit_str(ErrorDebug.into_str(), &format!("{err:#?}"))?;
31
32        #[cfg(fbcode_build)]
33        {
34            let backtrace = err.backtrace();
35            if let std::backtrace::BacktraceStatus::Captured = backtrace.status() {
36                serializer.emit_str(Backtrace.into_str(), &backtrace.to_string())?;
37            }
38        }
39
40        let mut err = err.deref() as &dyn StdError;
41        while let Some(cause) = cause_workaround(err) {
42            serializer.emit_str(Cause.into_str(), &format!("{cause}"))?;
43            err = cause;
44        }
45        serializer.emit_str(RootCause.into_str(), &format!("{err}"))?;
46
47        Ok(())
48    }
49}
50
51impl slog::KV for SlogKVErrorWithoutBackTrace {
52    fn serialize(
53        &self,
54        _record: &slog::Record<'_>,
55        serializer: &mut dyn slog::Serializer,
56    ) -> slog::Result {
57        let error = &self.0;
58        serializer.emit_str(Error.into_str(), &format!("{error}"))?;
59        serializer.emit_str(ErrorDebug.into_str(), &format!("{error:#?}"))?;
60
61        let mut error = error.deref() as &dyn StdError;
62        while let Some(cause) = cause_workaround(error) {
63            serializer.emit_str(Cause.into_str(), &format!("{cause}"))?;
64            error = cause;
65        }
66        serializer.emit_str(RootCause.into_str(), &format!("{error}"))?;
67
68        Ok(())
69    }
70}
71
72/// Wrapper around [Error] that implements [slog::KV] trait, but without backtraces
73pub struct SlogKVErrorWithoutBackTrace(pub Error);
74
75/// Enum used in [slog::Serializer] implementation when [SlogKVError] is used
76#[derive(Debug, Clone, Eq, PartialEq)]
77pub enum SlogKVErrorKey {
78    /// The error that is being logged
79    Error,
80    /// Root cause of the chain of errors
81    RootCause,
82    /// Backtrace taken when error occured
83    Backtrace,
84    /// One of causes in a chain of errors
85    Cause,
86    /// The error that is being logged, but in debug format
87    ErrorDebug,
88}
89use crate::SlogKVErrorKey::*;
90
91impl SlogKVErrorKey {
92    /// Return string representations of enum values
93    pub fn into_str(self) -> &'static str {
94        match self {
95            Error => "error",
96            RootCause => "root_cause",
97            Backtrace => "backtrace",
98            Cause => "cause",
99            ErrorDebug => "error_debug",
100        }
101    }
102}
103
104impl ::std::str::FromStr for SlogKVErrorKey {
105    type Err = ();
106
107    fn from_str(s: &str) -> Result<Self, Self::Err> {
108        match s {
109            "error" => Ok(Error),
110            "root_cause" => Ok(RootCause),
111            "backtrace" => Ok(Backtrace),
112            "cause" => Ok(Cause),
113            "error_debug" => Ok(ErrorDebug),
114            _ => Err(()),
115        }
116    }
117}
118
119/// Like Fail::cause, but handles SharedError whose Fail implementation
120/// does not return the right underlying error.
121pub fn cause_workaround(fail: &dyn StdError) -> Option<&dyn StdError> {
122    let mut cause = fail.source()?;
123    if let Some(shared) = cause.downcast_ref::<SharedError<Compat<Error>>>() {
124        cause = shared.0.deref();
125    }
126    Some(cause)
127}