Skip to content

Commit

Permalink
feat(templates): improve Boilerplate app insights #9105 (#9106)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmoradi authored Nov 5, 2024
1 parent 7561b43 commit 8cbc4d0
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,8 @@
"condition": "(appInsights != true)",
"exclude": [
"src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs",
"src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs"
"src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs",
"src/Client/Boilerplate.Client.Core/Services/AppInsightsJsSdkService.cs"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
//#if (signalr == true)
using Microsoft.AspNetCore.SignalR.Client;
//#endif
//#if (appInsights == true)
using BlazorApplicationInsights.Interfaces;
//#endif

namespace Boilerplate.Client.Core.Components;

Expand All @@ -15,6 +18,9 @@ public partial class AppInitializer : AppComponentBase
//#if (notification == true)
[AutoInject] private IPushNotificationService pushNotificationService = default!;
//#endif
//#if (appInsights == true)
[AutoInject] private IApplicationInsights appInsights = default!;
//#endif
[AutoInject] private Navigator navigator = default!;
[AutoInject] private IJSRuntime jsRuntime = default!;
[AutoInject] private Bit.Butil.Console console = default!;
Expand All @@ -29,7 +35,30 @@ protected override async Task OnInitAsync()
{
AuthenticationManager.AuthenticationStateChanged += AuthenticationStateChanged;

AuthenticationStateChanged(AuthenticationManager.GetAuthenticationStateAsync());
if (InPrerenderSession is false)
{
TelemetryContext.UserAgent = await navigator.GetUserAgent();
TelemetryContext.TimeZone = await jsRuntime.GetTimeZone();
TelemetryContext.Culture = CultureInfo.CurrentCulture.Name;
if (AppPlatform.IsBlazorHybrid is false)
{
TelemetryContext.OS = await jsRuntime.GetBrowserPlatform();
}

//#if (appInsights == true)
await appInsights.AddTelemetryInitializer(new()
{
Data = new()
{
["ai.application.ver"] = TelemetryContext.AppVersion,
["ai.session.id"] = TelemetryContext.AppSessionId,
["ai.device.locale"] = TelemetryContext.Culture
}
});
//#endif

AuthenticationStateChanged(AuthenticationManager.GetAuthenticationStateAsync());
}

if (AppPlatform.IsBlazorHybrid)
{
Expand All @@ -48,19 +77,6 @@ await storageService.GetItem("Culture") ?? // 2- User settings
await base.OnInitAsync();
}

protected override async Task OnAfterFirstRenderAsync()
{
await base.OnAfterFirstRenderAsync();

TelemetryContext.UserAgent = await navigator.GetUserAgent();
TelemetryContext.TimeZone = await jsRuntime.GetTimeZone();
TelemetryContext.Culture = CultureInfo.CurrentCulture.Name;
if (AppPlatform.IsBlazorHybrid is false)
{
TelemetryContext.OS = await jsRuntime.GetBrowserPlatform();
}
}

private async void AuthenticationStateChanged(Task<AuthenticationState> task)
{
try
Expand All @@ -69,23 +85,30 @@ private async void AuthenticationStateChanged(Task<AuthenticationState> task)
TelemetryContext.UserId = user.IsAuthenticated() ? user.GetUserId() : null;
TelemetryContext.UserSessionId = user.IsAuthenticated() ? user.GetSessionId() : null;

using var scope = authLogger.BeginScope(TelemetryContext.ToDictionary());
var data = TelemetryContext.ToDictionary();

//#if (appInsights == true)
if (user.IsAuthenticated())
{
authLogger.LogInformation("Authentication state changed.");
await appInsights.SetAuthenticatedUserContext(user.GetUserId().ToString());
}

//#if (signalr == true)
if (InPrerenderSession is false)
else
{
await ConnectSignalR();
await appInsights.ClearAuthenticatedUserContext();
}
//#endif

//#if (notification == true)
if (InPrerenderSession is false)
using var scope = authLogger.BeginScope(data);
{
await pushNotificationService.RegisterDevice(CurrentCancellationToken);
authLogger.LogInformation("Authentication state changed.");
}

//#if (signalr == true)
await ConnectSignalR();
//#endif

//#if (notification == true)
await pushNotificationService.RegisterDevice(CurrentCancellationToken);
//#endif
}
catch (Exception exp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//#endif
//#if (appInsights == true)
using BlazorApplicationInsights;
using BlazorApplicationInsights.Interfaces;
//#endif
using Boilerplate.Client.Core;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -97,13 +98,10 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle
//#endif

//#if (appInsights == true)
services.Add(ServiceDescriptor.Describe(typeof(IApplicationInsights), typeof(AppInsightsJsSdkService), AppPlatform.IsBrowser ? ServiceLifetime.Singleton : ServiceLifetime.Scoped));
services.AddBlazorApplicationInsights(x =>
{
var connectionString = configuration.Get<ClientCoreSettings>()!.ApplicationInsights?.ConnectionString;
if (string.IsNullOrEmpty(connectionString) is false)
{
x.ConnectionString = connectionString;
}
x.ConnectionString = configuration.Get<ClientCoreSettings>()!.ApplicationInsights?.ConnectionString;
});
//#endif

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using BlazorApplicationInsights;
using BlazorApplicationInsights.Models;
using BlazorApplicationInsights.Interfaces;

namespace Boilerplate.Client.Core.Services;

public partial class AppInsightsJsSdkService : IApplicationInsights
{
private readonly ApplicationInsights applicationInsights = new();
private TaskCompletionSource? appInsightsReady;

[AutoInject] private IJSRuntime jsRuntime = default!;

public async Task AddTelemetryInitializer(TelemetryItem telemetryItem)
{
await EnsureReady();
await applicationInsights.AddTelemetryInitializer(telemetryItem);
}

public async Task ClearAuthenticatedUserContext()
{
await EnsureReady();
await applicationInsights.ClearAuthenticatedUserContext();
}

public async Task<TelemetryContext> Context()
{
await EnsureReady();
return await applicationInsights.Context();
}

public async Task Flush()
{
await EnsureReady();
await applicationInsights.Flush();
}

public CookieMgr GetCookieMgr()
{
if (appInsightsReady?.Task.IsCompleted is false)
throw new InvalidOperationException("app insights is not ready");

return applicationInsights.GetCookieMgr();
}

public void InitJSRuntime(IJSRuntime jSRuntime)
{
applicationInsights.InitJSRuntime(jSRuntime);
}

public async Task SetAuthenticatedUserContext(string authenticatedUserId, string? accountId = null, bool? storeInCookie = null)
{
await EnsureReady();
await applicationInsights.SetAuthenticatedUserContext(authenticatedUserId, accountId, storeInCookie);
}

public async Task StartTrackEvent(string name)
{
await EnsureReady();
await applicationInsights.StartTrackEvent(name);
}

public async Task StartTrackPage(string? name = null)
{
await EnsureReady();
await applicationInsights.StartTrackPage(name);
}

public async Task StopTrackEvent(string name, Dictionary<string, object?>? properties = null, Dictionary<string, decimal>? measurements = null)
{
await EnsureReady();
await applicationInsights.StopTrackEvent(name, properties, measurements);
}

public async Task StopTrackPage(string? name = null, string? url = null, Dictionary<string, object?>? customProperties = null, Dictionary<string, decimal>? measurements = null)
{
await EnsureReady();
await applicationInsights.StopTrackPage(name, url, customProperties, measurements);
}

public async Task TrackDependencyData(DependencyTelemetry dependency)
{
await EnsureReady();
await applicationInsights.TrackDependencyData(dependency);
}

public async Task TrackEvent(EventTelemetry @event)
{
await EnsureReady();
await applicationInsights.TrackEvent(@event);
}

public async Task TrackException(ExceptionTelemetry exception)
{
await EnsureReady();
await applicationInsights.TrackException(exception);
}

public async Task TrackMetric(MetricTelemetry metric)
{
await EnsureReady();
await applicationInsights.TrackMetric(metric);
}

public async Task TrackPageView(PageViewTelemetry? pageView = null)
{
await EnsureReady();
await applicationInsights.TrackPageView(pageView);
}

public async Task TrackPageViewPerformance(PageViewPerformanceTelemetry pageViewPerformance)
{
await EnsureReady();
await applicationInsights.TrackPageViewPerformance(pageViewPerformance);
}

public async Task TrackTrace(TraceTelemetry trace)
{
await EnsureReady();
await applicationInsights.TrackTrace(trace);
}

public async Task UpdateCfg(Config newConfig, bool mergeExisting = true)
{
await EnsureReady();
await applicationInsights.UpdateCfg(newConfig, mergeExisting);
}

private Task EnsureReady()
{
async void CheckForAppInsightsReady()
{
while (true)
{
try
{
var appInsightsVersion = await jsRuntime!.InvokeAsync<int>("eval", "window.appInsights.version");
appInsightsReady.SetResult();
break;
}
catch { await Task.Delay(250); }
}
}

if (appInsightsReady is null)
{
appInsightsReady = new TaskCompletionSource();
CheckForAppInsightsReady();
}

return appInsightsReady!.Task;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//+:cnd:noEmit
using System.Reflection;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;
Expand All @@ -19,9 +20,6 @@ public void Handle(Exception exception,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "")
{
if (IgnoreException(exception))
return;

parameters = TelemetryContext.ToDictionary(parameters);

parameters[nameof(filePath)] = filePath;
Expand All @@ -35,6 +33,7 @@ protected virtual void Handle(Exception exception, Dictionary<string, object> pa
{
var isDevEnv = AppEnvironment.IsDev();


using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty)))
{
var exceptionMessage = exception.Message;
Expand All @@ -60,6 +59,20 @@ protected virtual void Handle(Exception exception, Dictionary<string, object> pa
}
}

protected Exception UnWrapException(Exception exception)
{
if (exception is AggregateException aggregateException)
{
return aggregateException.Flatten().InnerException ?? aggregateException;
}
else if (exception is TargetInvocationException)
{
return exception.InnerException ?? exception;
}

return exception;
}

protected bool IgnoreException(Exception exception)
{
return exception is TaskCanceledException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ public MainPage(ClientMauiSettings clientMauiSettings)
{
InitializeComponent();
//#if (appInsights == true)
if (string.IsNullOrEmpty(clientMauiSettings.ApplicationInsights?.ConnectionString) is false)
AppWebView.RootComponents.Insert(0, new()
{
AppWebView.RootComponents.Add(new()
{
ComponentType = typeof(BlazorApplicationInsights.ApplicationInsightsInit),
Selector = "head::after"
});
}
ComponentType = typeof(BlazorApplicationInsights.ApplicationInsightsInit),
Selector = "head::after"
});
//#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public partial class MauiExceptionHandler : ExceptionHandlerBase
{
protected override void Handle(Exception exception, Dictionary<string, object> parameters)
{
exception = UnWrapException(exception);

if (IgnoreException(exception))
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,13 @@ public static async Task Main(string[] args)
{
// By default, App.razor adds Routes and HeadOutlet.
// The following is only required for blazor webassembly standalone.
builder.RootComponents.Add<Routes>("#app-container");
builder.RootComponents.Add<HeadOutlet>("head::after");
//+:cnd:noEmit
//#if (appInsights == true)
var clientWebSettings = builder.Configuration.Get<ClientWebSettings>()!;
if (string.IsNullOrEmpty(clientWebSettings.ApplicationInsights?.ConnectionString) is false)
{
builder.RootComponents.Add<BlazorApplicationInsights.ApplicationInsightsInit>("head::after");
}
builder.RootComponents.Add<BlazorApplicationInsights.ApplicationInsightsInit>(selector: "head::after");
//#endif
//-:cnd:noEmit
builder.RootComponents.Add<Routes>("#app-container");
}

builder.ConfigureServices();
Expand Down
Loading

0 comments on commit 8cbc4d0

Please sign in to comment.