Skip to content

Commit 13a0caa

Browse files
authored
Introduced MauiExceptions to setup unified UnhandledExceptionEventHandler (#17)
Credits Matt Johnson-Pint
1 parent 8c70fe3 commit 13a0caa

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,39 @@ Example `NLog.config`-file:
7272
</rules>
7373
</nlog>
7474
```
75+
76+
### Logging Unhandled Exceptions
77+
78+
The MAUI platform have failed to provide an unified way of tracking unhandled exceptions.
79+
80+
The normal way would be to hook into `AppDomain.CurrentDomain.UnhandledException`, but extra logic needed for iOS and Android:
81+
82+
Exceptions on iOS flows through AppDomain.CurrentDomain.UnhandledException, but one must set [UnwindNativeCode](https://github.com/xamarin/xamarin-macios/issues/15252):
83+
```csharp
84+
ObjCRuntime.Runtime.MarshalManagedException += (_, args) =>
85+
{
86+
args.ExceptionMode = ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode;
87+
};
88+
```
89+
90+
Exceptions on Android only flows through `Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser`:
91+
```csharp
92+
Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
93+
{
94+
// Log unhandled args.Exception
95+
};
96+
```
97+
98+
NLog.Targets.MauiLog includes unified method to setup `UnhandledExceptionEventHandler`:
99+
```csharp
100+
NLog.LogManager.Setup().RegisterMauiLog((sender, ex) => {
101+
NLog.LogManager.GetLogger("Application").Fatal(ex, "Unhandled Exception");
102+
});
103+
```
104+
105+
One can also consider monitoring unobserved task exceptions:
106+
```csharp
107+
TaskScheduler.UnobservedTaskException += (sender, args) => {
108+
NLog.LogManager.GetLogger("Application").Error(args.Exception, "Unobserved Task Exception");
109+
};
110+
```

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
version: 5.0.0.{build}
2-
image: Visual Studio 2022
2+
image: Previous Visual Studio 2022
33
configuration: Release
44
platform: Any CPU
55
build_script:

src/NLog.Targets.MauiLog/Config/SetupBuilderExtensions.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using NLog.Config;
1+
using System;
2+
using NLog.Config;
23

34
namespace NLog
45
{
@@ -15,5 +16,19 @@ public static ISetupBuilder RegisterMauiLog(this ISetupBuilder setupBuilder)
1516
setupBuilder.SetupExtensions(e => e.RegisterMauiLog());
1617
return setupBuilder;
1718
}
19+
20+
/// <summary>
21+
/// Register the NLog.Web LayoutRenderers before loading NLog config
22+
/// </summary>
23+
public static ISetupBuilder RegisterMauiLog(this ISetupBuilder setupBuilder, UnhandledExceptionEventHandler unhandledException)
24+
{
25+
if (unhandledException is null)
26+
throw new ArgumentNullException(nameof(unhandledException));
27+
28+
setupBuilder.SetupExtensions(e => e.RegisterMauiLog());
29+
NLog.Targets.MauiLog.MauiExceptions.UnhandledException -= unhandledException; // Avoid double registration
30+
NLog.Targets.MauiLog.MauiExceptions.UnhandledException += unhandledException;
31+
return setupBuilder;
32+
}
1833
}
1934
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// MIT License
2+
//
3+
// Copyright (c) 2022 Matt Johnson-Pint
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
namespace NLog.Targets.MauiLog
24+
{
25+
using System;
26+
27+
internal static class MauiExceptions
28+
{
29+
// We'll route all unhandled exceptions through this one event.
30+
public static event UnhandledExceptionEventHandler UnhandledException;
31+
32+
static MauiExceptions()
33+
{
34+
// This is the normal event expected, and should still be used.
35+
// It will fire for exceptions from iOS and Mac Catalyst,
36+
// and for exceptions on background threads from WinUI 3.
37+
38+
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
39+
{
40+
UnhandledException?.Invoke(sender, args);
41+
};
42+
43+
#if __APPLE__
44+
45+
// For iOS and Mac Catalyst
46+
// Exceptions will flow through AppDomain.CurrentDomain.UnhandledException,
47+
// but we need to set UnwindNativeCode to get it to work correctly.
48+
//
49+
// See: https://github.com/xamarin/xamarin-macios/issues/15252
50+
51+
ObjCRuntime.Runtime.MarshalManagedException += (_, args) =>
52+
{
53+
args.ExceptionMode = ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode;
54+
};
55+
56+
#elif __ANDROID__
57+
58+
// For Android:
59+
// All exceptions will flow through Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser,
60+
// and NOT through AppDomain.CurrentDomain.UnhandledException
61+
62+
Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
63+
{
64+
UnhandledException?.Invoke(sender, new UnhandledExceptionEventArgs(args.Exception, true));
65+
};
66+
#endif
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)