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: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1521>
This commit is contained in:
François Laignel 2024-03-26 20:05:32 +01:00
parent 9f27bde36a
commit a870d60621
7 changed files with 34 additions and 17 deletions

View file

@ -276,10 +276,9 @@ impl S3HlsSink {
gst::error!( gst::error!(
CAT, CAT,
imp: self, 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_key,
s3_data_len, s3_data_len,
err,
); );
element_imp_error!( element_imp_error!(
self, self,
@ -320,9 +319,8 @@ impl S3HlsSink {
gst::error!( gst::error!(
CAT, CAT,
imp: self, imp: self,
"Delete object request for S3 key {} failed with error {:?}", "Delete object request for S3 key {} failed with error {err}",
s3_key, s3_key,
err
); );
element_imp_error!( element_imp_error!(
self, self,

View file

@ -14,6 +14,7 @@ use gst_base::subclass::prelude::*;
use aws_sdk_s3::{ use aws_sdk_s3::{
config::{self, retry::RetryConfig, Credentials, Region}, config::{self, retry::RetryConfig, Credentials, Region},
error::ProvideErrorMetadata,
operation::{ operation::{
abort_multipart_upload::builders::AbortMultipartUploadFluentBuilder, abort_multipart_upload::builders::AbortMultipartUploadFluentBuilder,
complete_multipart_upload::builders::CompleteMultipartUploadFluentBuilder, complete_multipart_upload::builders::CompleteMultipartUploadFluentBuilder,
@ -265,7 +266,7 @@ impl S3Sink {
self.flush_multipart_upload(state); self.flush_multipart_upload(state);
Some(gst::error_msg!( Some(gst::error_msg!(
gst::ResourceError::OpenWrite, gst::ResourceError::OpenWrite,
["Failed to upload part: {}", err] ["Failed to upload part: {err}: {}", err.meta()]
)) ))
} }
WaitError::Cancelled => None, WaitError::Cancelled => None,
@ -407,7 +408,7 @@ impl S3Sink {
WaitError::FutureError(err) => { WaitError::FutureError(err) => {
gst::error_msg!( gst::error_msg!(
gst::ResourceError::Write, gst::ResourceError::Write,
["Failed to abort multipart upload: {}.", err.to_string()] ["Failed to abort multipart upload: {err}: {}", err.meta()]
) )
} }
WaitError::Cancelled => { WaitError::Cancelled => {
@ -431,7 +432,7 @@ impl S3Sink {
.map_err(|err| match err { .map_err(|err| match err {
WaitError::FutureError(err) => gst::error_msg!( WaitError::FutureError(err) => gst::error_msg!(
gst::ResourceError::Write, gst::ResourceError::Write,
["Failed to complete multipart upload: {}.", err.to_string()] ["Failed to complete multipart upload: {err}: {}", err.meta()]
), ),
WaitError::Cancelled => { WaitError::Cancelled => {
gst::error_msg!( gst::error_msg!(
@ -512,7 +513,7 @@ impl S3Sink {
.map_err(|err| match err { .map_err(|err| match err {
WaitError::FutureError(err) => gst::error_msg!( WaitError::FutureError(err) => gst::error_msg!(
gst::ResourceError::OpenWrite, gst::ResourceError::OpenWrite,
["Failed to create SDK config: {}", err] ["Failed to create SDK config: {err}"]
), ),
WaitError::Cancelled => { WaitError::Cancelled => {
gst::error_msg!( gst::error_msg!(
@ -541,7 +542,7 @@ impl S3Sink {
|err| match err { |err| match err {
WaitError::FutureError(err) => gst::error_msg!( WaitError::FutureError(err) => gst::error_msg!(
gst::ResourceError::OpenWrite, gst::ResourceError::OpenWrite,
["Failed to create multipart upload: {}", err] ["Failed to create multipart upload: {err}: {}", err.meta()]
), ),
WaitError::Cancelled => { WaitError::Cancelled => {
gst::error_msg!( gst::error_msg!(

View file

@ -15,6 +15,7 @@ use gst_base::subclass::prelude::*;
use aws_sdk_s3::{ use aws_sdk_s3::{
config::{self, retry::RetryConfig, Credentials, Region}, config::{self, retry::RetryConfig, Credentials, Region},
error::ProvideErrorMetadata,
operation::put_object::builders::PutObjectFluentBuilder, operation::put_object::builders::PutObjectFluentBuilder,
primitives::ByteStream, primitives::ByteStream,
Client, Client,
@ -202,7 +203,7 @@ impl S3PutObjectSink {
s3utils::wait(&self.canceller, put_object_req_future).map_err(|err| match err { s3utils::wait(&self.canceller, put_object_req_future).map_err(|err| match err {
WaitError::FutureError(err) => Some(gst::error_msg!( WaitError::FutureError(err) => Some(gst::error_msg!(
gst::ResourceError::OpenWrite, gst::ResourceError::OpenWrite,
["Failed to upload object: {}", err] ["Failed to upload object: {err}: {}", err.meta()]
)), )),
WaitError::Cancelled => None, WaitError::Cancelled => None,
})?; })?;

View file

@ -14,6 +14,7 @@ use std::time::Duration;
use aws_sdk_s3::{ use aws_sdk_s3::{
config::{self, retry::RetryConfig, Credentials}, config::{self, retry::RetryConfig, Credentials},
error::ProvideErrorMetadata,
Client, Client,
}; };
@ -184,7 +185,7 @@ impl S3Src {
s3utils::wait(&self.canceller, head_object_future).map_err(|err| match err { s3utils::wait(&self.canceller, head_object_future).map_err(|err| match err {
WaitError::FutureError(err) => gst::error_msg!( WaitError::FutureError(err) => gst::error_msg!(
gst::ResourceError::NotFound, gst::ResourceError::NotFound,
["Failed to get HEAD object: {:?}", err] ["Failed to get HEAD object: {err}: {}", err.meta()]
), ),
WaitError::Cancelled => { WaitError::Cancelled => {
gst::error_msg!( gst::error_msg!(
@ -243,7 +244,7 @@ impl S3Src {
s3utils::wait(&self.canceller, get_object_future).map_err(|err| match err { s3utils::wait(&self.canceller, get_object_future).map_err(|err| match err {
WaitError::FutureError(err) => Some(gst::error_msg!( WaitError::FutureError(err) => Some(gst::error_msg!(
gst::ResourceError::Read, gst::ResourceError::Read,
["Could not read: {}", err] ["Could not read: {err}: {}", err.meta()]
)), )),
WaitError::Cancelled => None, WaitError::Cancelled => None,
})?; })?;
@ -253,7 +254,7 @@ impl S3Src {
s3utils::wait_stream(&self.canceller, &mut output.body).map_err(|err| match err { s3utils::wait_stream(&self.canceller, &mut output.body).map_err(|err| match err {
WaitError::FutureError(err) => Some(gst::error_msg!( WaitError::FutureError(err) => Some(gst::error_msg!(
gst::ResourceError::Read, gst::ResourceError::Read,
["Could not read: {}", err] ["Could not read: {err}"]
)), )),
WaitError::Cancelled => None, WaitError::Cancelled => None,
}) })

View file

@ -9,6 +9,7 @@
use aws_config::meta::region::RegionProviderChain; use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::{ use aws_sdk_s3::{
config::{timeout::TimeoutConfig, Credentials, Region}, config::{timeout::TimeoutConfig, Credentials, Region},
error::ProvideErrorMetadata,
primitives::{ByteStream, ByteStreamError}, primitives::{ByteStream, ByteStreamError},
}; };
use aws_types::sdk_config::SdkConfig; use aws_types::sdk_config::SdkConfig;
@ -16,6 +17,7 @@ use aws_types::sdk_config::SdkConfig;
use bytes::{buf::BufMut, Bytes, BytesMut}; use bytes::{buf::BufMut, Bytes, BytesMut};
use futures::{future, Future}; use futures::{future, Future};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::fmt;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
use tokio::runtime; use tokio::runtime;
@ -40,6 +42,15 @@ pub enum WaitError<E> {
FutureError(E), FutureError(E),
} }
impl<E: ProvideErrorMetadata + std::error::Error> fmt::Display for WaitError<E> {
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<F, T, E>( pub fn wait<F, T, E>(
canceller: &Mutex<Option<future::AbortHandle>>, canceller: &Mutex<Option<future::AbortHandle>>,
future: F, future: F,

View file

@ -11,6 +11,7 @@ use gst::subclass::prelude::*;
use gst::{glib, prelude::*}; use gst::{glib, prelude::*};
use aws_sdk_transcribestreaming as aws_transcribe; use aws_sdk_transcribestreaming as aws_transcribe;
use aws_sdk_transcribestreaming::error::ProvideErrorMetadata;
use aws_sdk_transcribestreaming::types; use aws_sdk_transcribestreaming::types;
use futures::channel::mpsc; use futures::channel::mpsc;
@ -165,7 +166,7 @@ impl TranscriberStream {
.send() .send()
.await .await
.map_err(|err| { .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!(CAT, imp: imp, "{err}");
gst::error_msg!(gst::LibraryError::Init, ["{err}"]) gst::error_msg!(gst::LibraryError::Init, ["{err}"])
})?; })?;
@ -187,7 +188,7 @@ impl TranscriberStream {
.recv() .recv()
.await .await
.map_err(|err| { .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!(CAT, imp: self.imp, "{err}");
gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) gst::error_msg!(gst::LibraryError::Failed, ["{err}"])
})?; })?;

View file

@ -10,6 +10,7 @@ use gst::glib;
use gst::subclass::prelude::*; use gst::subclass::prelude::*;
use aws_sdk_translate as aws_translate; use aws_sdk_translate as aws_translate;
use aws_sdk_translate::error::ProvideErrorMetadata;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::prelude::*; use futures::prelude::*;
@ -78,7 +79,10 @@ impl TranslateLoop {
pub async fn check_language(&self) -> Result<(), gst::ErrorMessage> { pub async fn check_language(&self) -> Result<(), gst::ErrorMessage> {
let language_list = self.client.list_languages().send().await.map_err(|err| { 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::info!(CAT, imp: self.pad, "{err}");
gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) gst::error_msg!(gst::LibraryError::Failed, ["{err}"])
})?; })?;
@ -143,7 +147,7 @@ impl TranslateLoop {
.send() .send()
.await .await
.map_err(|err| { .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::info!(CAT, imp: self.pad, "{err}");
gst::error_msg!(gst::LibraryError::Failed, ["{err}"]) gst::error_msg!(gst::LibraryError::Failed, ["{err}"])
})? })?