Improve logging when avatar request fails

Max Brunsfeld and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/util/src/lib.rs | 34 +++++++++++++++++++++++++++++++---
crates/zed/src/user.rs |  5 ++++-
2 files changed, 35 insertions(+), 4 deletions(-)

Detailed changes

crates/util/src/lib.rs 🔗

@@ -40,6 +40,7 @@ pub trait ResultExt {
     type Ok;
 
     fn log_err(self) -> Option<Self::Ok>;
+    fn warn_on_err(self) -> Option<Self::Ok>;
 }
 
 impl<T> ResultExt for anyhow::Result<T> {
@@ -54,12 +55,25 @@ impl<T> ResultExt for anyhow::Result<T> {
             }
         }
     }
+
+    fn warn_on_err(self) -> Option<T> {
+        match self {
+            Ok(value) => Some(value),
+            Err(error) => {
+                log::warn!("{:?}", error);
+                None
+            }
+        }
+    }
 }
 
 pub trait TryFutureExt {
     fn log_err(self) -> LogErrorFuture<Self>
     where
         Self: Sized;
+    fn warn_on_err(self) -> LogErrorFuture<Self>
+    where
+        Self: Sized;
 }
 
 impl<F, T> TryFutureExt for F
@@ -70,11 +84,18 @@ where
     where
         Self: Sized,
     {
-        LogErrorFuture(self)
+        LogErrorFuture(self, log::Level::Error)
+    }
+
+    fn warn_on_err(self) -> LogErrorFuture<Self>
+    where
+        Self: Sized,
+    {
+        LogErrorFuture(self, log::Level::Warn)
     }
 }
 
-pub struct LogErrorFuture<F>(F);
+pub struct LogErrorFuture<F>(F, log::Level);
 
 impl<F, T> Future for LogErrorFuture<F>
 where
@@ -83,9 +104,16 @@ where
     type Output = Option<T>;
 
     fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let level = self.1;
         let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) };
         match inner.poll(cx) {
-            Poll::Ready(output) => Poll::Ready(output.log_err()),
+            Poll::Ready(output) => Poll::Ready(match output {
+                Ok(output) => Some(output),
+                Err(error) => {
+                    log::log!(level, "{:?}", error);
+                    None
+                }
+            }),
             Poll::Pending => Poll::Pending,
         }
     }

crates/zed/src/user.rs 🔗

@@ -209,7 +209,7 @@ impl User {
         User {
             id: message.id,
             github_login: message.github_login,
-            avatar: fetch_avatar(http, &message.avatar_url).log_err().await,
+            avatar: fetch_avatar(http, &message.avatar_url).warn_on_err().await,
         }
     }
 }
@@ -257,6 +257,9 @@ async fn fetch_avatar(http: &dyn HttpClient, url: &str) -> Result<Arc<ImageData>
         .send(request)
         .await
         .map_err(|e| anyhow!("failed to send user avatar request: {}", e))?;
+    if !response.status().is_success() {
+        return Err(anyhow!("avatar request failed {:?}", response.status()));
+    }
     let bytes = response
         .body_bytes()
         .await