Skip to content

Add exception logging to service classes for improved debuggability#1728

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-exceptions-swallowed-issue
Draft

Add exception logging to service classes for improved debuggability#1728
Copilot wants to merge 3 commits intomainfrom
copilot/fix-exceptions-swallowed-issue

Conversation

Copy link

Copilot AI commented Jan 27, 2026

GitHub Issue (If applicable): #145

PR Type

What kind of change does this PR introduce?

  • Bugfix

What is the current behavior?

Exceptions thrown in service classes under Chefs.Business.Services are swallowed silently with no console output, making NativeAOT debugging nearly impossible. Users see "An error occurred" with no actionable information in logs.

What is the new behavior?

All public async methods in service classes now log exceptions before rethrowing. Adds ILogger<T> to:

  • ShareService
  • RecipeService
  • UserService
  • CookbookService
  • NotificationService

Pattern applied:

public async ValueTask<IImmutableList<Recipe>> GetAll(CancellationToken ct)
{
    try
    {
        return await GetAllCore(ct);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to get all recipes");
        throw;
    }
}

private async ValueTask<IImmutableList<Recipe>> GetAllCore(CancellationToken ct)
{
    // original implementation
}

Internal method calls use *Core variants directly to avoid redundant logging.

PR Checklist

Please check if your PR fulfills the following requirements:

  • Tested code with current supported SDKs
  • Docs have been added/updated which fit documentation template. (for bug fixes / features)
  • Unit Tests and/or UI Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Wasm UI Tests are not showing unexpected any differences. Validate PR Screenshots Compare Test Run results.
  • Contains NO breaking changes
  • Updated the Release Notes
  • Associated with an issue (GitHub or internal)

Other information

No breaking changes—loggers are injected via existing DI infrastructure.

Internal Issue (If applicable):

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • crl.godaddy.com
    • Triggering command: /usr/bin/dotnet dotnet build Chefs/Chefs.csproj -p:TargetFrameworkOverride=net9.0-desktop (dns block)
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/2B9D6FF9D398549B5FBB8065A02F803E/missingpackages_workingdir --packages /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • ocsp.godaddy.com
    • Triggering command: /usr/bin/dotnet dotnet build Chefs/Chefs.csproj -p:TargetFrameworkOverride=net9.0-desktop (dns block)
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/2B9D6FF9D398549B5FBB8065A02F803E/missingpackages_workingdir --packages /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /home/REDACTED/work/uno.chefs/.codeql-scratch/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Exceptions can be swallowed with no actionable output</issue_title>
<issue_description>(All the interesting bugs are under NativeAOT!)

Build and run Uno.Chefs for macOS desktop under NativeAOT, which in turn requires using Uno.Sdk 6.6.0:

sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.6.0-dev.3"/g' global.json
dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \
  -bl Chefs/Chefs.csproj -p:UseSkiaRendering=true -p:SkiaPublishAot=true \
  -p:SkiaPublishAot=true \
  -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \
  -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen
Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs

From the launch screen:

  1. Click Skip
  2. Click  Sign in with Apple
  3. Click 🔍Search on the left
  4. In the Search box, type m

Current behavior

The search results screen shows:

An error occurred.

Image

The console log shows nothing.

The lack of any information about the error that occurred makes it difficult to fix the bug.

Desired behavior

The console log should show the exception that was thrown.

Implementation

For all classes underneath the Chefs.Business.Services namespace, including:

  • Chefs/Business/Services/Sharing/ShareService.cs
  • Chefs/Business/Services/Recipes/RecipeService.cs
  • Chefs/Business/Services/Users/UserService.cs
  • Chefs/Business/Services/Cookbooks/CookbookService.cs
  • Chefs/Business/Services/Notifications/NotificationService.cs

