Skip to content

Commit

Permalink
Merge pull request #24 from AvaloniaUtils/addLogging
Browse files Browse the repository at this point in the history
Add errors logging to loaders
  • Loading branch information
SKProCH authored Aug 17, 2024
2 parents f02aaab + 274706f commit 00edb42
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
9 changes: 9 additions & 0 deletions AsyncImageLoader.Avalonia/AdvancedImage.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Imaging;
Expand Down Expand Up @@ -74,6 +75,7 @@ public class AdvancedImage : ContentControl
private bool _shouldLoaderChangeTriggerUpdate;

private CancellationTokenSource? _updateCancellationToken;
private readonly ParametrizedLogger? _logger;

static AdvancedImage()
{
Expand All @@ -89,6 +91,7 @@ static AdvancedImage()
public AdvancedImage(Uri? baseUri)
{
_baseUri = baseUri;
_logger = Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
}

/// <summary>
Expand Down Expand Up @@ -241,6 +244,12 @@ private async void UpdateImage(string? source, IAsyncImageLoader? loader)
{
return null;
}
catch (Exception e)
{
_logger?.Log(this, "AdvancedImage image resolution failed: {0}", e);

return null;
}
finally
{
cancellationTokenSource.Dispose();
Expand Down
23 changes: 19 additions & 4 deletions AsyncImageLoader.Avalonia/ImageBrushLoader.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
using AsyncImageLoader.Loaders;
using System;
using AsyncImageLoader.Loaders;
using Avalonia;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Media.Imaging;

namespace AsyncImageLoader {
public static class ImageBrushLoader {
private static readonly ParametrizedLogger? Logger;
public static IAsyncImageLoader AsyncImageLoader { get; set; } = new RamCachedWebImageLoader();
static ImageBrushLoader() {
SourceProperty.Changed.AddClassHandler<ImageBrush>(OnSourceChanged);
Logger = Avalonia.Logging.Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
}

private static async void OnSourceChanged(ImageBrush imageBrush, AvaloniaPropertyChangedEventArgs args) {
Expand All @@ -16,9 +21,19 @@ private static async void OnSourceChanged(ImageBrush imageBrush, AvaloniaPropert

SetIsLoading(imageBrush, true);

var bitmap = newValue == null
? null
: await AsyncImageLoader.ProvideImageAsync(newValue);
Bitmap? bitmap = null;
try
{
if (newValue is not null)
{
bitmap = await AsyncImageLoader.ProvideImageAsync(newValue);
}
}
catch (Exception e)
{
Logger?.Log("ImageBrushLoader", "ImageBrushLoader image resolution failed: {0}", e);
}

if (GetSource(imageBrush) != newValue) return;
imageBrush.Source = bitmap;

Expand Down
19 changes: 14 additions & 5 deletions AsyncImageLoader.Avalonia/ImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using System.Collections.Concurrent;
using Avalonia.Logging;

namespace AsyncImageLoader;

public static class ImageLoader
{
public const string AsyncImageLoaderLogArea = "AsyncImageLoader";
private static readonly ParametrizedLogger? Logger;
public const string AsyncImageLoaderLogArea = "AsyncImageLoader";

public static readonly AttachedProperty<string?> SourceProperty =
AvaloniaProperty.RegisterAttached<Image, string?>("Source", typeof(ImageLoader));
Expand All @@ -23,16 +25,17 @@ public static class ImageLoader
static ImageLoader()
{
SourceProperty.Changed.AddClassHandler<Image>(OnSourceChanged);
Logger = Avalonia.Logging.Logger.TryGet(LogEventLevel.Error, AsyncImageLoaderLogArea);
}

public static IAsyncImageLoader AsyncImageLoader { get; set; } = new RamCachedWebImageLoader();

private static ConcurrentDictionary<Image, CancellationTokenSource> _pendingOperations = new ConcurrentDictionary<Image, CancellationTokenSource>();
private static readonly ConcurrentDictionary<Image, CancellationTokenSource> PendingOperations = new();
private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedEventArgs args) {
var url = args.GetNewValue<string?>();

// Cancel/Add new pending operation
CancellationTokenSource? cts = _pendingOperations.AddOrUpdate(sender, new CancellationTokenSource(),
CancellationTokenSource? cts = PendingOperations.AddOrUpdate(sender, new CancellationTokenSource(),
(x, y) =>
{
y.Cancel();
Expand All @@ -41,7 +44,7 @@ private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedE

if (url == null)
{
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)_pendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)PendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
sender.Source = null;
return;
}
Expand All @@ -62,13 +65,19 @@ private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedE
{
return null;
}
catch (Exception e)
{
Logger?.Log(LogEventLevel.Error, "ImageLoader image resolution failed: {0}", e);

return null;
}
});

if (bitmap != null && !cts.Token.IsCancellationRequested)
sender.Source = bitmap!;

// "It is not guaranteed to be thread safe by ICollection, but ConcurrentDictionary's implementation is. Additionally, we recently exposed this API for .NET 5 as a public ConcurrentDictionary.TryRemove"
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)_pendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)PendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
SetIsLoading(sender, false);
}

Expand Down
11 changes: 8 additions & 3 deletions AsyncImageLoader.Avalonia/Loaders/BaseWebImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public BaseWebImageLoader() : this(new HttpClient(), true) { }
public BaseWebImageLoader(HttpClient httpClient, bool disposeHttpClient) {
HttpClient = httpClient;
_shouldDisposeHttpClient = disposeHttpClient;
_logger = Logger.TryGet(LogEventLevel.Information, ImageLoader.AsyncImageLoaderLogArea);
_logger = Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
}

protected HttpClient HttpClient { get; }
Expand Down Expand Up @@ -69,7 +69,10 @@ await LoadFromLocalAsync(url).ConfigureAwait(false)
await SaveToGlobalCache(url, externalBytes).ConfigureAwait(false);
return bitmap;
}
catch (Exception) {
catch (Exception e)
{
_logger?.Log(this, "Failed to resolve image: {RequestUri}\nException: {Exception}", url, e);

return null;
}
}
Expand Down Expand Up @@ -121,7 +124,9 @@ await LoadFromLocalAsync(url).ConfigureAwait(false)
try {
return await HttpClient.GetByteArrayAsync(url).ConfigureAwait(false);
}
catch (Exception) {
catch (Exception e) {
_logger?.Log(this,
"Failed to resolve image from request with uri: {RequestUri}\nException: {Exception}", url, e);
return null;
}
}
Expand Down

0 comments on commit 00edb42

Please sign in to comment.