From 1b702e69e5a597ff1a27b113081f8ca62f0512c6 Mon Sep 17 00:00:00 2001 From: Thomas Ardal Date: Fri, 24 May 2024 11:58:55 +0200 Subject: [PATCH] Initial version --- .github/FUNDING.yml | 1 + .github/workflows/build.yml | 59 +++++++ Elmah.Io.WinUI.sln | 98 +++++++++++ samples/Elmah.Io.WinUI.Sample/App.xaml | 16 ++ samples/Elmah.Io.WinUI.Sample/App.xaml.cs | 52 ++++++ .../Assets/LockScreenLogo.scale-200.png | Bin 0 -> 432 bytes .../Assets/SplashScreen.scale-200.png | Bin 0 -> 5372 bytes .../Assets/Square150x150Logo.scale-200.png | Bin 0 -> 1755 bytes .../Assets/Square44x44Logo.scale-200.png | Bin 0 -> 637 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 0 -> 283 bytes .../Assets/StoreLogo.png | Bin 0 -> 456 bytes .../Assets/Wide310x150Logo.scale-200.png | Bin 0 -> 2097 bytes .../Elmah.Io.WinUI.Sample.csproj | 52 ++++++ samples/Elmah.Io.WinUI.Sample/MainWindow.xaml | 14 ++ .../Elmah.Io.WinUI.Sample/MainWindow.xaml.cs | 41 +++++ .../Package.appxmanifest | 51 ++++++ .../Properties/launchSettings.json | 10 ++ samples/Elmah.Io.WinUI.Sample/app.manifest | 19 +++ src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj | 40 +++++ src/Elmah.Io.WinUI/ElmahIoWinUI.cs | 154 ++++++++++++++++++ src/Elmah.Io.WinUI/ElmahIoWinUIOptions.cs | 50 ++++++ src/Elmah.Io.WinUI/images/icon.png | Bin 0 -> 8119 bytes .../Elmah.Io.WinUI.Test.csproj | 23 +++ test/Elmah.Io.WinUI.Test/ElmahIoWinUITest.cs | 66 ++++++++ 24 files changed, 746 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/build.yml create mode 100644 Elmah.Io.WinUI.sln create mode 100644 samples/Elmah.Io.WinUI.Sample/App.xaml create mode 100644 samples/Elmah.Io.WinUI.Sample/App.xaml.cs create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/LockScreenLogo.scale-200.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/SplashScreen.scale-200.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/Square150x150Logo.scale-200.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/Square44x44Logo.scale-200.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/Square44x44Logo.targetsize-24_altform-unplated.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/StoreLogo.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Assets/Wide310x150Logo.scale-200.png create mode 100644 samples/Elmah.Io.WinUI.Sample/Elmah.Io.WinUI.Sample.csproj create mode 100644 samples/Elmah.Io.WinUI.Sample/MainWindow.xaml create mode 100644 samples/Elmah.Io.WinUI.Sample/MainWindow.xaml.cs create mode 100644 samples/Elmah.Io.WinUI.Sample/Package.appxmanifest create mode 100644 samples/Elmah.Io.WinUI.Sample/Properties/launchSettings.json create mode 100644 samples/Elmah.Io.WinUI.Sample/app.manifest create mode 100644 src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj create mode 100644 src/Elmah.Io.WinUI/ElmahIoWinUI.cs create mode 100644 src/Elmah.Io.WinUI/ElmahIoWinUIOptions.cs create mode 100644 src/Elmah.Io.WinUI/images/icon.png create mode 100644 test/Elmah.Io.WinUI.Test/Elmah.Io.WinUI.Test.csproj create mode 100644 test/Elmah.Io.WinUI.Test/ElmahIoWinUITest.cs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..abf45ac --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://elmah.io/', 'https://elmah.io/pricing/'] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ec5f7b7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,59 @@ +name: build + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 6.0.x + 7.0.x + 8.0.x + + - name: Restore + run: nuget restore Elmah.Io.WinUI.sln + + - name: Check for vulnerable packages + run: | + set -e # This will cause the script to exit on the first error + OUTPUT=$(dotnet list package --vulnerable) + echo "$OUTPUT" + if echo "$OUTPUT" | grep -q 'no vulnerable packages'; then + echo "No vulnerable packages found" + else + if echo "$OUTPUT" | grep -q 'vulnerable'; then + echo "Vulnerable packages found" + exit 1 + fi + fi + + - name: Build + run: msbuild Elmah.Io.WinUI.sln /t:Build /p:Configuration=Release /p:Version=5.1.${{ github.run_number }}-pre + + - name: Test + run: dotnet test --no-build --configuration Release --verbosity normal + + - name: Pack + run: dotnet pack --configuration Release src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj /p:Version=5.1.${{ github.run_number }}-pre + + - name: Install dotnet-validate + run: dotnet tool install --global dotnet-validate --version 0.0.1-preview.304 + + - name: Validate + run: dotnet-validate package local src/Elmah.Io.WinUI/bin/Release/Elmah.Io.WinUI.5.1.${{ github.run_number }}-pre.nupkg + + - name: Push to nuget.org + run: dotnet nuget push src/Elmah.Io.WinUI/bin/Release/Elmah.Io.WinUI.5.1.${{ github.run_number }}-pre.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json + if: ${{ github.event_name == 'push' }} diff --git a/Elmah.Io.WinUI.sln b/Elmah.Io.WinUI.sln new file mode 100644 index 0000000..e151aab --- /dev/null +++ b/Elmah.Io.WinUI.sln @@ -0,0 +1,98 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34916.146 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EE52BC71-E006-4D4F-8353-C4A4BBDBF255}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{9A2AC3AB-5D26-4BDE-BD9C-F6E01A8600DE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{37464AD4-C968-41A3-A19E-BD34AA22F175}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elmah.Io.WinUI", "src\Elmah.Io.WinUI\Elmah.Io.WinUI.csproj", "{BAA888FB-705A-4A56-BBE8-6664326C73E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elmah.Io.WinUI.Sample", "samples\Elmah.Io.WinUI.Sample\Elmah.Io.WinUI.Sample.csproj", "{E8575D6E-3447-4317-8105-0D97EFFA3945}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elmah.Io.WinUI.Test", "test\Elmah.Io.WinUI.Test\Elmah.Io.WinUI.Test.csproj", "{AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|ARM64.Build.0 = Debug|ARM64 + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|x64.ActiveCfg = Debug|x64 + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|x64.Build.0 = Debug|x64 + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Debug|x86.Build.0 = Debug|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|ARM64.ActiveCfg = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|ARM64.Build.0 = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|x64.ActiveCfg = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|x64.Build.0 = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|x86.ActiveCfg = Release|Any CPU + {BAA888FB-705A-4A56-BBE8-6664326C73E2}.Release|x86.Build.0 = Release|Any CPU + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|Any CPU.ActiveCfg = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|Any CPU.Build.0 = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|Any CPU.Deploy.0 = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|ARM64.Build.0 = Debug|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x64.ActiveCfg = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x64.Build.0 = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x64.Deploy.0 = Debug|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x86.ActiveCfg = Debug|x86 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x86.Build.0 = Debug|x86 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Debug|x86.Deploy.0 = Debug|x86 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|Any CPU.ActiveCfg = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|Any CPU.Build.0 = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|Any CPU.Deploy.0 = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|ARM64.ActiveCfg = Release|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|ARM64.Build.0 = Release|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|ARM64.Deploy.0 = Release|ARM64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x64.ActiveCfg = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x64.Build.0 = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x64.Deploy.0 = Release|x64 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x86.ActiveCfg = Release|x86 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x86.Build.0 = Release|x86 + {E8575D6E-3447-4317-8105-0D97EFFA3945}.Release|x86.Deploy.0 = Release|x86 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|Any CPU.ActiveCfg = Debug|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|Any CPU.Build.0 = Debug|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|ARM64.Build.0 = Debug|ARM64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|x64.ActiveCfg = Debug|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|x64.Build.0 = Debug|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|x86.ActiveCfg = Debug|x86 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Debug|x86.Build.0 = Debug|x86 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|Any CPU.ActiveCfg = Release|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|Any CPU.Build.0 = Release|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|ARM64.ActiveCfg = Release|ARM64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|ARM64.Build.0 = Release|ARM64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|x64.ActiveCfg = Release|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|x64.Build.0 = Release|x64 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|x86.ActiveCfg = Release|x86 + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BAA888FB-705A-4A56-BBE8-6664326C73E2} = {EE52BC71-E006-4D4F-8353-C4A4BBDBF255} + {E8575D6E-3447-4317-8105-0D97EFFA3945} = {9A2AC3AB-5D26-4BDE-BD9C-F6E01A8600DE} + {AC9D6790-774D-4447-9B55-EF3DBAF9E1AD} = {37464AD4-C968-41A3-A19E-BD34AA22F175} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {74377C86-B599-4978-9556-F5332234CCC4} + EndGlobalSection +EndGlobal diff --git a/samples/Elmah.Io.WinUI.Sample/App.xaml b/samples/Elmah.Io.WinUI.Sample/App.xaml new file mode 100644 index 0000000..d742714 --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/App.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/samples/Elmah.Io.WinUI.Sample/App.xaml.cs b/samples/Elmah.Io.WinUI.Sample/App.xaml.cs new file mode 100644 index 0000000..9ece89b --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/App.xaml.cs @@ -0,0 +1,52 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Microsoft.UI.Xaml.Shapes; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Elmah.Io.WinUI.Sample +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + ElmahIoWinUI.Init(new ElmahIoWinUIOptions("API_KEY", new Guid("LOG_ID"))); + + this.InitializeComponent(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + m_window = new MainWindow(); + m_window.Activate(); + } + + private Window m_window; + } +} diff --git a/samples/Elmah.Io.WinUI.Sample/Assets/LockScreenLogo.scale-200.png b/samples/Elmah.Io.WinUI.Sample/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..7440f0d4bf7c7e26e4e36328738c68e624ee851e GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FqV6|IEGZ*x-#9g>~Mkr+x6^F zy~CDX2QIMs&Gcs3RnRBoxBA!*(Mfw0KTCYuYk0WlEIV>qBmPl! zq4ukrvfADX@#p8fbLY(H47N+k`FZ(FZh?cDro7>{8mkBO3>^oaIx`3!Jl)Qq)HI!+ z(S=1{o~eT)&W^=Ea8C`-17(Jv5(nHFJ{dOjGdxLVkY_y6&S1whfuFI4MM0kF0f&cO zPDVpV%nz;Id$>+0Ga5e9625-JcI)oq=#Pa3p^>8BB}21BUw@eN!-6@w%X+^`+Vn?! zryu|3T>kVWNBYyBc=7Y6H#s1Ah!OI_nezW zXTqOdkv2Az6KKBV=$yHdF^R3Fqw(TZEoNSZX>reXJ#bwX42%f|Pgg&ebxsLQ010xn AssI20 literal 0 HcmV?d00001 diff --git a/samples/Elmah.Io.WinUI.Sample/Assets/SplashScreen.scale-200.png b/samples/Elmah.Io.WinUI.Sample/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..32f486a86792a5e34cd9a8261b394c49b48f86be GIT binary patch literal 5372 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 literal 0 HcmV?d00001 diff --git a/samples/Elmah.Io.WinUI.Sample/Assets/Square44x44Logo.scale-200.png b/samples/Elmah.Io.WinUI.Sample/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..f713bba67f551ef91020b75716a4dc8ebd744b1c GIT binary patch literal 637 zcmeAS@N?(olHy`uVBq!ia0vp^5g^RL1|$oo8kjIJFu8cTIEGZ*dUI*J;2{SImxtDO zm%3!R$UazoY}x{$j0P5ABYXWr(l=jxJ6ps1W{tV=^>{Dl><3nv3A}sm=EZ)#l3`NR zpZda3^rNox*D1%NC98Z~L*6zipLw~Gxn&(Y-;KmJ+aR6eLabU-L#y8HW%7P-E_-VlLqIabbHPHKT*)fT@9iWJ7iWgOT9%0}Lrj>lztPxWq6sPw3pi z#-<=#$jjrP_DD*i!RLsn0mIA=>4~N)IMYWIf=j%-zuKCdMG%tHYot70D1| zvWa0wMhauW#S>1CnI_;>!1Q3zMA17@DOVq{MQ+{U7^a&yA+%dMCG;WNPV0i;w$tu; zX^b}UKziPM)(<;)ruW;-`)bBN+rQNM*Zs_>?n$|FVFo-e*PZb*@U7VAd+tHb4e?=Blc~}S6K)wL}r*Gf`BM#QB z+y>N$mCswb4d{^{S9v_!eQj4fTRMOwOCi?lSk9%<=vAz}jM-*PQtH@Odn1LZcd^j#o> hW$4xn+CT+ep9lJ{OAO?njobhL002ovPDHLkV1nYebbkN< literal 0 HcmV?d00001 diff --git a/samples/Elmah.Io.WinUI.Sample/Assets/StoreLogo.png b/samples/Elmah.Io.WinUI.Sample/Assets/StoreLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..a4586f26bdf7841cad10f39cdffe2aca3af252c1 GIT binary patch literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fF!p=8IEGZ*dUM0H=rDtTTVkd2 z(%lbKn@VS_lUaADVB&;Z6F#LM+mPsa?e>FnHo;HND^!P`-lX%BH~FOg%y&x+t*x!? zg$#_1A1kgsSvO(fw`bOmo;lrJX8byO1j^gf7qohR%mmt z@L)WX;>gqgK|tWJvQ5j;4;=gt4HXVKSMYRv5RhY5vS~TqfK_NAP*r{h!!g^BZ;w4r z7CGdsai)y;fJQc`7{Zc2b==h%o`Op$|bg6a&nL{*m7-=0>k4M4-PXlU;G-?%*(*g>iFt^ U$m#7DfHB12>FVdQ&MBb@0G`#n8vpc0sq%A~kJcD9FY~qQRMt?ZR3YyDZt}Od;|mgpc{2dv9AHF){kXU%k({ z=Y8JidEayHTkG@twPZ|U3_^%3ct-OgLSiFAqDN!|tbCX@c@?4P`2x*TMK!+Q4b?k0 ziW7!!KF6dPWcF<%I|iznM~`QJ_V7sHGV_D`dhgpA9Vd@&X}ErK+j~_rdv;Bp?OA@a zFXOk7eWOJe5NcK;6h$FaM&7JxNc#-@QTwzW6x#d_zmQNkz5) zPI;kh;3d;5UCJU+9a(cOxX(|edWoOiAEdGU#kPJ&xnc2||3vDbuhBCkj-pb0as$Zl z5;}4n=**n6(1g`JEtSy;SG6X;#-F~Oz3lESG2b5`j@wAwY4Yp<=4Xeb>iH=6aicF?DxD&q{`!&}ct zBI)aycwuobQAf&678Uf+Mmh-@9RUhyH~>?w0dixO0#jZjEc9R^=5NZw=|a(kcB?9^ zfnTiEFXp-q#B;Tn>(O%$A*ud^Rg&eVH6Y_5Y%!E39RR&s?XpG`gKwU!6FE1 z7X)DC7)*(5g}lh`4`{i~DZcWupZI`K)_4P)VE{@gc7@Xsd^86zl~_mOYH?I4!aGeX z^E(_=L6?PgveDQ+r%P@UISEXrkn`LHJZ##+!-anV>6h)IkKp;E@p8+3&(5%kS2)ld*J*rJccZM0iyaAx7+F~GW1UWFK&3X$PE1^}NH zgAG9ck5K!{07OwU@j@Do>TbH=CDEo#4m0cEyAuXy_<&jlzJVcKweSJ5 z&=q~iIn18$w8yb=rmEmHxVEUA^?RwnB?6Qlp1os8@*dWTGL2bhzZ!s*xqScR?EPL` zo(JwNdKUUYy7GtvZ3asXm)cgFvCx9EmAi;|w=a0iGiv%%VYKh`P0Wma4y`Xyx|T~( zAmfGbgbEEC7)j8b@WA@+5W3a61HJXC1dX@6_T|Czk0I0zBk%tnW~()VWITGI!`$c< gARL?UBrYYkwoDw4eo*CrzXGTrZ@;GF>596)00d&n@&Et; literal 0 HcmV?d00001 diff --git a/samples/Elmah.Io.WinUI.Sample/Elmah.Io.WinUI.Sample.csproj b/samples/Elmah.Io.WinUI.Sample/Elmah.Io.WinUI.Sample.csproj new file mode 100644 index 0000000..cebb064 --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/Elmah.Io.WinUI.Sample.csproj @@ -0,0 +1,52 @@ + + + WinExe + net8.0-windows10.0.19041.0 + 10.0.17763.0 + Elmah.Io.WinUI.Sample + app.manifest + x86;x64;ARM64 + win-x86;win-x64;win-arm64 + win10-x86;win10-x64;win10-arm64 + win-$(Platform).pubxml + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + \ No newline at end of file diff --git a/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml b/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml new file mode 100644 index 0000000..d82df92 --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml.cs b/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml.cs new file mode 100644 index 0000000..451c655 --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/MainWindow.xaml.cs @@ -0,0 +1,41 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Elmah.Io.WinUI.Sample +{ + /// + /// An empty window that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainWindow : Window + { + public MainWindow() + { + this.InitializeComponent(); + } + + private void myButton_Click(object sender, RoutedEventArgs e) + { + // Comment out to add a breadcrumb before the error + //ElmahIoWinUI.AddBreadcrumb(new Client.Breadcrumb(DateTimeOffset.Now, "Information", "Click", "User clicking error button")); + + myButton.Content = "Clicked"; + + throw new ApplicationException("Oh no"); + } + } +} diff --git a/samples/Elmah.Io.WinUI.Sample/Package.appxmanifest b/samples/Elmah.Io.WinUI.Sample/Package.appxmanifest new file mode 100644 index 0000000..9edf5d9 --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/Package.appxmanifest @@ -0,0 +1,51 @@ + + + + + + + + + + Elmah.Io.WinUI.Sample + thoma + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Elmah.Io.WinUI.Sample/Properties/launchSettings.json b/samples/Elmah.Io.WinUI.Sample/Properties/launchSettings.json new file mode 100644 index 0000000..974009d --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Elmah.Io.WinUI.Sample (Package)": { + "commandName": "MsixPackage" + }, + "Elmah.Io.WinUI.Sample (Unpackaged)": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/samples/Elmah.Io.WinUI.Sample/app.manifest b/samples/Elmah.Io.WinUI.Sample/app.manifest new file mode 100644 index 0000000..c317c3e --- /dev/null +++ b/samples/Elmah.Io.WinUI.Sample/app.manifest @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + PerMonitorV2 + + + \ No newline at end of file diff --git a/src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj b/src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj new file mode 100644 index 0000000..547fb7a --- /dev/null +++ b/src/Elmah.Io.WinUI/Elmah.Io.WinUI.csproj @@ -0,0 +1,40 @@ + + + + net8.0-windows10.0.19041.0 + 10.0.17763.0 + win-x86;win-x64;win-arm64 + x86;x64;ARM64 + enable + 12.0 + + Logging to elmah.io from Windows UI (WinUI) + elmah.io + Elmah.Io.WinUI + Elmah.Io.WinUI + Error;Exception;Reporting;Management;Logging;ELMAH;Diagnostics;Tracing;WinUI + https://secure.gravatar.com/avatar/5c4cb3646528821117abde6d2d5ee22d?s=64 + https://secure.gravatar.com/avatar/5c4cb3646528821117abde6d2d5ee22d?s=64 + icon.png + https://elmah.io + https://github.com/elmahio/Elmah.Io.WinUI + git + Apache-2.0 + false + false + false + true + README.md + true + + true + true + embedded + + + + + + + + diff --git a/src/Elmah.Io.WinUI/ElmahIoWinUI.cs b/src/Elmah.Io.WinUI/ElmahIoWinUI.cs new file mode 100644 index 0000000..9f6bfd1 --- /dev/null +++ b/src/Elmah.Io.WinUI/ElmahIoWinUI.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Elmah.Io.Client; +using System.Linq; +using System.Security.Principal; +using System.Net.Http.Headers; +using Microsoft.UI.Xaml; + +namespace Elmah.Io.WinUI +{ + /// + /// Main class used to interact with the elmah.io API from WinUI. + /// + public static class ElmahIoWinUI + { + internal static readonly string _assemblyVersion = typeof(ElmahIoWinUI).Assembly.GetName().Version?.ToString() ?? "Unknown"; + internal static readonly string _winUiAssemblyVersion = typeof(Application).Assembly.GetName().Version?.ToString() ?? "Unknown"; + +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + private static ElmahIoWinUIOptions _options; + private static IElmahioAPI _logger; + private static List _breadcrumbs; +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + + /// + /// Initialize logging of all uncaught errors to elmah.io. + /// + public static void Init(ElmahIoWinUIOptions options) + { + if (options == null) throw new ArgumentNullException(nameof(options)); +#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one + if (string.IsNullOrWhiteSpace(options.ApiKey)) throw new ArgumentNullException(nameof(options.ApiKey)); +#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one + if (options.LogId == Guid.Empty) throw new ArgumentException(nameof(options.LogId)); + + _options = options; + _breadcrumbs = new List(1 + options.MaximumBreadcrumbs); + _logger = ElmahioAPI.Create(options.ApiKey, new ElmahIoOptions + { + Timeout = new TimeSpan(0, 0, 5), + UserAgent = UserAgent(), + }); + + _logger.Messages.OnMessageFail += (sender, args) => + { + options.OnError?.Invoke(args.Message, args.Error); + }; + + AppDomain.CurrentDomain.UnhandledException += (sender, args) => + Log(args.ExceptionObject as Exception); + + TaskScheduler.UnobservedTaskException += (sender, args) => + Log(args.Exception); + + Application.Current.UnhandledException += (sender, args) => + Log(args.Exception); + } + + /// + /// Log an exception to elmah.io manually. + /// + public static void Log(Exception? exception) + { + var baseException = exception?.GetBaseException(); + var createMessage = new CreateMessage + { + DateTime = DateTime.UtcNow, + Detail = exception?.ToString(), + Type = baseException?.GetType().FullName, + Title = baseException?.Message ?? "An error occurred", + Data = exception?.ToDataList(), + Severity = "Error", + Source = baseException?.Source, + User = WindowsIdentity.GetCurrent().Name, + Hostname = Hostname(), + Breadcrumbs = Breadcrumbs(), + Application = _options.Application, + ServerVariables = + [ + new("User-Agent", $"X-ELMAHIO-APPLICATION; OS=Windows; OSVERSION={Environment.OSVersion.Version}; ENGINE=WinUI"), + ] + }; + + if (_options.OnFilter != null && _options.OnFilter(createMessage)) + { + return; + } + + _options.OnMessage?.Invoke(createMessage); + + try + { + _logger.Messages.Create(_options.LogId.ToString(), createMessage); + } + catch (Exception ex) + { + _options.OnError?.Invoke(createMessage, ex); + } + } + + /// + /// Add a breadcrumb in-memory. Breadcrumbs will be added to errors when logged + /// either automatically or manually. + /// + public static void AddBreadcrumb(Breadcrumb breadcrumb) + { + _breadcrumbs.Add(breadcrumb); + + if (_breadcrumbs.Count >= _options.MaximumBreadcrumbs) + { + var oldest = _breadcrumbs.OrderBy(b => b.DateTime).First(); + _breadcrumbs.Remove(oldest); + } + } + + private static string? Hostname() + { + var machineName = Environment.MachineName; + if (!string.IsNullOrWhiteSpace(machineName)) return machineName; + + return Environment.GetEnvironmentVariable("COMPUTERNAME"); + } + + private static List Breadcrumbs() + { + if (_breadcrumbs == null || _breadcrumbs.Count == 0) return []; + + var utcNow = DateTime.UtcNow; + + // Set default values on properties not set + foreach (var breadcrumb in _breadcrumbs) + { + if (!breadcrumb.DateTime.HasValue) breadcrumb.DateTime = utcNow; + if (string.IsNullOrWhiteSpace(breadcrumb.Severity)) breadcrumb.Severity = "Information"; + if (string.IsNullOrWhiteSpace(breadcrumb.Action)) breadcrumb.Action = "Log"; + } + + var breadcrumbs = _breadcrumbs.OrderByDescending(l => l.DateTime).ToList(); + _breadcrumbs.Clear(); + return breadcrumbs; + } + + private static string UserAgent() + { + return new StringBuilder() + .Append(new ProductInfoHeaderValue(new ProductHeaderValue("Elmah.Io.WinUI", _assemblyVersion)).ToString()) + .Append(' ') + .Append(new ProductInfoHeaderValue(new ProductHeaderValue("Microsoft.WinUI", _winUiAssemblyVersion)).ToString()) + .ToString(); + } + } +} diff --git a/src/Elmah.Io.WinUI/ElmahIoWinUIOptions.cs b/src/Elmah.Io.WinUI/ElmahIoWinUIOptions.cs new file mode 100644 index 0000000..333c62d --- /dev/null +++ b/src/Elmah.Io.WinUI/ElmahIoWinUIOptions.cs @@ -0,0 +1,50 @@ +using Elmah.Io.Client; +using System; + +namespace Elmah.Io.WinUI +{ + /// + /// Options for setting up elmah.io logging from WinUI. + /// + public class ElmahIoWinUIOptions(string apiKey, Guid logId) + { + /// + /// The API key from the elmah.io UI. + /// + public string ApiKey { get; set; } = apiKey; + + /// + /// The id of the log to send messages to. + /// + public Guid LogId { get; set; } = logId; + + /// + /// An application name to put on all error messages. + /// + public string? Application { get; set; } + + /// + /// Register an action to be called before logging an error. Use the OnMessage action to + /// decorate error messages with additional information. + /// + public Action? OnMessage { get; set; } + + /// + /// Register an action to be called if communicating with the elmah.io API fails. + /// You can use this callback to log the error through which ever logging framework + /// you may use. + /// + public Action? OnError { get; set; } + + /// + /// Register an action to filter log messages. Use this to add client-side ignore + /// of some error messages. If the filter action returns true, the error is ignored. + /// + public Func? OnFilter { get; set; } + + /// + /// The maximum number of breadcrumbs to store in-memory. Default = 10. + /// + public int MaximumBreadcrumbs { get; set; } = 10; + } +} diff --git a/src/Elmah.Io.WinUI/images/icon.png b/src/Elmah.Io.WinUI/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..199964104348331076cf34958b34447a3afe7585 GIT binary patch literal 8119 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 literal 0 HcmV?d00001 diff --git a/test/Elmah.Io.WinUI.Test/Elmah.Io.WinUI.Test.csproj b/test/Elmah.Io.WinUI.Test/Elmah.Io.WinUI.Test.csproj new file mode 100644 index 0000000..c2a0967 --- /dev/null +++ b/test/Elmah.Io.WinUI.Test/Elmah.Io.WinUI.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0-windows10.0.19041.0 + enable + enable + x86;x64;ARM64 + win-x86;win-x64;win-arm64 + win10-x86;win10-x64;win10-arm64 + + + + + + + + + + + + + + diff --git a/test/Elmah.Io.WinUI.Test/ElmahIoWinUITest.cs b/test/Elmah.Io.WinUI.Test/ElmahIoWinUITest.cs new file mode 100644 index 0000000..f2849c6 --- /dev/null +++ b/test/Elmah.Io.WinUI.Test/ElmahIoWinUITest.cs @@ -0,0 +1,66 @@ +using Elmah.Io.Client; +using NSubstitute; +using NUnit.Framework; +using System.Reflection; + +namespace Elmah.Io.WinUI.Test +{ + public class ElmahIoWinUITest + { + [Test] + public void Test() + { + // Arrange + var options = new ElmahIoWinUIOptions("hello", Guid.NewGuid()) + { + Application = "MyApp" + }; + + var optionsField = typeof(ElmahIoWinUI).GetField("_options", BindingFlags.Static | BindingFlags.NonPublic); + optionsField?.SetValue(null, options); + + var messagesClient = Substitute.For(); + var elmahIoClient = Substitute.For(); + elmahIoClient.Messages.Returns(messagesClient); + + var loggerField = typeof(ElmahIoWinUI).GetField("_logger", BindingFlags.Static | BindingFlags.NonPublic); + loggerField?.SetValue(null, elmahIoClient); + + var breadcrumbs = new List(); + var breadcrumbsField = typeof(ElmahIoWinUI).GetField("_breadcrumbs", BindingFlags.Static | BindingFlags.NonPublic); + breadcrumbsField?.SetValue(null, breadcrumbs); + + ElmahIoWinUI.AddBreadcrumb(new Breadcrumb + { + DateTime = DateTime.UtcNow, + Action = "Navigation", + Message = "Opening app", + Severity = "Information", + }); + + var ex = new ApplicationException("Oh no"); + + // Act + ElmahIoWinUI.Log(ex); + + // Assert + messagesClient.Received().Create(Arg.Is(s => s == options.LogId.ToString()), Arg.Is(msg => AssertMessage(msg, ex))); + } + + private static bool AssertMessage(CreateMessage msg, ApplicationException ex) + { + if (msg.Title != "Oh no") return false; + if (msg.Breadcrumbs.Count != 1) return false; + var openingBreadcrumb = msg.Breadcrumbs.First(); + if (openingBreadcrumb.Action != "Navigation" || openingBreadcrumb.Severity != "Information" || openingBreadcrumb.Message != "Opening app") return false; + + if (string.IsNullOrWhiteSpace(msg.Detail)) return false; + if (msg.Type != ex.GetType().FullName) return false; + if (msg.Severity != "Error") return false; + if (msg.Source != ex.Source) return false; + if (msg.Application != "MyApp") return false; + + return true; + } + } +}