Perform the following changes:

  1. Add an ILogger<T> logger constructor parameter, in which T is the declaring type:

    diff --git a/Chefs/Business/Services/Sharing/ShareService.cs b/Chefs/Business/Services/Sharing/ShareService.cs
    index ec38f458..0e5a6e07 100644
    --- a/Chefs/Business/Services/Sharing/ShareService.cs
    +++ b/Chefs/Business/Services/Sharing/ShareService.cs
    @@ -6,7 +6,7 @@ using WinRT.Interop;
     
     namespace Chefs.Business.Services.Sharing;
     
    -public class ShareService() : IShareService
    +public class ShareService(ILogger<ShareService> logger) : IShareService
     {
            private Recipe? _recipe;
            private IImmutableList<Step>? _steps;
  2. For each public method which returns Task, Task<T>, ValueTask, or ValueTask<T>, rename the method by appending a Core suffix, and change the visibility to private:

    diff --git a/Chefs/Business/Services/Sharing/ShareService.cs b/Chefs/Business/Services/Sharing/ShareService.cs
    index ec38f458..fb82cef4 100644
    --- a/Chefs/Business/Services/Sharing/ShareService.cs
    +++ b/Chefs/Business/Services/Sharing/ShareService.cs
    @@ -16,7 +16,7 @@ public class ShareService() : IShareService
            static IDataTransferManagerInterop DataTransferManagerInterop => DataTransferManager.As<IDataTransferManagerInterop>();
     #endif
     
    -       public async Task ShareRecipe(Recipe recipe, IImmutableList<Step> steps, CancellationToken ct)
    +       private async Task ShareRecipeCore(Recipe recipe, IImmutableList<Step> steps, CancellationToken ct)
            {
                    _recipe = recipe;
                    _steps = steps;
  3. For each method changed in (2), write a method with the original method declaration which invokes the Core suffixed method, wrapping the invocation in a try/catch block. The catch block should write the exception to the logger parameter, and re-throw the exception:

    	public async Task ShareRecipe(Recipe recipe, IImmutableList<Step> steps, CancellationToken ct)
    	{
    		try
    		{
    			await ShareRecipeCore(recipe, steps, ct);
    		}
    		catch (Exception ex)
    		{
    			logger.LogError(ex, "Failed to share recipe");
    			throw;
    		}
    	}

For example, the complete diff to ShareService is:

diff --git a/Chefs/Business/Services/Sharing/ShareService.cs b/Chefs/Business/Services/Sharing/ShareService.cs
index ec38f458..7149430e 100644
--- a/Chefs/Business/Services/Sharing/ShareService.cs
+++ b/Chefs/Business/Services/Sharing/ShareService.cs
@@ -6,7 +6,7 @@ using WinRT.Interop;
 
 namespace Chefs.Business.Services.Sharing;
 
-public class ShareService() : IShareService
+public class ShareService(ILogger<ShareService> logger) : IShareService
 {
 	private Recipe? _recipe;
 	private IImmutableList<Step>? _steps;
@@ -17,6 +17,19 @@ public class ShareService() : IShareService
 #endif
 
 	public async Task ShareRecipe(Recipe recipe, IImmutableList<Step> steps, CancellationToken ct)
+	{
+		try
+		{
+			await ShareRecipeCore(recipe, steps, ct);
+		}
+		catch (Ex...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes unoplatform/uno.chefs#1726

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 2 commits January 27, 2026 14:56
Co-authored-by: kazo0 <4793020+kazo0@users.noreply.github.com>
Co-authored-by: kazo0 <4793020+kazo0@users.noreply.github.com>
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

1 similar comment
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI changed the title [WIP] Fix exceptions not displaying actionable output in NativeAOT Add exception logging to service classes for improved debuggability Jan 27, 2026
Copilot AI requested a review from kazo0 January 27, 2026 15:01
jonpryor added a commit that referenced this pull request Jan 27, 2026
Context: #1726
Context: #1728

Build and run `Chefs.csproj` for e.g. net10.0-desktop and NativeAOT
while using Uno 6.6.0-dev.3:

	sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.6.0-dev.3"/g' global.json
	git clean -xdf Chefs{,.Api}
	dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \
	  -bl Chefs/Chefs.csproj -p:UseSkiaRendering=true -p:SkiaPublishAot=true \
	  -p:SkiaPublishAot=true \
	  -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \
	  -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen
	Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs

From the launch screen:

 1. Click **Skip**
 2. Click ** Sign in with Apple**
 3. Click **🔍Search** on the left
 4. In the Search box, type `m`

When doing this with e838af9, the search results screen shows:

> An error occurred.

![Search Results > An error occurred][0]

…which isn't particularly helpful.

If you do all this with #1728 applied, the Console log shows:

	fail: Chefs.Business.Services.Recipes.RecipeService[0]
	      Failed to search recipes with term m
	      System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfo() or IHostBuilder.UseSerialization(IHostBuilder, IEnumerable<IJsonTypeInfoResolver>, Action<IServiceCollection>) extension methods to enable JSON deserialization for type `System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Chefs.Business.Models.SearchHistory, Chefs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]`.
	         at Uno.Extensions.Serialization.SystemTextJsonSerializer.FromStream(Stream, Type) + 0x88
	         at Uno.Extensions.Serialization.SerializerExtensions.FromStream[T](ISerializer, Stream) + 0x34
	         at Uno.Extensions.Configuration.WritableOptions`1.<UpdateAsync>d__10.MoveNext() + 0x292
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<SaveSearchHistory>d__51.MoveNext() + 0x5d
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<SearchCore>d__24.MoveNext() + 0x8a
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<Search>d__23.MoveNext() + 0x175

which shows what the problem is: `RecipeService.Search()` tries to
save the search history, but JSON serialization does not support
`Dictionary<string, SearchHistory>`, so an `InvalidOperationException`
is thrown.

(Unfortunately the exception is basically swallowed; PR #1728 adds
logging to that code path, and was needed to diagnose this issue.)

Update `MockEndpointContext` to emit JSON serialization metadata
for `Dictionary<string, SearchHistory>`, so that Search now works.

[0]: https://private-user-images.githubusercontent.com/155958/541004949-2be43b36-4eb1-42b1-b91d-e53a5e5545de.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Njk1NDE0NjMsIm5iZiI6MTc2OTU0MTE2MywicGF0aCI6Ii8xNTU5NTgvNTQxMDA0OTQ5LTJiZTQzYjM2LTRlYjEtNDJiMS1iOTFkLWU1M2E1ZTU1NDVkZS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwMTI3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDEyN1QxOTEyNDNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yZDlkNzA5OTM0NzI2NWMxMjg4Njc0MTVmNWMyNjkzYTQzY2MzYWIxYTAzNGRlZjE5NjlmN2FjNzRkOGIxYjJiJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.a2gF7zICQIBUqWB-qdZv58UDg_DIwzKWAD8pNKJtu_g
jonpryor added a commit that referenced this pull request Jan 27, 2026
Context: #1726
Context: #1728

Build and run `Chefs.csproj` for e.g. net10.0-desktop and NativeAOT
while using Uno 6.6.0-dev.3:

	sed -i '' 's/"Uno.Sdk": ".*"/"Uno.Sdk": "6.6.0-dev.3"/g' global.json
	git clean -xdf Chefs{,.Api}
	dotnet publish -c Release -r osx-x64 -f net10.0-desktop -p:TargetFrameworkOverride=net10.0-desktop \
	  -bl Chefs/Chefs.csproj -p:UseSkiaRendering=true -p:SkiaPublishAot=true \
	  -p:SkiaPublishAot=true \
	  -p:IlcGenerateMapFile=true -p:IlcGenerateMstatFile=true -p:IlcGenerateDgmlFile=true -p:IlcGenerateMetadataLog=true \
	  -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=`pwd`/_gen
	Chefs/bin/Release/net10.0-desktop/osx-x64/publish/Chefs

From the launch screen:

 1. Click **Skip**
 2. Click ** Sign in with Apple**
 3. Click **🔍Search** on the left
 4. In the Search box, type `m`

When doing this with e838af9, the search results screen shows:

> An error occurred.

![Search Results > An error occurred][0]

…which isn't particularly helpful.

If you do all this with #1728 applied, the Console log shows:

	fail: Chefs.Business.Services.Recipes.RecipeService[0]
	      Failed to search recipes with term m
	      System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Use the IServiceCollection.AddJsonTypeInfo() or IHostBuilder.UseSerialization(IHostBuilder, IEnumerable<IJsonTypeInfoResolver>, Action<IServiceCollection>) extension methods to enable JSON deserialization for type `System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Chefs.Business.Models.SearchHistory, Chefs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]`.
	         at Uno.Extensions.Serialization.SystemTextJsonSerializer.FromStream(Stream, Type) + 0x88
	         at Uno.Extensions.Serialization.SerializerExtensions.FromStream[T](ISerializer, Stream) + 0x34
	         at Uno.Extensions.Configuration.WritableOptions`1.<UpdateAsync>d__10.MoveNext() + 0x292
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<SaveSearchHistory>d__51.MoveNext() + 0x5d
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<SearchCore>d__24.MoveNext() + 0x8a
	      --- End of stack trace from previous location ---
	         at Chefs.Business.Services.Recipes.RecipeService.<Search>d__23.MoveNext() + 0x175

which shows what the problem is: `RecipeService.Search()` tries to
save the search history, but JSON serialization does not support
`Dictionary<string, SearchHistory>`, so an `InvalidOperationException`
is thrown.

(Unfortunately the exception is basically swallowed; PR #1728 adds
logging to that code path, and was needed to diagnose this issue.)

Update `MockEndpointContext` to emit JSON serialization metadata
for `Dictionary<string, SearchHistory>`, so that Search now works.

[0]: https://private-user-images.githubusercontent.com/155958/541004949-2be43b36-4eb1-42b1-b91d-e53a5e5545de.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Njk1NDE0NjMsIm5iZiI6MTc2OTU0MTE2MywicGF0aCI6Ii8xNTU5NTgvNTQxMDA0OTQ5LTJiZTQzYjM2LTRlYjEtNDJiMS1iOTFkLWU1M2E1ZTU1NDVkZS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwMTI3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDEyN1QxOTEyNDNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yZDlkNzA5OTM0NzI2NWMxMjg4Njc0MTVmNWMyNjkzYTQzY2MzYWIxYTAzNGRlZjE5NjlmN2FjNzRkOGIxYjJiJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.a2gF7zICQIBUqWB-qdZv58UDg_DIwzKWAD8pNKJtu_g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants