1+ // --------------------------------------------------------------------------------------------------------------------
2+ // <copyright file="AppDiagnostics.cs" company="MADE Apps">
3+ // Copyright (c) MADE Apps.
4+ // </copyright>
5+ // <summary>
6+ // Defines a service for managing application wide event logging for exceptions.
7+ // </summary>
8+ // --------------------------------------------------------------------------------------------------------------------
9+
10+ namespace MADE . App . Diagnostics
11+ {
12+ using System . Threading . Tasks ;
13+
14+ using MADE . App . Dependency ;
15+ using MADE . App . Diagnostics . Logging ;
16+
17+ /// <summary>
18+ /// Defines a service for managing application wide event logging for exceptions.
19+ /// </summary>
20+ public class AppDiagnostics : IAppDiagnostics
21+ {
22+ /// <summary>
23+ /// Defines the name of the folder where logs are stored locally in the application.
24+ /// </summary>
25+ public const string LogsFolderName = "Logs" ;
26+
27+ /// <summary>
28+ /// Defines the format for the name of the file where a log is stored locally in the application.
29+ /// </summary>
30+ public const string LogFileNameFormat = "Log-{0:yyyyMMdd}.txt" ;
31+
32+ #if WINDOWS_UWP
33+ private StorageFileEventListener listener ;
34+ #endif
35+
36+ /// <summary>
37+ /// Initializes a new instance of the <see cref="AppDiagnostics"/> class.
38+ /// </summary>
39+ public AppDiagnostics ( )
40+ {
41+ if ( ! SimpleDependencyService . Instance . IsRegistered < IEventLogger > ( ) )
42+ {
43+ SimpleDependencyService . Instance . Register < IEventLogger , EventLogger > ( ) ;
44+ }
45+
46+ this . EventLogger = SimpleDependencyService . Instance . GetInstance < IEventLogger > ( ) ;
47+ }
48+
49+ /// <summary>
50+ /// Initializes a new instance of the <see cref="AppDiagnostics"/> class.
51+ /// </summary>
52+ /// <param name="eventLogger">
53+ /// The instance of the service for logging application event messages.
54+ /// </param>
55+ public AppDiagnostics ( IEventLogger eventLogger )
56+ {
57+ this . EventLogger = eventLogger ;
58+ }
59+
60+ /// <summary>
61+ /// Gets the service for logging application event messages.
62+ /// </summary>
63+ public IEventLogger EventLogger { get ; }
64+
65+ /// <summary>
66+ /// Gets the string path to the file used for capturing application diagnostic messages.
67+ /// </summary>
68+ public string DiagnosticsFilePath => this . EventLogger ? . LogPath ;
69+
70+ /// <summary>
71+ /// Gets a value indicating whether application diagnostic messages are being recorded.
72+ /// </summary>
73+ public bool IsRecordingDiagnostics { get ; private set ; }
74+
75+ /// <summary>
76+ /// Starts tracking and recording the application diagnostic messages.
77+ /// </summary>
78+ /// <returns>
79+ /// An asynchronous operation.
80+ /// </returns>
81+ public async Task StartRecordingDiagnosticsAsync ( )
82+ {
83+ if ( this . IsRecordingDiagnostics )
84+ {
85+ await Task . CompletedTask ;
86+ }
87+
88+ this . IsRecordingDiagnostics = true ;
89+
90+ #if WINDOWS_UWP
91+ this . listener = new StorageFileEventListener ( ) ;
92+ this . listener . EnableEvents ( this . EventLogger as System . Diagnostics . Tracing . EventSource , System . Diagnostics . Tracing . EventLevel . Verbose ) ;
93+ #endif
94+
95+ this . EventLogger . WriteInfo ( "Application diagnostics initialized." ) ;
96+
97+ #if WINDOWS_UWP
98+ Windows . UI . Xaml . Application . Current . UnhandledException += this . OnAppUnhandledException ;
99+ #else
100+ System . AppDomain . CurrentDomain . UnhandledException += this . OnAppUnhandledException ;
101+ #endif
102+ TaskScheduler . UnobservedTaskException += this . OnTaskUnobservedException ;
103+
104+ await Task . CompletedTask ;
105+ }
106+
107+ /// <summary>
108+ /// Stops tracking and recording the application diagnostic messages.
109+ /// </summary>
110+ public void StopRecordingDiagnostics ( )
111+ {
112+ if ( ! this . IsRecordingDiagnostics )
113+ {
114+ return ;
115+ }
116+
117+ #if WINDOWS_UWP
118+ Windows . UI . Xaml . Application . Current . UnhandledException -= this . OnAppUnhandledException ;
119+ #else
120+ System . AppDomain . CurrentDomain . UnhandledException -= this . OnAppUnhandledException ;
121+ #endif
122+ TaskScheduler . UnobservedTaskException -= this . OnTaskUnobservedException ;
123+
124+ this . IsRecordingDiagnostics = false ;
125+ }
126+
127+ private void OnTaskUnobservedException ( object sender , UnobservedTaskExceptionEventArgs args )
128+ {
129+ args . SetObserved ( ) ;
130+
131+ this . EventLogger . WriteCritical (
132+ args . Exception != null
133+ ? $ "An unobserved task exception was thrown. Error: { args . Exception } "
134+ : "An unobserved task exception was thrown. Error: No exception information was available." ) ;
135+ }
136+
137+ #if WINDOWS_UWP
138+ private void OnAppUnhandledException ( object sender , Windows . UI . Xaml . UnhandledExceptionEventArgs args )
139+ {
140+ args . Handled = true ;
141+
142+ this . EventLogger . WriteCritical (
143+ args . Exception != null
144+ ? $ "An unhandled exception was thrown. Error: { args . Exception } "
145+ : "An unhandled exception was thrown. Error: No exception information was available." ) ;
146+ }
147+ #else
148+ private void OnAppUnhandledException ( object sender , System . UnhandledExceptionEventArgs args )
149+ {
150+ if ( args . IsTerminating )
151+ {
152+ this . EventLogger . WriteCritical ( "The application is terminating due to an unhandled exception being thrown." ) ;
153+ }
154+
155+ if ( ! ( args . ExceptionObject is System . Exception ex ) )
156+ {
157+ return ;
158+ }
159+
160+ this . EventLogger . WriteCritical ( $ "An unhandled exception was thrown. Error: { ex } ") ;
161+ }
162+ #endif
163+ }
164+ }
0 commit comments