From 3f7fad7a4fcabf21c43265119d059ddd990752b9 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Fri, 8 Nov 2024 16:48:20 +0000 Subject: [PATCH 01/14] update --- TinyInsights/ApplicationInsightsProvider.cs | 167 ++++++++++++-------- 1 file changed, 100 insertions(+), 67 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 6867416..c833b2b 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -41,7 +41,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if(IsTrackCrashesEnabled) { HandleCrash(e.Exception); } @@ -49,7 +49,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if(IsTrackCrashesEnabled) { HandleCrash((Exception)e.ExceptionObject); } @@ -85,14 +85,14 @@ public void Initialize() { CreateTelemetryClient(); - if (IsInitialized) + if(IsInitialized) { return; } - if (IsAutoTrackPageViewsEnabled) + if(IsAutoTrackPageViewsEnabled) { - if (Application.Current is null) + if(Application.Current is null) { throw new NullReferenceException("Unable to configure `IsAutoTrackPageViewsEnabled` as `Application.Current` is null. You can either set `IsAutoTrackPageViewsEnabled` to false to ignore this issue, or check out this link for a possible reason - https://github.com/dhindrik/TinyInsights.Maui/issues/21"); } @@ -115,14 +115,14 @@ private static void OnAppearing(object? sender, Page e) private TelemetryClient? CreateTelemetryClient() { - if (client is not null) + if(client is not null) { return client; } try { - if (string.IsNullOrWhiteSpace(ConnectionString)) + if(string.IsNullOrWhiteSpace(ConnectionString)) { throw new ArgumentNullException("ConnectionString", "ConnectionString is required to initialize TinyInsights"); } @@ -144,9 +144,38 @@ private static void OnAppearing(object? sender, Page e) client.Context.User.Id = GetUserId(); // Add any global properties, the user has already added - foreach (var property in _globalProperties) + foreach(var property in _globalProperties) { - client.Context.GlobalProperties[property.Key] = property.Value; + switch(property.Key) + { + case "Cloud.RoleName": + client.Context.Cloud.RoleName = property.Value; + break; + + case "Cloud.RoleInstance": + client.Context.Cloud.RoleInstance = property.Value; + break; + + case "Device.OperatingSystem": + client.Context.Device.OperatingSystem = property.Value; + break; + + case "Device.Model": + client.Context.Device.Model = property.Value; + break; + + case "Device.Type": + client.Context.Device.Type = property.Value; + break; + + case "Device.Id": + client.Context.Device.Id = property.Value; + break; + + default: + client.Context.GlobalProperties[property.Key] = property.Value; + break; + } } client.Context.GlobalProperties.TryAdd("Language", CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); @@ -156,12 +185,11 @@ private static void OnAppearing(object? sender, Page e) client.Context.GlobalProperties.TryAdd("OperatingSystemVersion", DeviceInfo.VersionString); client.Context.Session.Id = Guid.NewGuid().ToString(); - return client; } - catch (Exception) + catch(Exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Error creating TelemetryClient"); } @@ -170,53 +198,58 @@ private static void OnAppearing(object? sender, Page e) public void UpsertGlobalProperty(string key, string value) { - if (Client is null) + _globalProperties[key] = value; + + if(Client is null) { return; } - switch (key) + switch(key) { case "Cloud.RoleName": Client.Context.Cloud.RoleName = value; - return; + break; + case "Cloud.RoleInstance": Client.Context.Cloud.RoleInstance = value; - return; + break; case "Device.OperatingSystem": Client.Context.Device.OperatingSystem = value; - return; + break; + case "Device.Model": Client.Context.Device.Model = value; - return; + break; + case "Device.Type": Client.Context.Device.Type = value; - return; + break; + case "Device.Id": Client.Context.Device.Id = value; - return; - } - - _globalProperties[key] = value; - - Client.Context.GlobalProperties[key] = value; + break; + default: + Client.Context.GlobalProperties[key] = value; + break; + } } public void RemoveGlobalProperty(string key) { - if (Client is null) + if(Client is null) { return; } - if (_globalProperties.ContainsKey(key)) + if(_globalProperties.ContainsKey(key)) { _globalProperties.Remove(key); } - if (Client.Context.GlobalProperties.ContainsKey(key)) + if(Client.Context.GlobalProperties.ContainsKey(key)) { Client.Context.GlobalProperties.Remove(key); } @@ -225,7 +258,7 @@ public void RemoveGlobalProperty(string key) public void OverrideAnonymousUserId(string userId) { Preferences.Set(userIdKey, userId); - if (Client is not null) + if(Client is not null) { Client.Context.User.Id = userId; } @@ -248,7 +281,7 @@ public string GenerateNewAnonymousUserId() public void CreateNewSession() { - if (Client is null) + if(Client is null) { return; } @@ -260,26 +293,26 @@ private async Task SendCrashes() { try { - if (Client is null) + if(Client is null) { return; } var crashes = ReadCrashes(); - if (crashes is null || crashes.Count == 0) + if(crashes is null || crashes.Count == 0) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Sending {crashes.Count} crashes"); - foreach (var crash in crashes) + foreach(var crash in crashes) { var ex = crash.GetException(); - if (ex is null) + if(ex is null) { continue; } @@ -297,9 +330,9 @@ private async Task SendCrashes() ResetCrashes(); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error sending crashes. Message: {ex.Message}"); } } @@ -312,7 +345,7 @@ private async Task SendCrashes() var path = Path.Combine(logPath, crashLogFilename); - if (!File.Exists(path)) + if(!File.Exists(path)) { return null; } @@ -321,7 +354,7 @@ private async Task SendCrashes() return string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize>(json); } - catch (Exception ex) + catch(Exception ex) { Trace.WriteLine($"TinyInsights: Error reading crashes. Message: {ex.Message}"); } @@ -333,15 +366,15 @@ private void ResetCrashes() { try { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Reset crashes"); var path = Path.Combine(logPath, crashLogFilename); File.Delete(path); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error clearing crashes. Message: {ex.Message}"); } } @@ -350,7 +383,7 @@ private void HandleCrash(Exception ex) { try { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Handle crashes"); var crashes = ReadCrashes() ?? []; @@ -363,9 +396,9 @@ private void HandleCrash(Exception ex) File.WriteAllText(path, json); } - catch (Exception exception) + catch(Exception exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error handling crashes. Message: {exception.Message}"); } } @@ -374,17 +407,17 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking error {ex.Message}"); properties ??= []; - if (ex.StackTrace is not null) + if(ex.StackTrace is not null) { properties.TryAdd("StackTrace", ex.StackTrace); } @@ -392,9 +425,9 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop Client.TrackException(ex, properties); await Client.FlushAsync(CancellationToken.None); } - catch (Exception exception) + catch(Exception exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking error. Message: {exception.Message}"); } } @@ -403,20 +436,20 @@ public async Task TrackEventAsync(string eventName, Dictionary? { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking event {eventName}"); Client.TrackEvent(eventName, properties); await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking event. Message: {ex.Message}"); } } @@ -425,20 +458,20 @@ public async Task TrackPageViewAsync(string viewName, Dictionary { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: tracking page view {viewName}"); Client.TrackPageView(viewName); await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking page view. Message: {ex.Message}"); } } @@ -447,17 +480,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking dependency {dependencyName}"); var fullUrl = data; - if (data.Contains('?')) + if(data.Contains('?')) { var split = data.Split("?"); data = split[0]; @@ -476,17 +509,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN dependency.Properties.Add("FullUrl", fullUrl); - if (httpMethod is not null) + if(httpMethod is not null) { dependency.Properties.Add("HttpMethod", httpMethod.ToString()); } - if (exception is not null) + if(exception is not null) { dependency.Properties.Add("ExceptionMessage", exception.Message); dependency.Properties.Add("StackTrace", exception.StackTrace); - if (exception.InnerException is not null) + if(exception.InnerException is not null) { dependency.Properties.Add("InnerExceptionMessage", exception.InnerException.Message); dependency.Properties.Add("InnerExceptionStackTrace", exception.InnerException.StackTrace); @@ -496,9 +529,9 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN Client.TrackDependency(dependency); await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking dependency. Message: {ex.Message}"); } } @@ -512,7 +545,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s public async void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if (!IsEnabled(logLevel)) + if(!IsEnabled(logLevel)) { return; } From 9f7a013a68b3fcbd5e1e650dce486f203d15609b Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Mon, 11 Nov 2024 17:34:00 +0000 Subject: [PATCH 02/14] update --- TinyInsights/TinyInsights.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/TinyInsights/TinyInsights.csproj b/TinyInsights/TinyInsights.csproj index 569a435..ffa7ce4 100644 --- a/TinyInsights/TinyInsights.csproj +++ b/TinyInsights/TinyInsights.csproj @@ -27,8 +27,6 @@ - - From 77d3b2058fb2a310037d0ac70729277384988e7e Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Mon, 11 Nov 2024 17:49:39 +0000 Subject: [PATCH 03/14] Fix IsTrackPageViewsEnabled not being used --- TinyInsights/ApplicationInsightsProvider.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index c833b2b..2b679cd 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -73,10 +73,12 @@ void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionE } } #elif NET8_0_OR_GREATER + public ApplicationInsightsProvider() { // Do nothing. The net8.0 target exists for enabling unit testing, not for actual use. } + #endif public static bool IsInitialized { get; private set; } @@ -463,6 +465,11 @@ public async Task TrackPageViewAsync(string viewName, Dictionary return; } + if(!IsTrackPageViewsEnabled) + { + return; + } + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: tracking page view {viewName}"); From 96a6e2b69020b156565b1262fde1087dc4b06679 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Mon, 11 Nov 2024 18:29:34 +0000 Subject: [PATCH 04/14] Add crash handling and reporting enhancements Added `HandleCrashes` and `WriteCrashes` properties to `ApplicationInsightsProvider` for better control over crash handling and reporting. Updated crash handling logic to use `HandleCrashes`. Made `SendCrashes` method public and added `HasCrashed` method. Updated `IInsights` and `IInsightsProvider` interfaces with new methods and properties. Modified `Insights` class to implement new interface methods and added logic to check and send crashes. --- TinyInsights/ApplicationInsightsProvider.cs | 38 ++++++++++++++++--- TinyInsights/IInsights.cs | 8 ++++ TinyInsights/IInsightsProvider.cs | 9 ++++- TinyInsights/Insights.cs | 42 ++++++++++++++++----- 4 files changed, 81 insertions(+), 16 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 2b679cd..0d9b698 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -23,6 +23,8 @@ public class ApplicationInsightsProvider : IInsightsProvider, ILogger public bool IsTrackErrorsEnabled { get; set; } = true; public bool IsTrackCrashesEnabled { get; set; } = true; + public bool HandleCrashes { get; set; } = true; + public bool WriteCrashes { get; set; } = true; public bool IsTrackPageViewsEnabled { get; set; } = true; public bool IsAutoTrackPageViewsEnabled { get; set; } = true; public bool IsTrackEventsEnabled { get; set; } = true; @@ -41,7 +43,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if(IsTrackCrashesEnabled) + if(HandleCrashes) { HandleCrash(e.Exception); } @@ -49,7 +51,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if(IsTrackCrashesEnabled) + if(HandleCrashes) { HandleCrash((Exception)e.ExceptionObject); } @@ -66,7 +68,7 @@ public ApplicationInsightsProvider(MauiWinUIApplication app, string? connectionS void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if (HandleCrashes) { HandleCrash(e.Exception); } @@ -102,7 +104,10 @@ public void Initialize() Application.Current.PageAppearing += weakHandler.Handler; } - Task.Run(SendCrashes); + if(WriteCrashes) + { + Task.Run(SendCrashes); + } IsInitialized = true; } @@ -291,7 +296,7 @@ public void CreateNewSession() Client.Context.Session.Id = Guid.NewGuid().ToString(); } - private async Task SendCrashes() + public async Task SendCrashes() { try { @@ -339,6 +344,29 @@ private async Task SendCrashes() } } + public bool HasCrashed() + { + try + { + var path = Path.Combine(logPath, crashLogFilename); + + if(!File.Exists(path)) + { + return false; + } + + var json = File.ReadAllText(path); + + var crashes = string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize>(json); + + return crashes is null ? false : crashes.Count != 0; + } + catch(Exception) + { + return false; + } + } + private List? ReadCrashes() { try diff --git a/TinyInsights/IInsights.cs b/TinyInsights/IInsights.cs index d26c206..be1e401 100644 --- a/TinyInsights/IInsights.cs +++ b/TinyInsights/IInsights.cs @@ -3,9 +3,11 @@ namespace TinyInsights; public interface IInsights { void AddProvider(IInsightsProvider provider); + IReadOnlyList GetProviders(); void UpsertGlobalProperty(string key, string value); + void RemoveGlobalProperty(string key); Task TrackErrorAsync(Exception ex, Dictionary? properties = null); @@ -13,6 +15,7 @@ public interface IInsights Task TrackPageViewAsync(string viewName, Dictionary? properties = null); Task TrackEventAsync(string eventName, Dictionary? properties = null); + Task TrackErrorAsync(Exception ex, ErrorSeverity severity, Dictionary? properties = null); Task TrackDependencyAsync(string dependencyType, string dependencyName, string data, DateTimeOffset startTime, TimeSpan duration, bool success, int resultCode = 0, Exception? exception = null); @@ -25,5 +28,10 @@ Task TrackDependencyAsync(string dependencyType, string dependencyName, string d void OverrideAnonymousUserId(string userId); void GenerateNewAnonymousUserId(); + void CreateNewSession(); + + bool HasCrashed(); + + Task SendCrashes(); } \ No newline at end of file diff --git a/TinyInsights/IInsightsProvider.cs b/TinyInsights/IInsightsProvider.cs index 9d3ceb6..2cee079 100644 --- a/TinyInsights/IInsightsProvider.cs +++ b/TinyInsights/IInsightsProvider.cs @@ -3,7 +3,8 @@ namespace TinyInsights; public interface IInsightsProvider { bool IsTrackErrorsEnabled { get; set; } - public bool IsTrackCrashesEnabled { get; set; } + bool HandleCrashes { get; set; } + bool WriteCrashes { get; set; } bool IsTrackPageViewsEnabled { get; set; } bool IsAutoTrackPageViewsEnabled { get; set; } bool IsTrackEventsEnabled { get; set; } @@ -12,6 +13,7 @@ public interface IInsightsProvider void Initialize(); void UpsertGlobalProperty(string key, string value); + void RemoveGlobalProperty(string key); Task TrackErrorAsync(Exception ex, Dictionary? properties = null); @@ -27,5 +29,10 @@ public interface IInsightsProvider void OverrideAnonymousUserId(string userId); string GenerateNewAnonymousUserId(); + void CreateNewSession(); + + bool HasCrashed(); + + Task SendCrashes(); } \ No newline at end of file diff --git a/TinyInsights/Insights.cs b/TinyInsights/Insights.cs index d75bbbf..8a7d559 100644 --- a/TinyInsights/Insights.cs +++ b/TinyInsights/Insights.cs @@ -6,7 +6,7 @@ public class Insights : IInsights public void UpsertGlobalProperty(string key, string value) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.UpsertGlobalProperty(key, value); } @@ -14,7 +14,7 @@ public void UpsertGlobalProperty(string key, string value) public void RemoveGlobalProperty(string key) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.RemoveGlobalProperty(key); } @@ -39,14 +39,14 @@ public Task TrackErrorAsync(Exception ex, ErrorSeverity severity, Dictionary(); - if (properties == null) + if(properties == null) { properties = new Dictionary(); } properties.TryAdd(nameof(ErrorSeverity), severity.ToString()); - foreach (var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) { var task = provider.TrackErrorAsync(ex, properties); tasks.Add(task); @@ -61,7 +61,7 @@ public Task TrackPageViewAsync(string viewName, Dictionary? prop { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) { var task = provider.TrackPageViewAsync(viewName, properties); tasks.Add(task); @@ -75,7 +75,7 @@ public Task TrackEventAsync(string eventName, Dictionary? proper { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) { var task = provider.TrackEventAsync(eventName, properties); tasks.Add(task); @@ -97,7 +97,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) { var task = provider.TrackDependencyAsync(dependencyType, dependencyName, data, httpMethod, startTime, duration, success, resultCode, exception); tasks.Add(task); @@ -131,7 +131,7 @@ public Dependency CreateDependencyTracker(string dependencyType, string dependen public void OverrideAnonymousUserId(string userId) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.OverrideAnonymousUserId(userId); } @@ -139,7 +139,7 @@ public void OverrideAnonymousUserId(string userId) public void GenerateNewAnonymousUserId() { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.GenerateNewAnonymousUserId(); } @@ -147,9 +147,31 @@ public void GenerateNewAnonymousUserId() public void CreateNewSession() { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.CreateNewSession(); } } + + public bool HasCrashed() + { + foreach(var provider in insightsProviders) + { + bool hasCrashed = provider.HasCrashed(); + if(hasCrashed) + { + return true; + } + } + + return false; + } + + public async Task SendCrashes() + { + foreach(var provider in insightsProviders) + { + await provider.SendCrashes(); + } + } } \ No newline at end of file From 0254c9a4a059350a87c05153eed12e3dae4b8cd6 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Tue, 12 Nov 2024 08:50:47 +0000 Subject: [PATCH 05/14] Add ResetCrashes method to insights interfaces and classes A new method `ResetCrashes` has been added to the `ApplicationInsightsProvider` class, changing its access modifier from private to public. The `IInsights` and `IInsightsProvider` interfaces have been updated to include the new `ResetCrashes` method. The `Insights` class has been modified to implement the `ResetCrashes` method, which iterates through all `insightsProviders` and calls their `ResetCrashes` method. --- TinyInsights/ApplicationInsightsProvider.cs | 2 +- TinyInsights/IInsights.cs | 2 ++ TinyInsights/IInsightsProvider.cs | 2 ++ TinyInsights/Insights.cs | 8 ++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 0d9b698..c62e891 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -392,7 +392,7 @@ public bool HasCrashed() return null; } - private void ResetCrashes() + public void ResetCrashes() { try { diff --git a/TinyInsights/IInsights.cs b/TinyInsights/IInsights.cs index be1e401..a35ccea 100644 --- a/TinyInsights/IInsights.cs +++ b/TinyInsights/IInsights.cs @@ -34,4 +34,6 @@ Task TrackDependencyAsync(string dependencyType, string dependencyName, string d bool HasCrashed(); Task SendCrashes(); + + void ResetCrashes(); } \ No newline at end of file diff --git a/TinyInsights/IInsightsProvider.cs b/TinyInsights/IInsightsProvider.cs index 2cee079..6c9f5fe 100644 --- a/TinyInsights/IInsightsProvider.cs +++ b/TinyInsights/IInsightsProvider.cs @@ -35,4 +35,6 @@ public interface IInsightsProvider bool HasCrashed(); Task SendCrashes(); + + void ResetCrashes(); } \ No newline at end of file diff --git a/TinyInsights/Insights.cs b/TinyInsights/Insights.cs index 8a7d559..2bb31ad 100644 --- a/TinyInsights/Insights.cs +++ b/TinyInsights/Insights.cs @@ -174,4 +174,12 @@ public async Task SendCrashes() await provider.SendCrashes(); } } + + public void ResetCrashes() + { + foreach(var provider in insightsProviders) + { + provider.ResetCrashes(); + } + } } \ No newline at end of file From 0a5890cfb7406ea1e8d6d498d79e12f2faf29511 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Fri, 8 Nov 2024 16:48:20 +0000 Subject: [PATCH 06/14] update --- TinyInsights/ApplicationInsightsProvider.cs | 167 ++++++++++++-------- 1 file changed, 100 insertions(+), 67 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index b3989a1..61dbc9e 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -43,7 +43,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if(IsTrackCrashesEnabled) { HandleCrash(e.Exception); } @@ -51,7 +51,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if(IsTrackCrashesEnabled) { HandleCrash((Exception)e.ExceptionObject); } @@ -87,14 +87,14 @@ public void Initialize() { CreateTelemetryClient(); - if (IsInitialized) + if(IsInitialized) { return; } - if (IsAutoTrackPageViewsEnabled) + if(IsAutoTrackPageViewsEnabled) { - if (Application.Current is null) + if(Application.Current is null) { throw new NullReferenceException("Unable to configure `IsAutoTrackPageViewsEnabled` as `Application.Current` is null. You can either set `IsAutoTrackPageViewsEnabled` to false to ignore this issue, or check out this link for a possible reason - https://github.com/dhindrik/TinyInsights.Maui/issues/21"); } @@ -130,14 +130,14 @@ private static void OnDisappearing(object? sender, Page e) private TelemetryClient? CreateTelemetryClient() { - if (client is not null) + if(client is not null) { return client; } try { - if (string.IsNullOrWhiteSpace(ConnectionString)) + if(string.IsNullOrWhiteSpace(ConnectionString)) { throw new ArgumentNullException("ConnectionString", "ConnectionString is required to initialize TinyInsights"); } @@ -161,9 +161,38 @@ private static void OnDisappearing(object? sender, Page e) client.Context.Component.Version = AppInfo.VersionString; // Add any global properties, the user has already added - foreach (var property in _globalProperties) + foreach(var property in _globalProperties) { - client.Context.GlobalProperties[property.Key] = property.Value; + switch(property.Key) + { + case "Cloud.RoleName": + client.Context.Cloud.RoleName = property.Value; + break; + + case "Cloud.RoleInstance": + client.Context.Cloud.RoleInstance = property.Value; + break; + + case "Device.OperatingSystem": + client.Context.Device.OperatingSystem = property.Value; + break; + + case "Device.Model": + client.Context.Device.Model = property.Value; + break; + + case "Device.Type": + client.Context.Device.Type = property.Value; + break; + + case "Device.Id": + client.Context.Device.Id = property.Value; + break; + + default: + client.Context.GlobalProperties[property.Key] = property.Value; + break; + } } client.Context.GlobalProperties.TryAdd("Language", CultureInfo.CurrentUICulture.TwoLetterISOLanguageName); @@ -173,12 +202,11 @@ private static void OnDisappearing(object? sender, Page e) client.Context.GlobalProperties.TryAdd("OperatingSystemVersion", DeviceInfo.VersionString); client.Context.Session.Id = Guid.NewGuid().ToString(); - return client; } - catch (Exception) + catch(Exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Error creating TelemetryClient"); } @@ -187,53 +215,58 @@ private static void OnDisappearing(object? sender, Page e) public void UpsertGlobalProperty(string key, string value) { - if (Client is null) + _globalProperties[key] = value; + + if(Client is null) { return; } - switch (key) + switch(key) { case "Cloud.RoleName": Client.Context.Cloud.RoleName = value; - return; + break; + case "Cloud.RoleInstance": Client.Context.Cloud.RoleInstance = value; - return; + break; case "Device.OperatingSystem": Client.Context.Device.OperatingSystem = value; - return; + break; + case "Device.Model": Client.Context.Device.Model = value; - return; + break; + case "Device.Type": Client.Context.Device.Type = value; - return; + break; + case "Device.Id": Client.Context.Device.Id = value; - return; - } - - _globalProperties[key] = value; - - Client.Context.GlobalProperties[key] = value; + break; + default: + Client.Context.GlobalProperties[key] = value; + break; + } } public void RemoveGlobalProperty(string key) { - if (Client is null) + if(Client is null) { return; } - if (_globalProperties.ContainsKey(key)) + if(_globalProperties.ContainsKey(key)) { _globalProperties.Remove(key); } - if (Client.Context.GlobalProperties.ContainsKey(key)) + if(Client.Context.GlobalProperties.ContainsKey(key)) { Client.Context.GlobalProperties.Remove(key); } @@ -242,7 +275,7 @@ public void RemoveGlobalProperty(string key) public void OverrideAnonymousUserId(string userId) { Preferences.Set(userIdKey, userId); - if (Client is not null) + if(Client is not null) { Client.Context.User.Id = userId; } @@ -265,7 +298,7 @@ public string GenerateNewAnonymousUserId() public void CreateNewSession() { - if (Client is null) + if(Client is null) { return; } @@ -277,26 +310,26 @@ private async Task SendCrashes() { try { - if (Client is null) + if(Client is null) { return; } var crashes = ReadCrashes(); - if (crashes is null || crashes.Count == 0) + if(crashes is null || crashes.Count == 0) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Sending {crashes.Count} crashes"); - foreach (var crash in crashes) + foreach(var crash in crashes) { var ex = crash.GetException(); - if (ex is null) + if(ex is null) { continue; } @@ -314,9 +347,9 @@ private async Task SendCrashes() ResetCrashes(); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error sending crashes. Message: {ex.Message}"); } } @@ -329,7 +362,7 @@ private async Task SendCrashes() var path = Path.Combine(logPath, crashLogFilename); - if (!File.Exists(path)) + if(!File.Exists(path)) { return null; } @@ -338,7 +371,7 @@ private async Task SendCrashes() return string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize>(json); } - catch (Exception ex) + catch(Exception ex) { Trace.WriteLine($"TinyInsights: Error reading crashes. Message: {ex.Message}"); } @@ -350,15 +383,15 @@ private void ResetCrashes() { try { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Reset crashes"); var path = Path.Combine(logPath, crashLogFilename); File.Delete(path); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error clearing crashes. Message: {ex.Message}"); } } @@ -367,7 +400,7 @@ private void HandleCrash(Exception ex) { try { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine("TinyInsights: Handle crashes"); var crashes = ReadCrashes() ?? []; @@ -380,9 +413,9 @@ private void HandleCrash(Exception ex) File.WriteAllText(path, json); } - catch (Exception exception) + catch(Exception exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error handling crashes. Message: {exception.Message}"); } } @@ -391,17 +424,17 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking error {ex.Message}"); properties ??= []; - if (ex.StackTrace is not null) + if(ex.StackTrace is not null) { properties.TryAdd("StackTrace", ex.StackTrace); } @@ -409,9 +442,9 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop Client.TrackException(ex, properties); await Client.FlushAsync(CancellationToken.None); } - catch (Exception exception) + catch(Exception exception) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking error. Message: {exception.Message}"); } } @@ -420,20 +453,20 @@ public async Task TrackEventAsync(string eventName, Dictionary? { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking event {eventName}"); Client.TrackEvent(eventName, properties); await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking event. Message: {ex.Message}"); } } @@ -442,12 +475,12 @@ public async Task TrackPageViewAsync(string viewName, Dictionary { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: tracking page view {viewName}"); var pageView = new PageViewTelemetry(viewName) @@ -472,9 +505,9 @@ public async Task TrackPageViewAsync(string viewName, Dictionary await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking page view. Message: {ex.Message}"); } } @@ -483,17 +516,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN { try { - if (Client is null) + if(Client is null) { return; } - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking dependency {dependencyName}"); var fullUrl = data; - if (data.Contains('?')) + if(data.Contains('?')) { var split = data.Split("?"); data = split[0]; @@ -512,17 +545,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN dependency.Properties.Add("FullUrl", fullUrl); - if (httpMethod is not null) + if(httpMethod is not null) { dependency.Properties.Add("HttpMethod", httpMethod.ToString()); } - if (exception is not null) + if(exception is not null) { dependency.Properties.Add("ExceptionMessage", exception.Message); dependency.Properties.Add("StackTrace", exception.StackTrace); - if (exception.InnerException is not null) + if(exception.InnerException is not null) { dependency.Properties.Add("InnerExceptionMessage", exception.InnerException.Message); dependency.Properties.Add("InnerExceptionStackTrace", exception.InnerException.StackTrace); @@ -532,9 +565,9 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN Client.TrackDependency(dependency); await Client.FlushAsync(CancellationToken.None); } - catch (Exception ex) + catch(Exception ex) { - if (EnableConsoleLogging) + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking dependency. Message: {ex.Message}"); } } @@ -548,7 +581,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s public async void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if (!IsEnabled(logLevel)) + if(!IsEnabled(logLevel)) { return; } From ed826d88eb26ac82b8b199667e4b1c4574ecc66b Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Mon, 11 Nov 2024 17:49:39 +0000 Subject: [PATCH 07/14] Fix IsTrackPageViewsEnabled not being used --- TinyInsights/ApplicationInsightsProvider.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 61dbc9e..721b7a5 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -75,10 +75,12 @@ void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionE } } #elif NET8_0_OR_GREATER + public ApplicationInsightsProvider() { // Do nothing. The net8.0 target exists for enabling unit testing, not for actual use. } + #endif public static bool IsInitialized { get; private set; } @@ -480,6 +482,11 @@ public async Task TrackPageViewAsync(string viewName, Dictionary return; } + if(!IsTrackPageViewsEnabled) + { + return; + } + if(EnableConsoleLogging) Console.WriteLine($"TinyInsights: tracking page view {viewName}"); From 1acdce512563755735643cb571e2400c17bd3e98 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Mon, 11 Nov 2024 18:29:34 +0000 Subject: [PATCH 08/14] Add crash handling and reporting enhancements Added `HandleCrashes` and `WriteCrashes` properties to `ApplicationInsightsProvider` for better control over crash handling and reporting. Updated crash handling logic to use `HandleCrashes`. Made `SendCrashes` method public and added `HasCrashed` method. Updated `IInsights` and `IInsightsProvider` interfaces with new methods and properties. Modified `Insights` class to implement new interface methods and added logic to check and send crashes. --- TinyInsights/ApplicationInsightsProvider.cs | 38 ++++++++++++++++--- TinyInsights/IInsights.cs | 8 ++++ TinyInsights/IInsightsProvider.cs | 9 ++++- TinyInsights/Insights.cs | 42 ++++++++++++++++----- 4 files changed, 81 insertions(+), 16 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 721b7a5..68e6320 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -23,6 +23,8 @@ public class ApplicationInsightsProvider : IInsightsProvider, ILogger public bool IsTrackErrorsEnabled { get; set; } = true; public bool IsTrackCrashesEnabled { get; set; } = true; + public bool HandleCrashes { get; set; } = true; + public bool WriteCrashes { get; set; } = true; public bool IsTrackPageViewsEnabled { get; set; } = true; public bool IsAutoTrackPageViewsEnabled { get; set; } = true; public bool IsTrackEventsEnabled { get; set; } = true; @@ -43,7 +45,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if(IsTrackCrashesEnabled) + if(HandleCrashes) { HandleCrash(e.Exception); } @@ -51,7 +53,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if(IsTrackCrashesEnabled) + if(HandleCrashes) { HandleCrash((Exception)e.ExceptionObject); } @@ -68,7 +70,7 @@ public ApplicationInsightsProvider(MauiWinUIApplication app, string? connectionS void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - if (IsTrackCrashesEnabled) + if (HandleCrashes) { HandleCrash(e.Exception); } @@ -108,7 +110,10 @@ public void Initialize() Application.Current.PageDisappearing += weakOnDisappearingHandler.Handler; } - Task.Run(SendCrashes); + if(WriteCrashes) + { + Task.Run(SendCrashes); + } IsInitialized = true; } @@ -308,7 +313,7 @@ public void CreateNewSession() Client.Context.Session.Id = Guid.NewGuid().ToString(); } - private async Task SendCrashes() + public async Task SendCrashes() { try { @@ -356,6 +361,29 @@ private async Task SendCrashes() } } + public bool HasCrashed() + { + try + { + var path = Path.Combine(logPath, crashLogFilename); + + if(!File.Exists(path)) + { + return false; + } + + var json = File.ReadAllText(path); + + var crashes = string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize>(json); + + return crashes is null ? false : crashes.Count != 0; + } + catch(Exception) + { + return false; + } + } + private List? ReadCrashes() { try diff --git a/TinyInsights/IInsights.cs b/TinyInsights/IInsights.cs index d024d4f..e2bff52 100644 --- a/TinyInsights/IInsights.cs +++ b/TinyInsights/IInsights.cs @@ -3,9 +3,11 @@ namespace TinyInsights; public interface IInsights { void AddProvider(IInsightsProvider provider); + IReadOnlyList GetProviders(); void UpsertGlobalProperty(string key, string value); + void RemoveGlobalProperty(string key); Task TrackErrorAsync(Exception ex, Dictionary? properties = null); @@ -13,6 +15,7 @@ public interface IInsights Task TrackPageViewAsync(string viewName, Dictionary? properties = null, TimeSpan? duration = null); Task TrackEventAsync(string eventName, Dictionary? properties = null); + Task TrackErrorAsync(Exception ex, ErrorSeverity severity, Dictionary? properties = null); Task TrackDependencyAsync(string dependencyType, string dependencyName, string data, DateTimeOffset startTime, TimeSpan duration, bool success, int resultCode = 0, Exception? exception = null); @@ -25,5 +28,10 @@ Task TrackDependencyAsync(string dependencyType, string dependencyName, string d void OverrideAnonymousUserId(string userId); void GenerateNewAnonymousUserId(); + void CreateNewSession(); + + bool HasCrashed(); + + Task SendCrashes(); } \ No newline at end of file diff --git a/TinyInsights/IInsightsProvider.cs b/TinyInsights/IInsightsProvider.cs index 7e8d1e3..8d01e79 100644 --- a/TinyInsights/IInsightsProvider.cs +++ b/TinyInsights/IInsightsProvider.cs @@ -3,7 +3,8 @@ namespace TinyInsights; public interface IInsightsProvider { bool IsTrackErrorsEnabled { get; set; } - public bool IsTrackCrashesEnabled { get; set; } + bool HandleCrashes { get; set; } + bool WriteCrashes { get; set; } bool IsTrackPageViewsEnabled { get; set; } bool IsAutoTrackPageViewsEnabled { get; set; } bool IsTrackEventsEnabled { get; set; } @@ -14,6 +15,7 @@ public interface IInsightsProvider void Initialize(); void UpsertGlobalProperty(string key, string value); + void RemoveGlobalProperty(string key); Task TrackErrorAsync(Exception ex, Dictionary? properties = null); @@ -29,5 +31,10 @@ public interface IInsightsProvider void OverrideAnonymousUserId(string userId); string GenerateNewAnonymousUserId(); + void CreateNewSession(); + + bool HasCrashed(); + + Task SendCrashes(); } \ No newline at end of file diff --git a/TinyInsights/Insights.cs b/TinyInsights/Insights.cs index 22e2c20..d84508b 100644 --- a/TinyInsights/Insights.cs +++ b/TinyInsights/Insights.cs @@ -6,7 +6,7 @@ public class Insights : IInsights public void UpsertGlobalProperty(string key, string value) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.UpsertGlobalProperty(key, value); } @@ -14,7 +14,7 @@ public void UpsertGlobalProperty(string key, string value) public void RemoveGlobalProperty(string key) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.RemoveGlobalProperty(key); } @@ -39,14 +39,14 @@ public Task TrackErrorAsync(Exception ex, ErrorSeverity severity, Dictionary(); - if (properties == null) + if(properties == null) { properties = new Dictionary(); } properties.TryAdd(nameof(ErrorSeverity), severity.ToString()); - foreach (var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) { var task = provider.TrackErrorAsync(ex, properties); tasks.Add(task); @@ -61,7 +61,7 @@ public Task TrackPageViewAsync(string viewName, Dictionary? prop { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) { var task = provider.TrackPageViewAsync(viewName, properties, duration); tasks.Add(task); @@ -75,7 +75,7 @@ public Task TrackEventAsync(string eventName, Dictionary? proper { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) { var task = provider.TrackEventAsync(eventName, properties); tasks.Add(task); @@ -97,7 +97,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s { var tasks = new List(); - foreach (var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) + foreach(var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) { if (provider.TrackDependencyFilter is not null && !provider.TrackDependencyFilter.Invoke((dependencyType, dependencyName, data, startTime, duration, success, resultCode, exception))) { @@ -136,7 +136,7 @@ public Dependency CreateDependencyTracker(string dependencyType, string dependen public void OverrideAnonymousUserId(string userId) { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.OverrideAnonymousUserId(userId); } @@ -144,7 +144,7 @@ public void OverrideAnonymousUserId(string userId) public void GenerateNewAnonymousUserId() { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.GenerateNewAnonymousUserId(); } @@ -152,9 +152,31 @@ public void GenerateNewAnonymousUserId() public void CreateNewSession() { - foreach (var provider in insightsProviders) + foreach(var provider in insightsProviders) { provider.CreateNewSession(); } } + + public bool HasCrashed() + { + foreach(var provider in insightsProviders) + { + bool hasCrashed = provider.HasCrashed(); + if(hasCrashed) + { + return true; + } + } + + return false; + } + + public async Task SendCrashes() + { + foreach(var provider in insightsProviders) + { + await provider.SendCrashes(); + } + } } \ No newline at end of file From c9e3f8ae9b228138e4f5a8e4ec3a408d623309cd Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Tue, 12 Nov 2024 08:50:47 +0000 Subject: [PATCH 09/14] Add ResetCrashes method to insights interfaces and classes A new method `ResetCrashes` has been added to the `ApplicationInsightsProvider` class, changing its access modifier from private to public. The `IInsights` and `IInsightsProvider` interfaces have been updated to include the new `ResetCrashes` method. The `Insights` class has been modified to implement the `ResetCrashes` method, which iterates through all `insightsProviders` and calls their `ResetCrashes` method. --- TinyInsights/ApplicationInsightsProvider.cs | 2 +- TinyInsights/IInsights.cs | 2 ++ TinyInsights/IInsightsProvider.cs | 2 ++ TinyInsights/Insights.cs | 8 ++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 68e6320..b69c785 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -409,7 +409,7 @@ public bool HasCrashed() return null; } - private void ResetCrashes() + public void ResetCrashes() { try { diff --git a/TinyInsights/IInsights.cs b/TinyInsights/IInsights.cs index e2bff52..5060ed4 100644 --- a/TinyInsights/IInsights.cs +++ b/TinyInsights/IInsights.cs @@ -34,4 +34,6 @@ Task TrackDependencyAsync(string dependencyType, string dependencyName, string d bool HasCrashed(); Task SendCrashes(); + + void ResetCrashes(); } \ No newline at end of file diff --git a/TinyInsights/IInsightsProvider.cs b/TinyInsights/IInsightsProvider.cs index 8d01e79..ab8446f 100644 --- a/TinyInsights/IInsightsProvider.cs +++ b/TinyInsights/IInsightsProvider.cs @@ -37,4 +37,6 @@ public interface IInsightsProvider bool HasCrashed(); Task SendCrashes(); + + void ResetCrashes(); } \ No newline at end of file diff --git a/TinyInsights/Insights.cs b/TinyInsights/Insights.cs index d84508b..f450d07 100644 --- a/TinyInsights/Insights.cs +++ b/TinyInsights/Insights.cs @@ -179,4 +179,12 @@ public async Task SendCrashes() await provider.SendCrashes(); } } + + public void ResetCrashes() + { + foreach(var provider in insightsProviders) + { + provider.ResetCrashes(); + } + } } \ No newline at end of file From 6691298f6745073a10d9a836e069b4acc0857b44 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Wed, 18 Dec 2024 10:02:57 +0000 Subject: [PATCH 10/14] Improve code readability and formatting This commit focuses on enhancing the readability of the codebase by adding spaces after keywords and before parentheses in conditional statements. Key changes include: - Standardizing formatting in the `Initialize` method and various error handling methods. - Improving the clarity of global properties handling and telemetry client creation. - Ensuring consistent spacing in loops and conditionals throughout the code. These adjustments aim to maintain the existing functionality while making the code more maintainable and easier to read. --- TinyInsights/ApplicationInsightsProvider.cs | 115 ++++++++++---------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index b69c785..36c8e65 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -45,7 +45,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if(HandleCrashes) + if (HandleCrashes) { HandleCrash(e.Exception); } @@ -53,7 +53,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if(HandleCrashes) + if (HandleCrashes) { HandleCrash((Exception)e.ExceptionObject); } @@ -77,12 +77,10 @@ void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionE } } #elif NET8_0_OR_GREATER - public ApplicationInsightsProvider() { // Do nothing. The net8.0 target exists for enabling unit testing, not for actual use. } - #endif public static bool IsInitialized { get; private set; } @@ -91,14 +89,14 @@ public void Initialize() { CreateTelemetryClient(); - if(IsInitialized) + if (IsInitialized) { return; } - if(IsAutoTrackPageViewsEnabled) + if (IsAutoTrackPageViewsEnabled) { - if(Application.Current is null) + if (Application.Current is null) { throw new NullReferenceException("Unable to configure `IsAutoTrackPageViewsEnabled` as `Application.Current` is null. You can either set `IsAutoTrackPageViewsEnabled` to false to ignore this issue, or check out this link for a possible reason - https://github.com/dhindrik/TinyInsights.Maui/issues/21"); } @@ -110,10 +108,7 @@ public void Initialize() Application.Current.PageDisappearing += weakOnDisappearingHandler.Handler; } - if(WriteCrashes) - { - Task.Run(SendCrashes); - } + Task.Run(SendCrashes); IsInitialized = true; } @@ -137,14 +132,14 @@ private static void OnDisappearing(object? sender, Page e) private TelemetryClient? CreateTelemetryClient() { - if(client is not null) + if (client is not null) { return client; } try { - if(string.IsNullOrWhiteSpace(ConnectionString)) + if (string.IsNullOrWhiteSpace(ConnectionString)) { throw new ArgumentNullException("ConnectionString", "ConnectionString is required to initialize TinyInsights"); } @@ -168,7 +163,7 @@ private static void OnDisappearing(object? sender, Page e) client.Context.Component.Version = AppInfo.VersionString; // Add any global properties, the user has already added - foreach(var property in _globalProperties) + foreach (var property in _globalProperties) { switch(property.Key) { @@ -211,9 +206,9 @@ private static void OnDisappearing(object? sender, Page e) return client; } - catch(Exception) + catch (Exception) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine("TinyInsights: Error creating TelemetryClient"); } @@ -229,7 +224,7 @@ public void UpsertGlobalProperty(string key, string value) return; } - switch(key) + switch (key) { case "Cloud.RoleName": Client.Context.Cloud.RoleName = value; @@ -263,17 +258,17 @@ public void UpsertGlobalProperty(string key, string value) public void RemoveGlobalProperty(string key) { - if(Client is null) + if (Client is null) { return; } - if(_globalProperties.ContainsKey(key)) + if (_globalProperties.ContainsKey(key)) { _globalProperties.Remove(key); } - if(Client.Context.GlobalProperties.ContainsKey(key)) + if (Client.Context.GlobalProperties.ContainsKey(key)) { Client.Context.GlobalProperties.Remove(key); } @@ -282,7 +277,7 @@ public void RemoveGlobalProperty(string key) public void OverrideAnonymousUserId(string userId) { Preferences.Set(userIdKey, userId); - if(Client is not null) + if (Client is not null) { Client.Context.User.Id = userId; } @@ -305,7 +300,7 @@ public string GenerateNewAnonymousUserId() public void CreateNewSession() { - if(Client is null) + if (Client is null) { return; } @@ -317,26 +312,26 @@ public async Task SendCrashes() { try { - if(Client is null) + if (Client is null) { return; } var crashes = ReadCrashes(); - if(crashes is null || crashes.Count == 0) + if (crashes is null || crashes.Count == 0) { return; } - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Sending {crashes.Count} crashes"); - foreach(var crash in crashes) + foreach (var crash in crashes) { var ex = crash.GetException(); - if(ex is null) + if (ex is null) { continue; } @@ -354,9 +349,9 @@ public async Task SendCrashes() ResetCrashes(); } - catch(Exception ex) + catch (Exception ex) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error sending crashes. Message: {ex.Message}"); } } @@ -392,7 +387,7 @@ public bool HasCrashed() var path = Path.Combine(logPath, crashLogFilename); - if(!File.Exists(path)) + if (!File.Exists(path)) { return null; } @@ -401,7 +396,7 @@ public bool HasCrashed() return string.IsNullOrWhiteSpace(json) ? null : JsonSerializer.Deserialize>(json); } - catch(Exception ex) + catch (Exception ex) { Trace.WriteLine($"TinyInsights: Error reading crashes. Message: {ex.Message}"); } @@ -413,15 +408,15 @@ public void ResetCrashes() { try { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine("TinyInsights: Reset crashes"); var path = Path.Combine(logPath, crashLogFilename); File.Delete(path); } - catch(Exception ex) + catch (Exception ex) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error clearing crashes. Message: {ex.Message}"); } } @@ -430,7 +425,7 @@ private void HandleCrash(Exception ex) { try { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine("TinyInsights: Handle crashes"); var crashes = ReadCrashes() ?? []; @@ -443,9 +438,9 @@ private void HandleCrash(Exception ex) File.WriteAllText(path, json); } - catch(Exception exception) + catch (Exception exception) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error handling crashes. Message: {exception.Message}"); } } @@ -454,17 +449,17 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop { try { - if(Client is null) + if (Client is null) { return; } - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking error {ex.Message}"); properties ??= []; - if(ex.StackTrace is not null) + if (ex.StackTrace is not null) { properties.TryAdd("StackTrace", ex.StackTrace); } @@ -472,9 +467,9 @@ public async Task TrackErrorAsync(Exception ex, Dictionary? prop Client.TrackException(ex, properties); await Client.FlushAsync(CancellationToken.None); } - catch(Exception exception) + catch (Exception exception) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking error. Message: {exception.Message}"); } } @@ -483,20 +478,20 @@ public async Task TrackEventAsync(string eventName, Dictionary? { try { - if(Client is null) + if (Client is null) { return; } - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking event {eventName}"); Client.TrackEvent(eventName, properties); await Client.FlushAsync(CancellationToken.None); } - catch(Exception ex) + catch (Exception ex) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking event. Message: {ex.Message}"); } } @@ -505,17 +500,17 @@ public async Task TrackPageViewAsync(string viewName, Dictionary { try { - if(Client is null) + if (Client is null) { return; } - if(!IsTrackPageViewsEnabled) + if (!IsTrackPageViewsEnabled) { return; } - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: tracking page view {viewName}"); var pageView = new PageViewTelemetry(viewName) @@ -540,9 +535,9 @@ public async Task TrackPageViewAsync(string viewName, Dictionary await Client.FlushAsync(CancellationToken.None); } - catch(Exception ex) + catch (Exception ex) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking page view. Message: {ex.Message}"); } } @@ -551,17 +546,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN { try { - if(Client is null) + if (Client is null) { return; } - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Tracking dependency {dependencyName}"); var fullUrl = data; - if(data.Contains('?')) + if (data.Contains('?')) { var split = data.Split("?"); data = split[0]; @@ -580,17 +575,17 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN dependency.Properties.Add("FullUrl", fullUrl); - if(httpMethod is not null) + if (httpMethod is not null) { dependency.Properties.Add("HttpMethod", httpMethod.ToString()); } - if(exception is not null) + if (exception is not null) { dependency.Properties.Add("ExceptionMessage", exception.Message); dependency.Properties.Add("StackTrace", exception.StackTrace); - if(exception.InnerException is not null) + if (exception.InnerException is not null) { dependency.Properties.Add("InnerExceptionMessage", exception.InnerException.Message); dependency.Properties.Add("InnerExceptionStackTrace", exception.InnerException.StackTrace); @@ -600,9 +595,9 @@ public async Task TrackDependencyAsync(string dependencyType, string dependencyN Client.TrackDependency(dependency); await Client.FlushAsync(CancellationToken.None); } - catch(Exception ex) + catch (Exception ex) { - if(EnableConsoleLogging) + if (EnableConsoleLogging) Console.WriteLine($"TinyInsights: Error tracking dependency. Message: {ex.Message}"); } } @@ -616,7 +611,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s public async void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if(!IsEnabled(logLevel)) + if (!IsEnabled(logLevel)) { return; } From aef554bb4fdabe02c555d5177ac6f3c7997e25be Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Wed, 18 Dec 2024 10:04:27 +0000 Subject: [PATCH 11/14] Improve formatting and readability in Insights.cs Updated `foreach` statements for consistent spacing and enhanced readability. Adjusted null check handling for the `properties` parameter in `TrackErrorAsync` to improve clarity. --- TinyInsights/Insights.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TinyInsights/Insights.cs b/TinyInsights/Insights.cs index f450d07..32da46e 100644 --- a/TinyInsights/Insights.cs +++ b/TinyInsights/Insights.cs @@ -6,7 +6,7 @@ public class Insights : IInsights public void UpsertGlobalProperty(string key, string value) { - foreach(var provider in insightsProviders) + foreach (var provider in insightsProviders) { provider.UpsertGlobalProperty(key, value); } @@ -14,7 +14,7 @@ public void UpsertGlobalProperty(string key, string value) public void RemoveGlobalProperty(string key) { - foreach(var provider in insightsProviders) + foreach (var provider in insightsProviders) { provider.RemoveGlobalProperty(key); } @@ -39,14 +39,14 @@ public Task TrackErrorAsync(Exception ex, ErrorSeverity severity, Dictionary(); - if(properties == null) + if (properties == null) { properties = new Dictionary(); } properties.TryAdd(nameof(ErrorSeverity), severity.ToString()); - foreach(var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) + foreach (var provider in insightsProviders.Where(x => x.IsTrackErrorsEnabled)) { var task = provider.TrackErrorAsync(ex, properties); tasks.Add(task); @@ -61,7 +61,7 @@ public Task TrackPageViewAsync(string viewName, Dictionary? prop { var tasks = new List(); - foreach(var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) + foreach (var provider in insightsProviders.Where(x => x.IsTrackPageViewsEnabled)) { var task = provider.TrackPageViewAsync(viewName, properties, duration); tasks.Add(task); @@ -75,7 +75,7 @@ public Task TrackEventAsync(string eventName, Dictionary? proper { var tasks = new List(); - foreach(var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) + foreach (var provider in insightsProviders.Where(x => x.IsTrackEventsEnabled)) { var task = provider.TrackEventAsync(eventName, properties); tasks.Add(task); @@ -97,7 +97,7 @@ public Task TrackDependencyAsync(string dependencyType, string dependencyName, s { var tasks = new List(); - foreach(var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) + foreach (var provider in insightsProviders.Where(x => x.IsTrackDependencyEnabled)) { if (provider.TrackDependencyFilter is not null && !provider.TrackDependencyFilter.Invoke((dependencyType, dependencyName, data, startTime, duration, success, resultCode, exception))) { @@ -136,7 +136,7 @@ public Dependency CreateDependencyTracker(string dependencyType, string dependen public void OverrideAnonymousUserId(string userId) { - foreach(var provider in insightsProviders) + foreach (var provider in insightsProviders) { provider.OverrideAnonymousUserId(userId); } @@ -144,7 +144,7 @@ public void OverrideAnonymousUserId(string userId) public void GenerateNewAnonymousUserId() { - foreach(var provider in insightsProviders) + foreach (var provider in insightsProviders) { provider.GenerateNewAnonymousUserId(); } @@ -152,7 +152,7 @@ public void GenerateNewAnonymousUserId() public void CreateNewSession() { - foreach(var provider in insightsProviders) + foreach (var provider in insightsProviders) { provider.CreateNewSession(); } From 942aa48d49a676328fd673fb6a9aabdf7f20658d Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Wed, 18 Dec 2024 10:05:52 +0000 Subject: [PATCH 12/14] Refactor crash handling in ApplicationInsightsProvider Removed the `HandleCrashes` property and replaced it with `IsTrackCrashesEnabled` in both the `ApplicationInsightsProvider` class and the `IInsightsProvider` interface. Updated exception handling logic to use `IsTrackCrashesEnabled`, ensuring that crash tracking behavior remains consistent with a default value of `true`. --- TinyInsights/ApplicationInsightsProvider.cs | 8 ++++---- TinyInsights/IInsightsProvider.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 36c8e65..4b0d53e 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -23,7 +23,7 @@ public class ApplicationInsightsProvider : IInsightsProvider, ILogger public bool IsTrackErrorsEnabled { get; set; } = true; public bool IsTrackCrashesEnabled { get; set; } = true; - public bool HandleCrashes { get; set; } = true; + public bool IsTrackCrashesEnabled { get; set; } = true; public bool WriteCrashes { get; set; } = true; public bool IsTrackPageViewsEnabled { get; set; } = true; public bool IsAutoTrackPageViewsEnabled { get; set; } = true; @@ -45,7 +45,7 @@ public ApplicationInsightsProvider(string? connectionString = null) void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if (HandleCrashes) + if (IsTrackCrashesEnabled) { HandleCrash(e.Exception); } @@ -53,7 +53,7 @@ void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExcepti void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - if (HandleCrashes) + if (IsTrackCrashesEnabled) { HandleCrash((Exception)e.ExceptionObject); } @@ -70,7 +70,7 @@ public ApplicationInsightsProvider(MauiWinUIApplication app, string? connectionS void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - if (HandleCrashes) + if (IsTrackCrashesEnabled) { HandleCrash(e.Exception); } diff --git a/TinyInsights/IInsightsProvider.cs b/TinyInsights/IInsightsProvider.cs index ab8446f..9ce42cd 100644 --- a/TinyInsights/IInsightsProvider.cs +++ b/TinyInsights/IInsightsProvider.cs @@ -3,7 +3,7 @@ namespace TinyInsights; public interface IInsightsProvider { bool IsTrackErrorsEnabled { get; set; } - bool HandleCrashes { get; set; } + bool IsTrackCrashesEnabled { get; set; } bool WriteCrashes { get; set; } bool IsTrackPageViewsEnabled { get; set; } bool IsAutoTrackPageViewsEnabled { get; set; } From 8c42868429430567eebcc7e827cbd7b8e47042d1 Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Wed, 18 Dec 2024 10:11:23 +0000 Subject: [PATCH 13/14] Remove duplicate property definition in ApplicationInsightsProvider The property `IsTrackCrashesEnabled` was defined twice in the `ApplicationInsightsProvider` class. The duplicate definition has been removed, streamlining the code and ensuring that the property is only declared once. The remaining definition retains its default value of `true`. --- TinyInsights/ApplicationInsightsProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index 4b0d53e..ea7757a 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -23,7 +23,6 @@ public class ApplicationInsightsProvider : IInsightsProvider, ILogger public bool IsTrackErrorsEnabled { get; set; } = true; public bool IsTrackCrashesEnabled { get; set; } = true; - public bool IsTrackCrashesEnabled { get; set; } = true; public bool WriteCrashes { get; set; } = true; public bool IsTrackPageViewsEnabled { get; set; } = true; public bool IsAutoTrackPageViewsEnabled { get; set; } = true; From 8e017cab397e38a6303f2f97baaecad33022f82f Mon Sep 17 00:00:00 2001 From: Ieuan Walker Date: Wed, 18 Dec 2024 10:44:05 +0000 Subject: [PATCH 14/14] Add conditional execution for crash reporting Modified the code to execute `Task.Run(SendCrashes)` only when the `WriteCrashes` flag is true. This change improves performance by avoiding unnecessary crash reporting when not needed. --- TinyInsights/ApplicationInsightsProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TinyInsights/ApplicationInsightsProvider.cs b/TinyInsights/ApplicationInsightsProvider.cs index ea7757a..2a5f174 100644 --- a/TinyInsights/ApplicationInsightsProvider.cs +++ b/TinyInsights/ApplicationInsightsProvider.cs @@ -107,7 +107,10 @@ public void Initialize() Application.Current.PageDisappearing += weakOnDisappearingHandler.Handler; } - Task.Run(SendCrashes); + if (WriteCrashes) + { + Task.Run(SendCrashes); + } IsInitialized = true; }