use super::*;
use std::{error::Error, fmt, io};
const PAYLOAD_BYTES: usize = 6144;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum OriginalCaptureState {
CompleteEnqueued,
CompleteWritten,
OutputUnavailable,
OutputRejected,
FormattingFailed,
CauseCycle,
LegacyProjectionOnly,
}
#[derive(Serialize)]
struct Segment<'a> {
schema_version: u8,
event: &'static str,
occurrence: DiagnosticOccurrence,
sequence: u64,
channel: &'static str,
cause_depth: usize,
payload: &'a str,
state: &'static str,
}
trait OriginalOutput {
fn submit(&self, segment: &Segment<'_>) -> DiagnosticSubmission;
}
impl OriginalOutput for EmergencyDiagnosticHandle {
fn submit(&self, segment: &Segment<'_>) -> DiagnosticSubmission {
self.submit_original_record(segment)
}
}
impl OriginalOutput for crate::BootstrapDiagnostics {
fn submit(&self, segment: &Segment<'_>) -> DiagnosticSubmission {
match self.write_original_record(segment) {
Ok(()) => DiagnosticSubmission::Enqueued,
Err(_) => DiagnosticSubmission::Closed,
}
}
}
struct ConfirmedOutput<'a>(&'a EmergencyDiagnosticHandle);
impl OriginalOutput for ConfirmedOutput<'_> {
fn submit(&self, segment: &Segment<'_>) -> DiagnosticSubmission {
match self.0.write_original_record(segment) {
Ok(()) => DiagnosticSubmission::Enqueued,
Err(_) => DiagnosticSubmission::Closed,
}
}
}
struct Stream<'a, O: OriginalOutput> {
output: &'a O,
occurrence: DiagnosticOccurrence,
sequence: u64,
depth: usize,
channel: &'static str,
bytes: [u8; PAYLOAD_BYTES],
len: usize,
escaped: usize,
failure: Option<OriginalCaptureState>,
submission: DiagnosticSubmission,
}
impl<O: OriginalOutput> Stream<'_, O> {
fn emit(&mut self, state: &'static str) -> fmt::Result {
let payload = std::str::from_utf8(&self.bytes[..self.len]).map_err(|_| fmt::Error)?;
let submission = self.output.submit(&Segment {
schema_version: 1,
event: "request_error_original",
occurrence: self.occurrence,
sequence: self.sequence,
channel: self.channel,
cause_depth: self.depth,
payload,
state,
});
if submission != DiagnosticSubmission::Enqueued {
self.submission = submission;
self.failure = Some(OriginalCaptureState::OutputRejected);
return Err(fmt::Error);
}
self.sequence += 1;
self.len = 0;
self.escaped = 0;
Ok(())
}
fn push(&mut self, text: &str) -> fmt::Result {
if self.failure.is_some() {
return Err(fmt::Error);
}
for c in text.chars() {
let cost = match c {
'"' | '\\' | '\n' | '\r' | '\t' | '\u{8}' | '\u{c}' => 2,
'\u{0}'..='\u{1f}' => 6,
_ => c.len_utf8(),
};
if self.escaped + cost > PAYLOAD_BYTES || self.len + c.len_utf8() > PAYLOAD_BYTES {
self.emit("continuation")?;
}
let size = c.len_utf8();
c.encode_utf8(&mut self.bytes[self.len..self.len + size]);
self.len += size;
self.escaped += cost;
}
Ok(())
}
fn json<T: Serialize>(&mut self, value: &T) -> fmt::Result {
serde_json::to_writer(&mut *self, value).map_err(|_| fmt::Error)?;
self.emit("field_end")
}
fn report_failure(&mut self, state: OriginalCaptureState) {
if self.failure == Some(OriginalCaptureState::OutputRejected) {
return;
}
self.failure = Some(state);
let marker = match state {
OriginalCaptureState::FormattingFailed => "formatting_failed",
OriginalCaptureState::CauseCycle => "cause_cycle",
_ => "capture_failed",
};
let _ = self.emit(marker);
}
}
impl<O: OriginalOutput> fmt::Write for Stream<'_, O> {
fn write_str(&mut self, text: &str) -> fmt::Result {
self.push(text)
}
}
impl<O: OriginalOutput> io::Write for Stream<'_, O> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
let text = std::str::from_utf8(bytes).map_err(|_| io::ErrorKind::InvalidData)?;
self.push(text).map_err(|_| io::ErrorKind::Other)?;
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Serialize)]
struct Header<'a> {
timestamp_unix_ms: u128,
context: OriginalContext<'a>,
stage: RootRequestEvent,
facts: SourceDetail<'a>,
outcome: RootOutcomeFacts,
original_contract: &'static str,
source_interface: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
protocol: Option<RemoteProtocolContext<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
remote_target: Option<RemoteTargetContext<'a>>,
}
enum OriginalContext<'a> {
Request(&'a RequestExecutionView),
Unrooted(crate::AdmissionEventContext<'a>),
Outbound {
view: &'a RequestExecutionView,
business_unit: &'a str,
},
}
impl Serialize for OriginalContext<'_> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Request(view) => view.serialize(serializer),
Self::Unrooted(context) => context.serialize(serializer),
Self::Outbound { view, business_unit } =>
view.outbound_source_context(business_unit).serialize(serializer),
}
}
}
#[derive(Serialize)]
pub struct RemoteTargetContext<'a> {
pub business_unit: &'a str,
pub function: &'a str,
}
#[derive(Serialize)]
pub struct RemoteProtocolContext<'a> {
pub business_unit: &'a str,
pub function: &'a str,
pub technical_code: &'a str,
pub certainty: &'a str,
}
enum RawSource<'a> {
Error(&'a (dyn Error + 'static)),
BorrowedError(&'a (dyn Error + 'a)),
Description(&'a dyn fmt::Display, &'a dyn fmt::Debug),
}
#[derive(Serialize)]
struct IoFacts {
kind: Kind,
os_code: Option<i32>,
}
struct Kind(io::ErrorKind);
impl Serialize for Kind {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(&format_args!("{:?}", self.0))
}
}
impl RootDiagnosticScope<'_> {
#[track_caller]
pub fn source_error_written(
&self,
error: &(dyn Error + 'static),
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let code = DiagnosticCode::new("framework.error").expect("static code");
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(source_stage, code),
);
let receipt = self.original_record_mode(
RawSource::Error(error), SourceDetail::Bounded(&facts), code, stage, axes, true, None, None,
);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else {
Err(receipt)
}
}
#[track_caller]
pub fn source_error(
&self,
error: &(dyn Error + 'static),
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
let code = DiagnosticCode::new("framework.error").expect("static code");
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(source_stage, code),
);
self.source_error_with_facts(error, facts, code, stage, axes)
}
pub fn source_error_with_facts(
&self,
error: &(dyn Error + 'static),
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
self.original_record(
RawSource::Error(error),
SourceDetail::Bounded(&facts),
classification,
stage,
axes,
)
}
pub fn source_error_with_facts_written(
&self,
error: &(dyn Error + 'static),
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let receipt = self.original_record_mode(
RawSource::Error(error), SourceDetail::Bounded(&facts),
classification, stage, axes, true, None, None,
);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else {
Err(receipt)
}
}
pub fn source_borrowed_error_with_facts_written<E: Error>(
&self,
error: &E,
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let receipt = self.original_record_mode(
RawSource::BorrowedError(error), SourceDetail::Bounded(&facts),
classification, stage, axes, true, None, None,
);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else {
Err(receipt)
}
}
pub fn source_description<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
self.original_record(
RawSource::Description(description, description),
SourceDetail::Bounded(&facts),
classification,
stage,
axes,
)
}
pub fn source_description_written<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
self.written_record(
RawSource::Description(description, description),
SourceDetail::Bounded(&facts),
classification,
stage,
axes,
)
}
pub fn source_remote_protocol_written<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
protocol: RemoteProtocolContext<'_>,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let receipt = self.original_record_mode(
RawSource::Description(description, description),
SourceDetail::Bounded(&facts), classification, stage, axes, true,
Some(protocol), None,
);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else { Err(receipt) }
}
pub fn source_remote_error_written(
&self,
error: &(dyn Error + 'static),
facts: BoundedDiagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
target: RemoteTargetContext<'_>,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let receipt = self.original_record_mode(
RawSource::Error(error), SourceDetail::Bounded(&facts),
classification, stage, axes, true, None, Some(target),
);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else { Err(receipt) }
}
pub fn source_existing_description<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
diagnostic: Diagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
self.original_record(
RawSource::Description(description, description),
SourceDetail::Existing(&diagnostic),
classification,
stage,
axes,
)
}
pub fn source_existing_description_written<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
diagnostic: Diagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
self.written_record(
RawSource::Description(description, description),
SourceDetail::Existing(&diagnostic),
classification,
stage,
axes,
)
}
pub fn source_existing_error(
&self,
error: &(dyn Error + 'static),
diagnostic: Diagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
self.original_record(
RawSource::Error(error),
SourceDetail::Existing(&diagnostic),
classification,
stage,
axes,
)
}
pub fn source_existing_error_written(
&self,
error: &(dyn Error + 'static),
diagnostic: Diagnostic,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
self.written_record(
RawSource::Error(error),
SourceDetail::Existing(&diagnostic),
classification,
stage,
axes,
)
}
fn written_record(
&self,
raw: RawSource<'_>,
facts: SourceDetail<'_>,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<super::WrittenRootFailure, RootRequestFailure> {
let receipt = self.original_record_mode(raw, facts, classification, stage, axes, true, None, None);
if receipt.original_capture == OriginalCaptureState::CompleteWritten {
Ok(super::WrittenRootFailure(receipt))
} else {
Err(receipt)
}
}
fn original_record(
&self,
raw: RawSource<'_>,
facts: SourceDetail<'_>,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> RootRequestFailure {
self.original_record_mode(raw, facts, classification, stage, axes, false, None, None)
}
fn original_record_mode(
&self,
raw: RawSource<'_>,
facts: SourceDetail<'_>,
classification: DiagnosticCode,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
confirmed: bool,
protocol: Option<RemoteProtocolContext<'_>>,
remote_target: Option<RemoteTargetContext<'_>>,
) -> RootRequestFailure {
let (occurrence, category) = match &facts {
SourceDetail::Bounded(d) => (d.occurrence(), d.category()),
SourceDetail::Existing(d) => (d.occurrence(), d.category()),
};
let source_interface = match raw {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "unavailable_description_debug_only",
};
let (submission, original_capture) = match self.output {
None => (
DiagnosticSubmission::OutputUnavailable,
OriginalCaptureState::OutputUnavailable,
),
Some(output) => {
let header = Header {
timestamp_unix_ms: timestamp(),
context: match protocol.as_ref().map(|value| value.business_unit)
.or_else(|| remote_target.as_ref().map(|value| value.business_unit)) {
Some(business_unit) => OriginalContext::Outbound { view: self.view, business_unit },
None => OriginalContext::Request(self.view),
},
stage,
facts,
outcome: axes,
original_contract: "display-debug-source/v1",
source_interface,
protocol,
remote_target,
};
if confirmed {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence, raw, &header);
if submission == DiagnosticSubmission::Enqueued && state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else {
(submission, state)
}
} else {
capture(output, occurrence, raw, &header)
}
},
};
RootRequestFailure {
source: self.view.clone(),
occurrence,
classification,
source_submission: submission,
category,
outcome: axes,
original_capture,
}
}
}
#[must_use = "capture facts describe submission, not durable output or resource completion"]
pub struct UnrootedCaptureFacts {
occurrence: DiagnosticOccurrence,
submission: DiagnosticSubmission,
original_capture: OriginalCaptureState,
}
#[must_use = "carry the written source to its consumer"]
pub struct WrittenUnrootedFailure(UnrootedCaptureFacts);
impl WrittenUnrootedFailure {
fn from_facts(facts: UnrootedCaptureFacts) -> Result<Self, UnrootedCaptureFacts> {
if facts.original_capture == OriginalCaptureState::CompleteWritten {
Ok(Self(facts))
} else {
Err(facts)
}
}
pub fn occurrence(&self) -> DiagnosticOccurrence { self.0.occurrence }
}
impl UnrootedCaptureFacts {
pub fn occurrence(&self) -> DiagnosticOccurrence {
self.occurrence
}
pub fn submission(&self) -> DiagnosticSubmission {
self.submission
}
pub fn original_capture(&self) -> OriginalCaptureState {
self.original_capture
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ComponentSourceKind { Start, Cleanup }
pub struct ComponentSourceReceipt {
facts: UnrootedCaptureFacts,
kind: ComponentSourceKind,
}
impl ComponentSourceReceipt {
pub fn kind(&self) -> ComponentSourceKind { self.kind }
pub fn occurrence(&self) -> DiagnosticOccurrence { self.facts.occurrence() }
pub fn original_capture(&self) -> OriginalCaptureState { self.facts.original_capture() }
}
#[must_use = "carry the written component source to its consumer"]
pub struct WrittenComponentFailure(ComponentSourceReceipt);
impl WrittenComponentFailure {
fn from_receipt(receipt: ComponentSourceReceipt) -> Result<Self, ComponentSourceReceipt> {
if receipt.original_capture() == OriginalCaptureState::CompleteWritten {
Ok(Self(receipt))
} else {
Err(receipt)
}
}
pub fn occurrence(&self) -> DiagnosticOccurrence { self.0.occurrence() }
pub fn kind(&self) -> ComponentSourceKind { self.0.kind() }
}
pub struct UnrootedDiagnosticScope<'a> {
application: &'a saddle_core::ContextLabel,
lifecycle: saddle_core::RequestViewPhase,
output: Option<&'a EmergencyDiagnosticHandle>,
}
impl<'a> UnrootedDiagnosticScope<'a> {
pub fn new(
application: &'a saddle_core::ContextLabel,
lifecycle: saddle_core::RequestViewPhase,
output: Option<&'a EmergencyDiagnosticHandle>,
) -> Self {
Self {
application,
lifecycle,
output,
}
}
#[track_caller]
pub fn source_error(
&self,
error: &(dyn Error + 'static),
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> UnrootedCaptureFacts {
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(
source_stage,
DiagnosticCode::new("framework.error").unwrap(),
),
);
self.original_record(RawSource::Error(error), &facts, stage, axes, false)
}
#[track_caller]
pub fn source_error_written(
&self,
error: &(dyn Error + 'static),
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> UnrootedCaptureFacts {
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(
source_stage,
DiagnosticCode::new("framework.error").unwrap(),
),
);
self.original_record(RawSource::Error(error), &facts, stage, axes, true)
}
#[track_caller]
pub fn source_error_recorded(
&self,
error: &(dyn Error + 'static),
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<WrittenUnrootedFailure, UnrootedCaptureFacts> {
WrittenUnrootedFailure::from_facts(
self.source_error_written(error, source_stage, stage, axes)
)
}
pub fn source_existing_error_recorded(
&self,
error: &(dyn Error + 'static),
diagnostic: &Diagnostic,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> Result<WrittenUnrootedFailure, UnrootedCaptureFacts> {
WrittenUnrootedFailure::from_facts(self.original_record_detail(
RawSource::Error(error), SourceDetail::Existing(diagnostic), stage, axes, true,
))
}
#[track_caller]
pub fn source_description<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> UnrootedCaptureFacts {
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(
source_stage,
DiagnosticCode::new("framework.error").unwrap(),
),
);
self.original_record(
RawSource::Description(description, description),
&facts,
stage,
axes,
false,
)
}
#[track_caller]
pub fn source_description_written<D: fmt::Display + fmt::Debug>(
&self,
description: &D,
source_stage: saddle_core::DiagnosticStage,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
) -> UnrootedCaptureFacts {
let facts = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(
source_stage,
DiagnosticCode::new("framework.error").unwrap(),
),
);
self.original_record(RawSource::Description(description, description), &facts, stage, axes, true)
}
fn original_record(
&self,
raw: RawSource<'_>,
facts: &BoundedDiagnostic,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
confirmed: bool,
) -> UnrootedCaptureFacts {
self.original_record_detail(raw, SourceDetail::Bounded(facts), stage, axes, confirmed)
}
fn original_record_detail(
&self,
raw: RawSource<'_>,
facts: SourceDetail<'_>,
stage: RootRequestEvent,
axes: RootOutcomeFacts,
confirmed: bool,
) -> UnrootedCaptureFacts {
let occurrence = match facts {
SourceDetail::Bounded(diagnostic) => diagnostic.occurrence(),
SourceDetail::Existing(diagnostic) => diagnostic.occurrence(),
};
let source_interface = match raw {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "unavailable_description_debug_only",
};
let (submission, original_capture) = match self.output {
None => (
DiagnosticSubmission::OutputUnavailable,
OriginalCaptureState::OutputUnavailable,
),
Some(output) => {
let header = Header {
timestamp_unix_ms: timestamp(),
context: OriginalContext::Unrooted(crate::AdmissionEventContext::Unrooted {
application: self.application,
lifecycle: self.lifecycle,
}),
stage,
facts,
outcome: axes,
original_contract: "display-debug-source/v1",
source_interface,
protocol: None,
remote_target: None,
};
if confirmed {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence, raw, &header);
if submission == DiagnosticSubmission::Enqueued && state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else {
(submission, state)
}
} else {
capture(output, occurrence, raw, &header)
}
},
};
UnrootedCaptureFacts {
occurrence,
submission,
original_capture,
}
}
}
struct CycleDetector<'a> {
anchor: &'a (dyn Error + 'static),
power: usize,
distance: usize,
}
impl<'a> CycleDetector<'a> {
fn new(anchor: &'a (dyn Error + 'static)) -> Self {
Self {
anchor,
power: 1,
distance: 0,
}
}
fn repeats(&mut self, next: &'a (dyn Error + 'static)) -> bool {
if std::ptr::eq(self.anchor, next) {
return true;
}
self.distance += 1;
if self.distance == self.power {
self.anchor = next;
self.power = self.power.saturating_mul(2);
self.distance = 0;
}
false
}
}
pub fn write_json_error_debug<W: fmt::Write>(out: &mut W,
error: &serde_json::Error) -> fmt::Result {
struct FixedText<const N: usize> { bytes: [u8; N], len: usize }
impl<const N: usize> fmt::Write for FixedText<N> {
fn write_str(&mut self, value: &str) -> fmt::Result {
if self.len + value.len() > self.bytes.len() { return Err(fmt::Error); }
self.bytes[self.len..self.len + value.len()].copy_from_slice(value.as_bytes());
self.len += value.len();
Ok(())
}
}
let mut shown = FixedText { bytes: [0; 6144], len: 0 };
fmt::write(&mut shown, format_args!("{error}"))?;
let shown = std::str::from_utf8(&shown.bytes[..shown.len]).map_err(|_| fmt::Error)?;
let mut suffix = FixedText { bytes: [0; 64], len: 0 };
if error.line() != 0 {
fmt::write(&mut suffix,
format_args!(" at line {} column {}", error.line(), error.column()))?;
}
let suffix = std::str::from_utf8(&suffix.bytes[..suffix.len]).map_err(|_| fmt::Error)?;
let reason = shown.strip_suffix(suffix).ok_or(fmt::Error)?;
fmt::write(out, format_args!("Error({reason:?}, line: {}, column: {})",
error.line(), error.column()))
}
fn write_error_debug<O: OriginalOutput>(stream: &mut Stream<'_, O>,
error: &(dyn Error + 'static), bounded_input: bool) -> fmt::Result {
if bounded_input {
if let Some(json) = error.downcast_ref::<serde_json::Error>() {
return write_json_error_debug(stream, json);
}
if let Some(saddle_admission::InputDecodeFailure::Syntax(json)) =
error.downcast_ref::<saddle_admission::InputDecodeFailure>() {
stream.push("Syntax(")?;
write_json_error_debug(stream, json)?;
return stream.push(")");
}
}
fmt::write(stream, format_args!("{error:?}"))
}
fn capture<O: OriginalOutput>(
output: &O,
occurrence: DiagnosticOccurrence,
raw: RawSource<'_>,
header: &impl Serialize,
) -> (DiagnosticSubmission, OriginalCaptureState) {
capture_with_mode(output, occurrence, raw, header, true)
}
fn capture_with_mode<O: OriginalOutput>(
output: &O,
occurrence: DiagnosticOccurrence,
raw: RawSource<'_>,
header: &impl Serialize,
bounded_input: bool,
) -> (DiagnosticSubmission, OriginalCaptureState) {
let mut stream = Stream {
output,
occurrence,
sequence: 0,
depth: 0,
channel: "context",
bytes: [0; PAYLOAD_BYTES],
len: 0,
escaped: 0,
failure: None,
submission: DiagnosticSubmission::Enqueued,
};
let result = (|| -> fmt::Result {
stream.json(header)?;
let error = match raw {
RawSource::Error(error) => error,
RawSource::BorrowedError(error) => {
stream.channel = "description";
if fmt::write(&mut stream, format_args!("{error}")).is_err() {
let state = stream.failure.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.channel = "debug";
if fmt::write(&mut stream, format_args!("{error:?}")).is_err() {
let state = stream.failure.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.depth = 1;
let Some(first) = error.source() else {
stream.channel = "terminal";
return stream.emit("exposed_chain_complete");
};
let mut cycle = CycleDetector::new(first);
let mut next = Some(first);
while let Some(current) = next {
stream.channel = "description";
if fmt::write(&mut stream, format_args!("{current}")).is_err() {
let state = stream.failure.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.channel = "debug";
if write_error_debug(&mut stream, current, bounded_input).is_err() {
let state = stream.failure.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
if let Some(io) = current.downcast_ref::<io::Error>() {
stream.channel = "io_facts";
stream.json(&IoFacts { kind: Kind(io.kind()), os_code: io.raw_os_error() })?;
}
next = current.source();
stream.depth += 1;
if next.is_some_and(|next| cycle.repeats(next)) {
stream.channel = "description";
stream.report_failure(OriginalCaptureState::CauseCycle);
return Err(fmt::Error);
}
}
stream.channel = "terminal";
return stream.emit("exposed_chain_complete");
}
RawSource::Description(description, debug) => {
stream.channel = "description";
if fmt::write(&mut stream, format_args!("{description}")).is_err() {
let state = stream
.failure
.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.channel = "debug";
if fmt::write(&mut stream, format_args!("{debug:?}")).is_err() {
let state = stream
.failure
.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.depth = 1;
stream.channel = "terminal";
return stream.emit("description_complete_source_unavailable");
}
};
let mut cycle = CycleDetector::new(error);
let mut next = Some(error);
while let Some(current) = next {
stream.channel = "description";
if fmt::write(&mut stream, format_args!("{current}")).is_err() {
let failure = stream
.failure
.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(failure);
return Err(fmt::Error);
}
stream.emit("field_end")?;
stream.channel = "debug";
if write_error_debug(&mut stream, current, bounded_input).is_err() {
let state = stream
.failure
.unwrap_or(OriginalCaptureState::FormattingFailed);
stream.report_failure(state);
return Err(fmt::Error);
}
stream.emit("field_end")?;
if let Some(io) = current.downcast_ref::<io::Error>() {
stream.channel = "io_facts";
stream.json(&IoFacts {
kind: Kind(io.kind()),
os_code: io.raw_os_error(),
})?;
}
next = current.source();
stream.depth += 1;
if next.is_some_and(|next| cycle.repeats(next)) {
stream.channel = "description";
stream.report_failure(OriginalCaptureState::CauseCycle);
return Err(fmt::Error);
}
}
stream.channel = "terminal";
stream.emit("exposed_chain_complete")
})();
if result.is_ok() {
(stream.submission, OriginalCaptureState::CompleteEnqueued)
} else {
let state = stream
.failure
.unwrap_or(OriginalCaptureState::FormattingFailed);
if stream.submission == DiagnosticSubmission::Enqueued {
(DiagnosticSubmission::EncodingFailed, state)
} else {
(stream.submission, state)
}
}
}
pub fn original_capture_layout() -> (std::alloc::Layout, std::alloc::Layout, usize) {
(
std::alloc::Layout::new::<Stream<'static, EmergencyDiagnosticHandle>>(),
std::alloc::Layout::new::<CycleDetector<'static>>(),
PAYLOAD_BYTES,
)
}
#[cfg(test)]
mod content_tests {
use super::*;
use std::cell::RefCell;
#[test]
fn json_parser_debug_stream_matches_native_bytes() {
for input in ["{broken", "[", "{\"a\":1,}", "\"\\uD800\""] {
let error = serde_json::from_str::<serde_json::Value>(input).unwrap_err();
let mut streamed = String::new();
write_json_error_debug(&mut streamed, &error).unwrap();
assert_eq!(streamed, format!("{error:?}"));
}
}
#[test]
fn owned_source_is_written_before_projection_and_mismatch_keeps_original() {
use saddle_core::{CaptureSite, DiagnosticCause, DiagnosticStage, ErrorKind,
RequestViewPhase, SaddleError};
let directory = std::env::temp_dir().join(format!(
"saddle-owned-source-{}-{}", std::process::id(),
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
.unwrap().as_nanos()));
std::fs::create_dir(&directory).unwrap();
let mut output = crate::EmergencyDiagnostics::start_checked(
&crate::FileLoggingConfig::new(directory.clone(), crate::Rotation::Daily)).unwrap();
let selected = output.source_output().expect("checked selected output");
let label = saddle_core::ContextLabel::checked("owned-test").unwrap();
let diagnostic = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("owned.test").unwrap()));
let target = output.target().to_owned();
let written = super::RecordedSaddleError::capture_unrooted(
io::Error::other("original before projection 9271"), diagnostic,
&label, RequestViewPhase::Admitted, &selected,
RootRequestEvent::Admission, RootOutcomeFacts::default(),
|_, diagnostic| {
assert!(std::fs::read_to_string(&target).unwrap()
.contains("original before projection 9271"));
SaddleError::new(ErrorKind::Infrastructure, "owned.test", "safe")
.with_diagnostic(diagnostic)
}).unwrap_or_else(|_| panic!("written and paired"));
assert_eq!(written.safe().code(), "owned.test");
let component = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::ShutdownComponent,
DiagnosticCode::new("owned.cleanup").unwrap()));
let mismatch = super::RecordedSaddleError::capture_component(
io::Error::other("owned cleanup original 9272"), component,
saddle_core::ContextFact::Present(label.clone()), &selected,
ComponentSourceKind::Cleanup,
|_, _| SaddleError::new(ErrorKind::Infrastructure, "owned.cleanup", "safe")
.with_diagnostic(Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::ShutdownComponent,
DiagnosticCode::new("owned.cleanup").unwrap()))),
).err().expect("wrong occurrence rejected");
assert!(mismatch.safe().source_unavailable());
assert_eq!(mismatch.source().to_string(), "owned cleanup original 9272");
assert!(std::fs::read_to_string(&target).unwrap()
.contains("owned cleanup original 9272"));
drop(written);
drop(selected);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
while output.shutdown() == crate::DiagnosticShutdown::Pending
&& std::time::Instant::now() < deadline { std::thread::yield_now(); }
assert_eq!(output.shutdown(), crate::DiagnosticShutdown::Finished);
drop(output);
std::fs::remove_dir_all(directory).unwrap();
}
#[test]
fn sealed_unrooted_failure_requires_written_matching_original() {
use saddle_core::{CaptureSite, DiagnosticCause, DiagnosticStage, ErrorKind,
RequestViewPhase, SaddleError};
let directory = std::env::temp_dir().join(format!(
"saddle-sealed-source-{}-{}", std::process::id(),
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
.unwrap().as_nanos()));
std::fs::create_dir(&directory).unwrap();
let mut output = crate::EmergencyDiagnostics::start(
&crate::FileLoggingConfig::new(directory.clone(), crate::Rotation::Daily)).unwrap();
let handle = output.handle();
let label = saddle_core::ContextLabel::checked("sealed-test").unwrap();
let diagnostic = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("sealed.test").unwrap()));
let original = io::Error::other("private original 1849");
let scope = UnrootedDiagnosticScope::new(&label, RequestViewPhase::Admitted,
Some(&handle));
let written = scope.source_existing_error_recorded(&original, &diagnostic,
RootRequestEvent::Admission, RootOutcomeFacts::default()).ok().expect("written");
let safe = SaddleError::new(ErrorKind::Infrastructure, "sealed.test", "safe")
.with_diagnostic(diagnostic);
let recorded = super::RecordedSaddleError::from_written(safe, written).unwrap();
assert!(!recorded.to_string().contains("private original 1849"));
assert!(std::fs::read_to_string(output.target()).unwrap()
.contains("private original 1849"));
let other = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("sealed.test").unwrap()));
let mismatch = scope.source_existing_error_recorded(&original, &other,
RootRequestEvent::Admission, RootOutcomeFacts::default()).ok().expect("written");
let wrong = SaddleError::new(ErrorKind::Infrastructure, "sealed.test", "safe")
.with_diagnostic(Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("sealed.test").unwrap())));
assert!(super::RecordedSaddleError::from_written(wrong, mismatch)
.unwrap_err().source_unavailable());
let missing = UnrootedDiagnosticScope::new(&label, RequestViewPhase::Admitted, None);
let no_output = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("sealed.test").unwrap()));
assert!(missing.source_existing_error_recorded(&original, &no_output,
RootRequestEvent::Admission, RootOutcomeFacts::default()).is_err());
let component = Diagnostic::capture(DiagnosticCategory::UnexpectedError,
CaptureSite::FirstObserved,
DiagnosticCause::new(DiagnosticStage::StartupService,
DiagnosticCode::new("sealed.component").unwrap()));
let component_written = process_component_start_recorded(Some(&handle),
saddle_core::ContextFact::Unavailable, &component, &original)
.ok().expect("component written");
let component_safe = SaddleError::new(ErrorKind::Infrastructure,
"sealed.component", "safe").with_diagnostic(component);
let component_recorded = super::RecordedSaddleError::from_component_written(
component_safe, component_written).unwrap();
assert_eq!(component_recorded.component_kind(), Some(ComponentSourceKind::Start));
drop(scope);
drop(recorded);
drop(component_recorded);
drop(handle);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
while output.shutdown() == crate::DiagnosticShutdown::Pending
&& std::time::Instant::now() < deadline { std::thread::yield_now(); }
assert_eq!(output.shutdown(), crate::DiagnosticShutdown::Finished);
drop(output);
std::fs::remove_dir_all(directory).unwrap();
}
struct Node {
text: String,
next: Option<Box<Node>>,
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.text)
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.text)
}
}
impl Error for Node {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.next.as_deref().map(|v| v as &dyn Error)
}
}
struct Readback<'a> {
expected: &'a [String],
state: RefCell<(u64, usize, usize, bool)>,
}
impl OriginalOutput for Readback<'_> {
fn submit(&self, segment: &Segment<'_>) -> DiagnosticSubmission {
let encoded = serde_json::to_vec(segment).unwrap();
assert!(encoded.len() < 8192);
let row: serde_json::Value = serde_json::from_slice(&encoded).unwrap();
let mut s = self.state.borrow_mut();
assert_eq!(row["sequence"].as_u64().unwrap(), s.0);
s.0 += 1;
if matches!(segment.channel, "description" | "debug") {
let expected = &self.expected[segment.cause_depth];
let payload = row["payload"].as_str().unwrap();
assert_eq!(payload, &expected[s.1..s.1 + payload.len()]);
s.1 += payload.len();
if segment.state == "field_end" {
assert_eq!(s.1, expected.len(), "original tail missing");
s.1 = 0;
s.2 += 1;
}
}
if segment.channel == "terminal" {
assert_eq!(segment.state, "exposed_chain_complete");
assert_eq!(segment.cause_depth, self.expected.len());
assert_eq!(s.2, self.expected.len() * 2);
s.3 = true;
}
DiagnosticSubmission::Enqueued
}
}
fn assert_content(expected: Vec<String>) -> u64 {
assert_content_context(expected, false)
}
fn assert_content_context(expected: Vec<String>, unrooted: bool) -> u64 {
let mut head = None;
for text in expected.iter().rev() {
head = Some(Box::new(Node {
text: text.clone(),
next: head,
}));
}
let publisher = saddle_core::RequestRootPublisher::create(
saddle_core::ContextLabel::checked("content-test").unwrap(),
saddle_core::ContextFact::NotEstablished,
)
.unwrap();
let view = publisher
.reference()
.view(saddle_core::RequestLocalFacts::new(
saddle_core::RequestViewPhase::Reading,
));
let diagnostic = BoundedDiagnostic::capture(
DiagnosticCategory::UnexpectedError,
saddle_core::CaptureSite::FirstObserved,
saddle_core::BoundedDiagnosticCause::new(
saddle_core::DiagnosticStage::RequestDecode,
DiagnosticCode::new("content.original").unwrap(),
),
);
let output = Readback {
expected: &expected,
state: RefCell::new((0, 0, 0, false)),
};
let application = saddle_core::ContextLabel::checked("content-test").unwrap();
let result = capture(
&output,
diagnostic.occurrence(),
RawSource::Error(head.as_deref().unwrap()),
&Header {
timestamp_unix_ms: 0,
context: if unrooted {
crate::AdmissionEventContext::Unrooted {
application: &application,
lifecycle: saddle_core::RequestViewPhase::Reading,
}
} else {
crate::AdmissionEventContext::Rooted(&view)
},
stage: RootRequestEvent::Ingress,
facts: SourceDetail::Bounded(&diagnostic),
outcome: RootOutcomeFacts::default(),
original_contract: "display-debug-source/v1",
source_interface: "error_source",
protocol: None,
remote_target: None,
},
);
assert_eq!(
result,
(
DiagnosticSubmission::Enqueued,
OriginalCaptureState::CompleteEnqueued
)
);
assert!(output.state.borrow().3);
output.state.into_inner().0
}
#[test]
fn rooted_and_unrooted_share_lossless_long_content() {
let expected = vec![
format!("HEAD-䏿–‡\n\"{}\"-TAIL", "原文".repeat(2200)),
"nested-cause\n完整".into(),
];
let rooted = assert_content_context(expected.clone(), false);
let unrooted = assert_content_context(expected, true);
assert_eq!(rooted, unrooted);
}
#[test]
fn finite_chain_beyond_old_256_keeps_every_cause() {
let segments = assert_content(
(0..300)
.map(|n| format!("unknown-{n}-原文\n\"tail\""))
.collect(),
);
assert!(segments > 600);
println!("content readback: 300 original causes and Debug fields complete");
}
#[test]
fn cycle_detector_handles_prefix_and_nontrivial_cycle() {
let nodes: Vec<Node> = (0..4)
.map(|n| Node {
text: n.to_string(),
next: None,
})
.collect();
let mut cycle = CycleDetector::new(&nodes[0]);
for node in [&nodes[1], &nodes[2], &nodes[3]] {
assert!(!cycle.repeats(node));
}
let found = (0..32).any(|step| cycle.repeats(&nodes[1 + step % 3]));
assert!(found);
}
#[test]
fn finite_text_beyond_old_4096_segments_keeps_tail() {
let text = format!("{}END-原文", "x".repeat(6144 * 4097));
let segments = assert_content(vec![text]);
assert!(segments > 8194);
println!("content readback: {segments} segments, complete Display/Debug tails");
}
}
#[doc(hidden)]
pub fn database_background_error(
output: Option<&EmergencyDiagnosticHandle>,
datasource: u64,
connection: Option<u64>,
sweep: u64,
phase: &'static str,
maintenance: bool,
error: &(dyn Error + 'static),
facts: BoundedDiagnostic,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct Context {
schema_version: u8,
datasource: u64,
connection: ContextFact<u64>,
maintenance_sweep: u64,
maintenance_phase: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
application: ContextFact<()>,
}
#[derive(Serialize)]
struct BackgroundHeader<'a> {
timestamp_unix_ms: u128,
context: Context,
stage: &'static str,
facts: &'a BoundedDiagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = facts.occurrence();
let missing = if maintenance { ContextFact::NotApplicable } else { ContextFact::Unavailable };
let header = BackgroundHeader {
timestamp_unix_ms: timestamp(),
context: Context { schema_version: 2, datasource,
connection: connection.map(ContextFact::Present).unwrap_or(ContextFact::NotEstablished),
maintenance_sweep: sweep, maintenance_phase: phase,
request: missing, trace_id: missing, application: ContextFact::Unavailable },
stage: if maintenance { "database_maintenance" } else { "database_connection" }, facts: &facts,
original_contract: "display-debug-source/v1", source_interface: "error_source",
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence, RawSource::Error(error), &header);
if submission == DiagnosticSubmission::Enqueued && state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else {
(submission, state)
}
},
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[doc(hidden)]
pub fn database_transaction_error(
output: Option<&EmergencyDiagnosticHandle>,
call: &saddle_core::CallContext,
phase: &'static str,
error: &(dyn Error + 'static),
facts: &Diagnostic,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct Context<'a> {
schema_version: u8,
application: &'a str,
module: &'a str,
service: &'a str,
operation: &'a str,
trace_id: String,
span_id: String,
request: ContextFact<()>,
transaction_phase: &'static str,
}
#[derive(Serialize)]
struct Header<'a> {
timestamp_unix_ms: u128,
context: Context<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = facts.occurrence();
let header = Header {
timestamp_unix_ms: timestamp(),
context: Context {
schema_version: 2,
application: call.application().as_str(),
module: call.module().as_str(),
service: call.service().as_str(),
operation: call.operation().as_str(),
trace_id: call.trace_id().to_string(),
span_id: call.span_id().to_string(),
request: ContextFact::Unavailable,
transaction_phase: phase,
},
stage: if phase == "begin" || phase == "commit" { "database_transaction" }
else { "database_transaction_cleanup" },
facts,
original_contract: "display-debug-source/v1",
source_interface: "error_source",
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(
&ConfirmedOutput(output), occurrence, RawSource::Error(error), &header);
if submission == DiagnosticSubmission::Enqueued
&& state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else {
(submission, state)
}
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[doc(hidden)]
pub fn database_operation_error(
output: Option<&EmergencyDiagnosticHandle>,
call: &saddle_core::CallContext,
phase: &'static str,
error: &(dyn Error + 'static),
diagnostic: &Diagnostic,
) -> UnrootedCaptureFacts {
database_operation_source(output, call, phase, RawSource::Error(error), diagnostic, diagnostic.occurrence(), false)
}
#[doc(hidden)]
pub fn database_operation_borrowed_error<D: Error>(
output: Option<&EmergencyDiagnosticHandle>,
call: &saddle_core::CallContext,
phase: &'static str,
source: &D,
diagnostic: &Diagnostic,
) -> UnrootedCaptureFacts {
database_operation_source(output, call, phase,
RawSource::BorrowedError(source), diagnostic, diagnostic.occurrence(), false)
}
#[doc(hidden)]
pub fn database_operation_borrowed_input_bounded<D: Error>(
output: Option<&EmergencyDiagnosticHandle>,
call: &saddle_core::CallContext,
phase: &'static str,
source: &D,
diagnostic: &BoundedDiagnostic,
) -> UnrootedCaptureFacts {
database_operation_source(output, call, phase,
RawSource::BorrowedError(source), diagnostic, diagnostic.occurrence(), true)
}
fn database_operation_source<D: Serialize>(
output: Option<&EmergencyDiagnosticHandle>,
call: &saddle_core::CallContext,
phase: &'static str,
source: RawSource<'_>,
diagnostic: &D,
occurrence: DiagnosticOccurrence,
bounded_input: bool,
) -> UnrootedCaptureFacts {
struct DisplayId<T>(T);
impl<T: fmt::Display> Serialize for DisplayId<T> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(&self.0)
}
}
#[derive(Serialize)]
struct Context<'a> {
schema_version: u8,
application: &'a str,
module: &'a str,
service: &'a str,
operation: &'a str,
trace_id: DisplayId<saddle_core::TraceId>,
trace_correlation_id: &'a str,
span_id: DisplayId<saddle_core::SpanId>,
rpc_correlation_id: Option<&'a str>,
request: ContextFact<()>,
operation_phase: &'static str,
}
#[derive(Serialize)]
struct Header<'a, D> {
timestamp_unix_ms: u128,
context: Context<'a>,
stage: &'static str,
facts: &'a D,
original_contract: &'static str,
source_interface: &'static str,
}
let header = Header {
timestamp_unix_ms: timestamp(),
context: Context {
schema_version: 2,
application: call.application().as_str(),
module: call.module().as_str(),
service: call.service().as_str(),
operation: call.operation().as_str(),
trace_id: DisplayId(call.trace_id()),
trace_correlation_id: call.trace_correlation_id().as_str(),
span_id: DisplayId(call.span_id()),
rpc_correlation_id: call.rpc_correlation_id().map(|id| id.as_str()),
request: ContextFact::Unavailable,
operation_phase: phase,
},
stage: "database_operation",
facts: diagnostic,
original_contract: "display-debug-source/v1",
source_interface: match &source {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "description_debug_only",
},
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture_with_mode(
&ConfirmedOutput(output), occurrence, source, &header, bounded_input);
if submission == DiagnosticSubmission::Enqueued
&& state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) }
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[doc(hidden)]
#[track_caller]
pub fn process_startup_error(
output: Option<&EmergencyDiagnosticHandle>,
application: &saddle_core::ContextLabel,
key: &'static str,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct StartupContext<'a> {
schema_version: u8,
application: &'a saddle_core::ContextLabel,
lifecycle: &'static str,
config_key: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct StartupHeader<'a> {
timestamp_unix_ms: u128,
context: StartupContext<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = StartupHeader {
timestamp_unix_ms: timestamp(),
context: StartupContext { schema_version: 2, application, lifecycle: "startup",
config_key: key, request: ContextFact::NotEstablished,
trace_id: ContextFact::NotEstablished },
stage: "startup_listener", facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: "error_source",
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence, RawSource::Error(error), &header);
if submission == DiagnosticSubmission::Enqueued && state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) }
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[doc(hidden)]
#[track_caller]
pub fn process_config_error(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
match output {
Some(output) => capture_config(&ConfirmedOutput(output), diagnostic, error),
None => UnrootedCaptureFacts {
occurrence: diagnostic.occurrence(),
submission: DiagnosticSubmission::OutputUnavailable,
original_capture: OriginalCaptureState::OutputUnavailable,
},
}
}
#[doc(hidden)]
#[track_caller]
pub fn process_bootstrap_error(
output: &crate::BootstrapDiagnostics,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
capture_config(output, diagnostic, error)
}
#[derive(Clone, Copy)]
pub enum ServiceRegistrySourceOutput<'a> {
Bootstrap(&'a crate::BootstrapDiagnostics),
Emergency(&'a EmergencyDiagnosticHandle),
}
#[track_caller]
pub fn service_registry_source_error(
output: Option<ServiceRegistrySourceOutput<'_>>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
lifecycle: &'static str,
) -> UnrootedCaptureFacts {
service_registry_source(output, diagnostic, RawSource::Error(error), lifecycle)
}
#[track_caller]
pub fn service_production_fact_source_error(
output: &crate::BootstrapDiagnostics,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
application: &saddle_core::ApplicationId,
) -> UnrootedCaptureFacts {
service_compiled_build_source_error(output, diagnostic, error, application, "service_production_fact")
}
#[track_caller]
pub fn service_compiled_build_source_error(
output: &crate::BootstrapDiagnostics,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
application: &saddle_core::ApplicationId,
source_stage: &'static str,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct FactContext<'a> {
schema_version: u8,
application: &'a str,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct FactHeader<'a> {
timestamp_unix_ms: u128,
context: FactContext<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = FactHeader {
timestamp_unix_ms: timestamp(),
context: FactContext {
schema_version: 2, application: application.as_str(), lifecycle: "startup",
request: ContextFact::NotEstablished, trace_id: ContextFact::NotEstablished,
},
stage: source_stage, facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: "error_source",
};
let (submission, original_capture) = capture(output, occurrence, RawSource::Error(error), &header);
let (submission, original_capture) = match (submission, original_capture) {
(DiagnosticSubmission::Enqueued, OriginalCaptureState::CompleteEnqueued) =>
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten),
facts => facts,
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[track_caller]
pub fn service_registry_source_description(
output: Option<ServiceRegistrySourceOutput<'_>>,
diagnostic: &Diagnostic,
description: &(impl fmt::Display + fmt::Debug),
lifecycle: &'static str,
) -> UnrootedCaptureFacts {
service_registry_source(output, diagnostic, RawSource::Description(description, description), lifecycle)
}
#[track_caller]
pub fn service_route_source_description(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
description: &(impl fmt::Display + fmt::Debug),
application: &saddle_core::ApplicationId,
route: &str,
) -> UnrootedCaptureFacts {
service_entry_source(output, diagnostic, RawSource::Description(description, description),
application, route, None, "startup", "service_route_registration")
}
#[track_caller]
pub fn service_entry_trace_source_error(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
application: &saddle_core::ApplicationId,
route: &str,
inbound_trace_id: &str,
) -> UnrootedCaptureFacts {
service_entry_source(output, diagnostic, RawSource::Error(error), application, route,
Some(inbound_trace_id), "request", "service_trace_validation")
}
#[track_caller]
fn service_entry_source(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
raw: RawSource<'_>,
application: &saddle_core::ApplicationId,
route: &str,
inbound_trace_id: Option<&str>,
lifecycle: &'static str,
stage: &'static str,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct RouteContext<'a> {
schema_version: u8,
application: &'a str,
route: &'a str,
inbound_trace_id: ContextFact<&'a str>,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct RouteHeader<'a> {
timestamp_unix_ms: u128,
context: RouteContext<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = RouteHeader {
timestamp_unix_ms: timestamp(),
context: RouteContext {
schema_version: 2, application: application.as_str(), route,
inbound_trace_id: inbound_trace_id.map(ContextFact::Present)
.unwrap_or(ContextFact::NotEstablished),
lifecycle, request: ContextFact::NotEstablished,
trace_id: ContextFact::NotEstablished,
},
stage, facts: diagnostic,
original_contract: "display-debug-source/v1",
source_interface: match raw {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "unavailable_description_debug_only",
},
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, capture_state) = capture(&ConfirmedOutput(output), occurrence,
raw, &header);
match (submission, capture_state) {
(DiagnosticSubmission::Enqueued, OriginalCaptureState::CompleteEnqueued) =>
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten),
facts => facts,
}
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[track_caller]
fn service_registry_source(
output: Option<ServiceRegistrySourceOutput<'_>>,
diagnostic: &Diagnostic,
raw: RawSource<'_>,
lifecycle: &'static str,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct RegistryContext {
schema_version: u8,
application: ContextFact<()>,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct RegistryHeader<'a> {
timestamp_unix_ms: u128,
context: RegistryContext,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = RegistryHeader {
timestamp_unix_ms: timestamp(),
context: RegistryContext {
schema_version: 2,
application: if lifecycle == "startup" { ContextFact::NotEstablished }
else { ContextFact::Unavailable },
lifecycle,
request: if lifecycle == "startup" { ContextFact::NotEstablished }
else { ContextFact::Unavailable },
trace_id: if lifecycle == "startup" { ContextFact::NotEstablished }
else { ContextFact::Unavailable },
},
stage: "service_registry", facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: match raw {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "unavailable_description_debug_only",
},
};
let result = match output {
Some(ServiceRegistrySourceOutput::Bootstrap(output)) =>
Some(capture(output, occurrence, raw, &header)),
Some(ServiceRegistrySourceOutput::Emergency(output)) =>
Some(capture(&ConfirmedOutput(output), occurrence, raw, &header)),
None => None,
};
let (submission, original_capture) = match result {
Some((DiagnosticSubmission::Enqueued, OriginalCaptureState::CompleteEnqueued)) =>
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten),
Some(result) => result,
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[track_caller]
pub fn service_request_source_error(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
call: &saddle_core::CallContext,
stage: &'static str,
) -> UnrootedCaptureFacts {
service_request_source(output, diagnostic, RawSource::Error(error), call, stage)
}
#[track_caller]
pub fn service_request_source_description(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
description: &(impl fmt::Display + fmt::Debug),
call: &saddle_core::CallContext,
stage: &'static str,
) -> UnrootedCaptureFacts {
service_request_source(output, diagnostic, RawSource::Description(description, description), call, stage)
}
#[track_caller]
pub fn runtime_admission_source_description(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
description: &(impl fmt::Display + fmt::Debug),
call: &saddle_core::CallContext,
) -> UnrootedCaptureFacts {
service_request_source(output, diagnostic, RawSource::Description(description, description),
call, "runtime_request_admission")
}
#[track_caller]
pub fn runtime_unrooted_admission_source_description(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
description: &(impl fmt::Display + fmt::Debug),
application: Option<&saddle_core::ApplicationId>,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct AdmissionContext<'a> {
schema_version: u8,
application: ContextFact<&'a str>,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct AdmissionHeader<'a> {
timestamp_unix_ms: u128,
context: AdmissionContext<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = AdmissionHeader {
timestamp_unix_ms: timestamp(),
context: AdmissionContext {
schema_version: 2,
application: application.map(|id| ContextFact::Present(id.as_str()))
.unwrap_or(ContextFact::Unavailable),
lifecycle: "request", request: ContextFact::NotEstablished,
trace_id: ContextFact::NotEstablished,
},
stage: "runtime_unrooted_admission", facts: diagnostic,
original_contract: "display-debug-source/v1",
source_interface: "unavailable_description_debug_only",
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence,
RawSource::Description(description, description), &header);
if submission == DiagnosticSubmission::Enqueued
&& state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) }
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[track_caller]
fn service_request_source(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
raw: RawSource<'_>,
call: &saddle_core::CallContext,
stage: &'static str,
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct ServiceContext<'a> {
schema_version: u8,
application: &'a str,
module: &'a str,
service: &'a str,
operation: &'a str,
trace_id: u128,
trace_correlation_id: &'a str,
span_id: u64,
rpc_correlation_id: Option<&'a str>,
request: ContextFact<()>,
lifecycle: &'static str,
}
#[derive(Serialize)]
struct ServiceHeader<'a> {
timestamp_unix_ms: u128,
context: ServiceContext<'a>,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = ServiceHeader {
timestamp_unix_ms: timestamp(),
context: ServiceContext {
schema_version: 2,
application: call.application().as_str(),
module: call.module().as_str(),
service: call.service().as_str(),
operation: call.operation().as_str(),
trace_id: call.trace_id().as_u128(),
trace_correlation_id: call.trace_correlation_id().as_str(),
span_id: call.span_id().as_u64(),
rpc_correlation_id: call.rpc_correlation_id().map(|id| id.as_str()),
request: ContextFact::Unavailable,
lifecycle: "request",
},
stage, facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: match raw {
RawSource::Error(_) | RawSource::BorrowedError(_) => "error_source",
RawSource::Description(_, _) => "unavailable_description_debug_only",
},
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(
&ConfirmedOutput(output), occurrence, raw, &header,
);
if submission == DiagnosticSubmission::Enqueued
&& state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) }
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
fn capture_config<O: OriginalOutput>(
output: &O,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct ConfigContext {
schema_version: u8,
application: ContextFact<()>,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct ConfigHeader<'a> {
timestamp_unix_ms: u128,
context: ConfigContext,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = ConfigHeader {
timestamp_unix_ms: timestamp(),
context: ConfigContext { schema_version: 2, application: ContextFact::NotEstablished,
lifecycle: "startup", request: ContextFact::NotEstablished,
trace_id: ContextFact::NotEstablished },
stage: "startup_config", facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: "error_source",
};
let (submission, state) = capture(output, occurrence, RawSource::Error(error), &header);
let (submission, original_capture) = if submission == DiagnosticSubmission::Enqueued
&& state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) };
UnrootedCaptureFacts { occurrence, submission, original_capture }
}
#[doc(hidden)]
pub fn process_finalization_error(
output: Option<&EmergencyDiagnosticHandle>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
process_lifecycle_error(output, ContextFact::Unavailable, "shutdown", "process_finalization", diagnostic, error)
}
#[doc(hidden)]
pub fn process_component_start_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> ComponentSourceReceipt {
ComponentSourceReceipt {
facts: process_lifecycle_error(output, application, "startup", "component_start", diagnostic, error),
kind: ComponentSourceKind::Start,
}
}
#[doc(hidden)]
pub fn process_component_start_recorded(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> Result<WrittenComponentFailure, ComponentSourceReceipt> {
WrittenComponentFailure::from_receipt(process_component_start_error(
output, application, diagnostic, error))
}
#[doc(hidden)]
pub fn process_component_cleanup_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> ComponentSourceReceipt {
ComponentSourceReceipt {
facts: process_lifecycle_error(output, application, "shutdown", "component_cleanup", diagnostic, error),
kind: ComponentSourceKind::Cleanup,
}
}
#[doc(hidden)]
pub fn process_component_cleanup_recorded(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> Result<WrittenComponentFailure, ComponentSourceReceipt> {
WrittenComponentFailure::from_receipt(process_component_cleanup_error(
output, application, diagnostic, error))
}
#[doc(hidden)]
pub fn process_shutdown_signal_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
process_lifecycle_error(output, application, "shutdown", "shutdown_signal", diagnostic, error)
}
#[doc(hidden)]
pub fn process_request_drain_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
process_lifecycle_error(output, application, "shutdown", "request_drain", diagnostic, error)
}
#[doc(hidden)]
pub fn process_post_driver_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
process_lifecycle_error(output, application, "shutdown", "post_driver", diagnostic, error)
}
fn process_lifecycle_error(
output: Option<&EmergencyDiagnosticHandle>,
application: ContextFact<saddle_core::ContextLabel>,
lifecycle: &'static str,
stage: &'static str,
diagnostic: &Diagnostic,
error: &(dyn Error + 'static),
) -> UnrootedCaptureFacts {
#[derive(Serialize)]
struct FinalizationContext {
schema_version: u8,
application: ContextFact<saddle_core::ContextLabel>,
lifecycle: &'static str,
request: ContextFact<()>,
trace_id: ContextFact<()>,
}
#[derive(Serialize)]
struct FinalizationHeader<'a> {
timestamp_unix_ms: u128,
context: FinalizationContext,
stage: &'static str,
facts: &'a Diagnostic,
original_contract: &'static str,
source_interface: &'static str,
}
let occurrence = diagnostic.occurrence();
let header = FinalizationHeader {
timestamp_unix_ms: timestamp(),
context: FinalizationContext { schema_version: 2,
application, lifecycle,
request: if lifecycle == "startup" { ContextFact::NotEstablished } else { ContextFact::NotApplicable },
trace_id: if lifecycle == "startup" { ContextFact::NotEstablished } else { ContextFact::NotApplicable } },
stage, facts: diagnostic,
original_contract: "display-debug-source/v1", source_interface: "error_source",
};
let (submission, original_capture) = match output {
Some(output) => {
let (submission, state) = capture(&ConfirmedOutput(output), occurrence, RawSource::Error(error), &header);
if submission == DiagnosticSubmission::Enqueued && state == OriginalCaptureState::CompleteEnqueued {
(DiagnosticSubmission::Written, OriginalCaptureState::CompleteWritten)
} else { (submission, state) }
}
None => (DiagnosticSubmission::OutputUnavailable, OriginalCaptureState::OutputUnavailable),
};
UnrootedCaptureFacts { occurrence, submission, original_capture }
}