Skip to content

Commit dc13d07

Browse files
Copilotheaths
andauthored
Add backtrace capture to azure_core::Error (Azure#4430)
Captures `std::backtrace::Backtrace` in `typespec::Error` at construction time when `RUST_BACKTRACE=1` is set. The backtrace is stored as `Option<Box<Backtrace>>` to avoid heap allocation when not captured, and is visible via `{:?}` formatting. `Debug` is implemented manually so the field is omitted (with `finish_non_exhaustive`) when `None` and shown without the `Option` wrapper (with `finish`) when present. `Display` is unchanged. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: heaths <1532486+heaths@users.noreply.github.com>
1 parent 3cbb32a commit dc13d07

4 files changed

Lines changed: 57 additions & 9 deletions

File tree

sdk/core/azure_core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- `Error` now captures a `std::backtrace::Backtrace` at construction time and includes it in `Debug` output (e.g., `{:?}`) when `RUST_BACKTRACE=1` is set.
8+
79
### Breaking Changes
810

911
### Bugs Fixed

sdk/core/typespec/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- `Error` now captures a `std::backtrace::Backtrace` at construction time and includes it in `Debug` output (e.g., `{:?}`) when `RUST_BACKTRACE=1` is set.
8+
79
### Breaking Changes
810

911
### Bugs Fixed

sdk/core/typespec/src/error/mod.rs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
66
#[cfg(feature = "http")]
77
use crate::http::{RawResponse, StatusCode};
8-
use std::borrow::Cow;
9-
use std::fmt::{Debug, Display};
8+
use std::{
9+
backtrace::{Backtrace, BacktraceStatus},
10+
borrow::Cow,
11+
fmt,
12+
};
1013

1114
/// A convenience alias for `Result` where the error type is hard coded to [`Error`].
1215
pub type Result<T> = std::result::Result<T, Error>;
@@ -45,12 +48,13 @@ impl ErrorKind {
4548
pub fn into_error(self) -> Error {
4649
Error {
4750
context: Repr::Simple(self),
51+
backtrace: capture_backtrace(),
4852
}
4953
}
5054
}
5155

52-
impl Display for ErrorKind {
53-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56+
impl fmt::Display for ErrorKind {
57+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5458
match self {
5559
#[cfg(feature = "http")]
5660
ErrorKind::HttpResponse {
@@ -70,9 +74,22 @@ impl Display for ErrorKind {
7074
}
7175

7276
/// An error encountered when communicating with the service.
73-
#[derive(Debug)]
7477
pub struct Error {
7578
context: Repr,
79+
// Only `Some` when `RUST_BACKTRACE` is set; boxed so the `Some` variant
80+
// doesn't inflate `Error` beyond `clippy::result_large_err` limits.
81+
backtrace: Option<Box<Backtrace>>,
82+
}
83+
84+
impl fmt::Debug for Error {
85+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86+
let mut dbg = f.debug_struct("Error");
87+
dbg.field("context", &self.context);
88+
if let Some(backtrace) = &self.backtrace {
89+
return dbg.field("backtrace", backtrace).finish();
90+
}
91+
dbg.finish_non_exhaustive()
92+
}
7693
}
7794

7895
impl Error {
@@ -86,6 +103,7 @@ impl Error {
86103
kind,
87104
error: error.into(),
88105
}),
106+
backtrace: capture_backtrace(),
89107
}
90108
}
91109

@@ -104,6 +122,7 @@ impl Error {
104122
},
105123
message.into(),
106124
),
125+
backtrace: capture_backtrace(),
107126
}
108127
}
109128

@@ -126,6 +145,7 @@ impl Error {
126145
{
127146
Self {
128147
context: Repr::SimpleMessage(kind, message.into()),
148+
backtrace: capture_backtrace(),
129149
}
130150
}
131151

@@ -244,6 +264,7 @@ impl From<ErrorKind> for Error {
244264
fn from(kind: ErrorKind) -> Self {
245265
Self {
246266
context: Repr::Simple(kind),
267+
backtrace: capture_backtrace(),
247268
}
248269
}
249270
}
@@ -303,12 +324,12 @@ impl From<core::convert::Infallible> for Error {
303324
}
304325
}
305326

306-
impl Display for Error {
307-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327+
impl fmt::Display for Error {
328+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308329
match &self.context {
309-
Repr::Simple(kind) => std::fmt::Display::fmt(&kind, f),
330+
Repr::Simple(kind) => fmt::Display::fmt(&kind, f),
310331
Repr::SimpleMessage(_, message) => f.write_str(message),
311-
Repr::Custom(Custom { error, .. }) => std::fmt::Display::fmt(&error, f),
332+
Repr::Custom(Custom { error, .. }) => fmt::Display::fmt(&error, f),
312333
Repr::CustomMessage(_, message) => f.write_str(message),
313334
}
314335
}
@@ -367,6 +388,7 @@ where
367388
},
368389
message.into(),
369390
),
391+
backtrace: capture_backtrace(),
370392
})
371393
}
372394

@@ -380,6 +402,16 @@ where
380402
}
381403
}
382404

405+
#[inline(always)]
406+
fn capture_backtrace() -> Option<Box<Backtrace>> {
407+
let backtrace = Backtrace::capture();
408+
if backtrace.status() == BacktraceStatus::Captured {
409+
Some(Box::new(backtrace))
410+
} else {
411+
None
412+
}
413+
}
414+
383415
#[derive(Debug)]
384416
enum Repr {
385417
Simple(ErrorKind),
@@ -482,4 +514,14 @@ mod tests {
482514
let result = result.with_kind(ErrorKind::Io);
483515
assert_eq!(&ErrorKind::Io, result.unwrap_err().kind());
484516
}
517+
518+
#[test]
519+
fn backtrace_captured_when_enabled() {
520+
let error = Error::new(ErrorKind::Other, "test error");
521+
if std::env::var("RUST_BACKTRACE").is_ok() {
522+
assert!(error.backtrace.is_some());
523+
} else {
524+
assert!(error.backtrace.is_none());
525+
}
526+
}
485527
}

sdk/core/typespec_client_core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- `Error` now captures a `std::backtrace::Backtrace` at construction time and includes it in `Debug` output (e.g., `{:?}`) when `RUST_BACKTRACE=1` is set.
8+
79
### Breaking Changes
810

911
### Bugs Fixed

0 commit comments

Comments
 (0)