Compare commits

...
Author SHA1 Message Date
Brandon Thomas feaac186aa more logging (endpoint failing without debugging info) 2026-04-15 17:48:44 -04:00
6 changed files with 65 additions and 8 deletions
@@ -24,6 +24,10 @@ pub struct NotificationDetails {
/// Whether this notification originated from an error.
pub(crate) is_from_error: bool,
/// Extra side-channel messages, eg. if we know we're sending an error/exception,
/// but want to include an additional message. (The "description" field is messy atm.)
pub(crate) extra_message: Option<String>,
/// HTTP method associated with the event, if any.
pub(crate) http_method: Option<String>,
@@ -90,25 +94,30 @@ impl NotificationDetails {
sections.push(desc.clone());
}
// 2. Error chain
// 2. Extra message
if let Some(message) = &self.extra_message {
sections.push(message.clone());
}
// 3. Error chain
if let Some(error_section) = self.format_error_chain() {
sections.push(error_section);
}
// 3. Event time
// 4. Event time
sections.push(self.format_event_time());
// 4. HTTP context
// 5. HTTP context
if let Some(http_section) = self.format_http_context() {
sections.push(http_section);
}
// 5. Service tokens
// 6. Service tokens
if let Some(tokens_section) = self.format_service_tokens() {
sections.push(tokens_section);
}
// 6. Application/service identity and hostname
// 7. Application/service identity and hostname
if let Some(identity_section) = Self::format_app_identity(application_name, service_id, hostname) {
sections.push(identity_section);
}
@@ -14,6 +14,10 @@ pub struct NotificationDetailsBuilder {
pub(crate) maybe_error: Option<Arc<dyn std::error::Error + Send + Sync + 'static>>,
pub(crate) is_from_error: bool,
/// Extra side-channel messages, eg. if we know we're sending an error/exception,
/// but want to include an additional message. (The "description" field is messy atm.)
pub(crate) extra_message: Option<String>,
pub(crate) http_method: Option<String>,
pub(crate) http_path: Option<String>,
pub(crate) http_status_code: Option<u16>,
@@ -41,6 +45,7 @@ impl NotificationDetailsBuilder {
event_time: Utc::now(),
maybe_error: None,
is_from_error: false,
extra_message: None,
http_method: None,
http_path: None,
http_status_code: None,
@@ -70,6 +75,7 @@ impl NotificationDetailsBuilder {
event_time: Utc::now(),
maybe_error: Some(error),
is_from_error: true,
extra_message: None,
http_method: None,
http_path: None,
http_status_code: None,
@@ -106,6 +112,11 @@ impl NotificationDetailsBuilder {
self
}
pub fn set_extra_message(mut self, extra_message: Option<String>) -> Self {
self.extra_message = extra_message;
self
}
pub fn set_http_method(mut self, http_method: Option<String>) -> Self {
self.http_method = http_method;
self
@@ -176,6 +187,7 @@ impl NotificationDetailsBuilder {
event_time: self.event_time,
maybe_error: self.maybe_error,
is_from_error: self.is_from_error,
extra_message: self.extra_message,
http_method: self.http_method,
http_path: self.http_path,
http_status_code: self.http_status_code,
@@ -50,6 +50,12 @@ pub enum AdvancedCommonWebError {
/// Stored in `Arc` so the error alerting middleware can clone it for paging
/// without consuming the original.
UncaughtServerError(Arc<dyn std::error::Error + Send + Sync + 'static>),
/// Same as above, with a specified extra message string
UncaughtServerErrorWithInternalMessage {
internal_message: String,
error: Arc<dyn std::error::Error + Send + Sync + 'static>
}
}
// =============== Public accessors ===============
@@ -60,6 +66,13 @@ impl AdvancedCommonWebError {
Self::UncaughtServerError(Arc::new(error))
}
pub fn from_error_with_message(message: String, error: impl std::error::Error + Send + Sync + 'static) -> Self {
Self::UncaughtServerErrorWithInternalMessage {
internal_message: message,
error: Arc::new(error)
}
}
/// Wrap an `anyhow::Error` as an `UncaughtServerError`.
/// (`anyhow::Error` doesn't implement `std::error::Error`, so `from_error` can't accept it.)
pub fn from_anyhow_error(error: anyhow::Error) -> Self {
@@ -75,6 +88,7 @@ impl AdvancedCommonWebError {
pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
match self {
Self::UncaughtServerError(err) => Some(err.as_ref()),
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(error.as_ref()),
_ => None,
}
}
@@ -84,13 +98,18 @@ impl AdvancedCommonWebError {
pub fn clone_cause_arc(&self) -> Option<Arc<dyn std::error::Error + Send + Sync + 'static>> {
match self {
Self::UncaughtServerError(err) => Some(Arc::clone(err)),
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(Arc::clone(error)),
_ => None,
}
}
/// Whether this is a server error (500).
pub fn is_server_error(&self) -> bool {
matches!(self, Self::UncaughtServerError(_))
match self {
Self::UncaughtServerError(_) => true,
Self::UncaughtServerErrorWithInternalMessage { .. } => true,
_ => false,
}
}
}
@@ -104,6 +123,9 @@ impl Display for AdvancedCommonWebError {
Self::NotFound => write!(f, "Not found"),
Self::PaymentRequired => write!(f, "Payment required"),
Self::UncaughtServerError(err) => write!(f, "Server error: {}", err),
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
write!(f, "Server error: {}: {}", internal_message, error)
}
}
}
}
@@ -116,6 +138,9 @@ impl std::fmt::Debug for AdvancedCommonWebError {
Self::NotFound => write!(f, "NotFound"),
Self::PaymentRequired => write!(f, "PaymentRequired"),
Self::UncaughtServerError(err) => write!(f, "UncaughtServerError({:?})", err),
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
write!(f, "UncaughtServerErrorWithInternalMessage({:?}, {:?})", internal_message, error)
}
}
}
}
@@ -139,6 +164,7 @@ impl ResponseError for AdvancedCommonWebError {
Self::NotFound => StatusCode::NOT_FOUND,
Self::PaymentRequired => StatusCode::PAYMENT_REQUIRED,
Self::UncaughtServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::UncaughtServerErrorWithInternalMessage { .. } => StatusCode::INTERNAL_SERVER_ERROR,
}
}
@@ -114,7 +114,10 @@ pub async fn edit_character_handler(
.await
.map_err(|err| {
error!("Error updating character on Kinovi: {:?}", err);
AdvancedCommonWebError::from_error(err)
AdvancedCommonWebError::from_error_with_message(
"Error Updating Kinovi Character API".to_string(),
err,
)
})?;
info!("Updated character {} on Kinovi (name='{}')", kinovi_id, final_name);
@@ -50,6 +50,13 @@ pub(crate) fn check_advanced_common_web_error(
.set_session_token(metadata.session_token.clone())
.set_session_user_token(metadata.session_user_token.clone());
match error {
AdvancedCommonWebError::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
builder = builder.set_extra_message(Some(internal_message.to_string()));
}
_ => {},
}
let notification = builder.build();
if let Err(err) = pager.enqueue_page(notification) {
@@ -16,7 +16,7 @@ pub(crate) fn check_status_code_fallback(
) {
match status_code {
500 => {
let summary = format!("HTTP 500: {} {}", method, path);
let summary = format!("[Legacy] HTTP 500: {} {}", method, path);
let description = format!(
"An untyped HTTP 500 response was returned (no typed error matched).\n\n\
Endpoint: {} {}\n\