From a870d6062186f6164a0e5fb34743b5050b808a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Tue, 26 Mar 2024 20:05:32 +0100 Subject: [PATCH] aws: improve error message logs The `Display` and `Debug` trait for the AWS error messages are not very useful. - `Display` only prints the high level error, e.g.: "service error". - `Debug` prints all the fields in the error stack, resulting in hard to read messages with redudant or unnecessary information. E.g.: > ServiceError(ServiceError { source: BadRequestException(BadRequestException { > message: Some("1 validation error detected: Value 'test' at 'languageCode' > failed to satisfy constraint: Member must satisfy enum value set: [ar-AE, > zh-HK, en-US, ar-SA, zh-CN, fi-FI, pl-PL, no-NO, nl-NL, pt-PT, es-ES, th-TH, > de-DE, it-IT, fr-FR, ko-KR, hi-IN, en-AU, pt-BR, sv-SE, ja-JP, ca-ES, es-US, > fr-CA, en-GB]"), meta: ErrorMetadata { code: Some("BadRequestException"), > message: Some("1 validation error detected: Value 'test' at 'languageCode' > failed to satisfy constraint: Member must satisfy enum value set: [ar-AE, > zh-HK, en-US, ar-SA, zh-CN, fi-FI, pl-PL, no-NO, nl-NL, pt-PT, es-ES, th-TH, > de-DE, it-IT, fr-FR, ko-KR, hi-IN, en-AU, pt-BR, sv-SE, ja-JP, ca-ES, es-US, > fr-CA, en-GB]"), extras: Some({"aws_request_id": "1b8bbafd-5b71-4ba5-8676-28432381e6a9"}) } }), > raw: Response { status: StatusCode(400), headers: Headers { headers: > {"x-amzn-requestid": HeaderValue { _private: H0("1b8bbafd-5b71-4ba5-8676-28432381e6a9") }, > "x-amzn-errortype": HeaderValue { _private: > H0("BadRequestException:http://internal.amazon.com/coral/com.amazonaws.transcribe.streaming/") }, > "date": HeaderValue { _private: H0("Tue, 26 Mar 2024 17:41:31 GMT") }, > "content-type": HeaderValue { _private: H0("application/x-amz-json-1.1") }, > "content-length": HeaderValue { _private: H0("315") }} }, body: SdkBody { > inner: Once(Some(b"{\"Message\":\"1 validation error detected: Value 'test' > at 'languageCode' failed to satisfy constraint: Member must satisfy enum value > set: [ar-AE, zh-HK, en-US, ar-SA, zh-CN, fi-FI, pl-PL, no-NO, nl-NL, pt-PT, > es-ES, th-TH, de-DE, it-IT, fr-FR, ko-KR, hi-IN, en-AU, pt-BR, sv-SE, ja-JP, > ca-ES, es-US, fr-CA, en-GB]\"}")), retryable: true }, extensions: Extensions { > extensions_02x: Extensions, extensions_1x: Extensions } } }) This commit adopts the most informative and concise solution I could come up with to log AWS errors. With the above error case, this results in: > service error: Error { code: "BadRequestException", message: "1 validation > error detected: Value 'test' at 'languageCode' failed to satisfy constraint: > Member must satisfy enum value set: [ar-AE, zh-HK, en-US, ar-SA, zh-CN, fi-FI, > pl-PL, no-NO, nl-NL, pt-PT, es-ES, th-TH, de-DE, it-IT, fr-FR, ko-KR, hi-IN, > en-AU, pt-BR, sv-SE, ja-JP, ca-ES, es-US, fr-CA, en-GB]", > aws_request_id: "a40a32a8-7b0b-4228-a348-f8502087a9f0" } Part-of: --- net/aws/src/s3hlssink/imp.rs | 6 ++---- net/aws/src/s3sink/multipartsink.rs | 11 ++++++----- net/aws/src/s3sink/putobjectsink.rs | 3 ++- net/aws/src/s3src/imp.rs | 7 ++++--- net/aws/src/s3utils.rs | 11 +++++++++++ net/aws/src/transcriber/transcribe.rs | 5 +++-- net/aws/src/transcriber/translate.rs | 8 ++++++-- 7 files changed, 34 insertions(+), 17 deletions(-) diff --git a/net/aws/src/s3hlssink/imp.rs b/net/aws/src/s3hlssink/imp.rs index 79fe4db9..6302fdfd 100644 --- a/net/aws/src/s3hlssink/imp.rs +++ b/net/aws/src/s3hlssink/imp.rs @@ -276,10 +276,9 @@ impl S3HlsSink { gst::error!( CAT, imp: self, - "Put object request for S3 key {} of data length {} failed with error {:?}", + "Put object request for S3 key {} of data length {} failed with error {err}", s3_key, s3_data_len, - err, ); element_imp_error!( self, @@ -320,9 +319,8 @@ impl S3HlsSink { gst::error!( CAT, imp: self, - "Delete object request for S3 key {} failed with error {:?}", + "Delete object request for S3 key {} failed with error {err}", s3_key, - err ); element_imp_error!( self, diff --git a/net/aws/src/s3sink/multipartsink.rs b/net/aws/src/s3sink/multipartsink.rs index 5f8d25fb..5554fb09 100644 --- a/net/aws/src/s3sink/multipartsink.rs +++ b/net/aws/src/s3sink/multipartsink.rs @@ -14,6 +14,7 @@ use gst_base::subclass::prelude::*; use aws_sdk_s3::{ config::{self, retry::RetryConfig, Credentials, Region}, + error::ProvideErrorMetadata, operation::{ abort_multipart_upload::builders::AbortMultipartUploadFluentBuilder, complete_multipart_upload::builders::CompleteMultipartUploadFluentBuilder, @@ -265,7 +266,7 @@ impl S3Sink { self.flush_multipart_upload(state); Some(gst::error_msg!( gst::ResourceError::OpenWrite, - ["Failed to upload part: {}", err] + ["Failed to upload part: {err}: {}", err.meta()] )) } WaitError::Cancelled => None, @@ -407,7 +408,7 @@ impl S3Sink { WaitError::FutureError(err) => { gst::error_msg!( gst::ResourceError::Write, - ["Failed to abort multipart upload: {}.", err.to_string()] + ["Failed to abort multipart upload: {err}: {}", err.meta()] ) } WaitError::Cancelled => { @@ -431,7 +432,7 @@ impl S3Sink { .map_err(|err| match err { WaitError::FutureError(err) => gst::error_msg!( gst::ResourceError::Write, - ["Failed to complete multipart upload: {}.", err.to_string()] + ["Failed to complete multipart upload: {err}: {}", err.meta()] ), WaitError::Cancelled => { gst::error_msg!( @@ -512,7 +513,7 @@ impl S3Sink { .map_err(|err| match err { WaitError::FutureError(err) => gst::error_msg!( gst::ResourceError::OpenWrite, - ["Failed to create SDK config: {}", err] + ["Failed to create SDK config: {err}"] ), WaitError::Cancelled => { gst::error_msg!( @@ -541,7 +542,7 @@ impl S3Sink { |err| match err { WaitError::FutureError(err) => gst::error_msg!( gst::ResourceError::OpenWrite, - ["Failed to create multipart upload: {}", err] + ["Failed to create multipart upload: {err}: {}", err.meta()] ), WaitError::Cancelled => { gst::error_msg!( diff --git a/net/aws/src/s3sink/putobjectsink.rs b/net/aws/src/s3sink/putobjectsink.rs index 3c6df124..8b13d5f3 100644 --- a/net/aws/src/s3sink/putobjectsink.rs +++ b/net/aws/src/s3sink/putobjectsink.rs @@ -15,6 +15,7 @@ use gst_base::subclass::prelude::*; use aws_sdk_s3::{ config::{self, retry::RetryConfig, Credentials, Region}, + error::ProvideErrorMetadata, operation::put_object::builders::PutObjectFluentBuilder, primitives::ByteStream, Client, @@ -202,7 +203,7 @@ impl S3PutObjectSink { s3utils::wait(&self.canceller, put_object_req_future).map_err(|err| match err { WaitError::FutureError(err) => Some(gst::error_msg!( gst::ResourceError::OpenWrite, - ["Failed to upload object: {}", err] + ["Failed to upload object: {err}: {}", err.meta()] )), WaitError::Cancelled => None, })?; diff --git a/net/aws/src/s3src/imp.rs b/net/aws/src/s3src/imp.rs index 242725ae..bfb8b76f 100644 --- a/net/aws/src/s3src/imp.rs +++ b/net/aws/src/s3src/imp.rs @@ -14,6 +14,7 @@ use std::time::Duration; use aws_sdk_s3::{ config::{self, retry::RetryConfig, Credentials}, + error::ProvideErrorMetadata, Client, }; @@ -184,7 +185,7 @@ impl S3Src { s3utils::wait(&self.canceller, head_object_future).map_err(|err| match err { WaitError::FutureError(err) => gst::error_msg!( gst::ResourceError::NotFound, - ["Failed to get HEAD object: {:?}", err] + ["Failed to get HEAD object: {err}: {}", err.meta()] ), WaitError::Cancelled => { gst::error_msg!( @@ -243,7 +244,7 @@ impl S3Src { s3utils::wait(&self.canceller, get_object_future).map_err(|err| match err { WaitError::FutureError(err) => Some(gst::error_msg!( gst::ResourceError::Read, - ["Could not read: {}", err] + ["Could not read: {err}: {}", err.meta()] )), WaitError::Cancelled => None, })?; @@ -253,7 +254,7 @@ impl S3Src { s3utils::wait_stream(&self.canceller, &mut output.body).map_err(|err| match err { WaitError::FutureError(err) => Some(gst::error_msg!( gst::ResourceError::Read, - ["Could not read: {}", err] + ["Could not read: {err}"] )), WaitError::Cancelled => None, }) diff --git a/net/aws/src/s3utils.rs b/net/aws/src/s3utils.rs index 24c8b4b9..dc03a465 100644 --- a/net/aws/src/s3utils.rs +++ b/net/aws/src/s3utils.rs @@ -9,6 +9,7 @@ use aws_config::meta::region::RegionProviderChain; use aws_sdk_s3::{ config::{timeout::TimeoutConfig, Credentials, Region}, + error::ProvideErrorMetadata, primitives::{ByteStream, ByteStreamError}, }; use aws_types::sdk_config::SdkConfig; @@ -16,6 +17,7 @@ use aws_types::sdk_config::SdkConfig; use bytes::{buf::BufMut, Bytes, BytesMut}; use futures::{future, Future}; use once_cell::sync::Lazy; +use std::fmt; use std::sync::Mutex; use std::time::Duration; use tokio::runtime; @@ -40,6 +42,15 @@ pub enum WaitError { FutureError(E), } +impl fmt::Display for WaitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WaitError::Cancelled => f.write_str("Cancelled"), + WaitError::FutureError(err) => write!(f, "{err}: {}", err.meta()), + } + } +} + pub fn wait( canceller: &Mutex>, future: F, diff --git a/net/aws/src/transcriber/transcribe.rs b/net/aws/src/transcriber/transcribe.rs index 8c931668..cff88f15 100644 --- a/net/aws/src/transcriber/transcribe.rs +++ b/net/aws/src/transcriber/transcribe.rs @@ -11,6 +11,7 @@ use gst::subclass::prelude::*; use gst::{glib, prelude::*}; use aws_sdk_transcribestreaming as aws_transcribe; +use aws_sdk_transcribestreaming::error::ProvideErrorMetadata; use aws_sdk_transcribestreaming::types; use futures::channel::mpsc; @@ -165,7 +166,7 @@ impl TranscriberStream { .send() .await .map_err(|err| { - let err = format!("Transcribe ws init error: {err}"); + let err = format!("Transcribe ws init error: {err}: {}", err.meta()); gst::error!(CAT, imp: imp, "{err}"); gst::error_msg!(gst::LibraryError::Init, ["{err}"]) })?; @@ -187,7 +188,7 @@ impl TranscriberStream { .recv() .await .map_err(|err| { - let err = format!("Transcribe ws stream error: {err}"); + let err = format!("Transcribe ws stream error: {err}: {}", err.meta()); gst::error!(CAT, imp: self.imp, "{err}"); gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) })?; diff --git a/net/aws/src/transcriber/translate.rs b/net/aws/src/transcriber/translate.rs index 23c0c85c..0b37422b 100644 --- a/net/aws/src/transcriber/translate.rs +++ b/net/aws/src/transcriber/translate.rs @@ -10,6 +10,7 @@ use gst::glib; use gst::subclass::prelude::*; use aws_sdk_translate as aws_translate; +use aws_sdk_translate::error::ProvideErrorMetadata; use futures::channel::mpsc; use futures::prelude::*; @@ -78,7 +79,10 @@ impl TranslateLoop { pub async fn check_language(&self) -> Result<(), gst::ErrorMessage> { let language_list = self.client.list_languages().send().await.map_err(|err| { - let err = format!("Failed to call list_languages service: {err}"); + let err = format!( + "Failed to call list_languages service: {err}: {}", + err.meta() + ); gst::info!(CAT, imp: self.pad, "{err}"); gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) })?; @@ -143,7 +147,7 @@ impl TranslateLoop { .send() .await .map_err(|err| { - let err = format!("Failed to call translation service: {err}"); + let err = format!("Failed to call translation service: {err}: {}", err.meta()); gst::info!(CAT, imp: self.pad, "{err}"); gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) })?