nblumhardt / serilog-sinks-browserhttp

Serilog sink for client-side Blazor, with matching ASP.NET Core server relay
https://serilog.net
Apache License 2.0
52 stars 22 forks source link

The usage in a blazor RC1 project breaks the NavigationManager #9

Open alexfdezsauco opened 4 years ago

alexfdezsauco commented 4 years ago

The usage in a blazor RC1 project breaks the NavigationManager.

How to reproduce:

1) Assume that you have a service locator that resolve backed services. In this case the ingest backend.

Program.cs

    ....
    host = await host.RecreateLogAsync();
    try
    {
        await host.RunAsync();
    }
    catch (Exception ex)
    {
        Log.Fatal(ex, "An exception occurred while creating the web assembly host");
        throw;
    }

WebAssemblyHostExtensions.cs

public static class WebAssemblyHostExtensions
{
    public static async Task<WebAssemblyHost> RecreateLogAsync(this WebAssemblyHost @this)
    {
        return await @this.RecreateLogAsync(@this.Services.GetService<IServiceDiscovery>(), @this.Services.GetService<LoggingLevelSwitch>());
    }

    private static async Task<WebAssemblyHost> RecreateLogAsync(this WebAssemblyHost @this, IServiceDiscovery serviceDiscovery, LoggingLevelSwitch loggingLevelSwitch)
    {
        var configuration = new LoggerConfiguration()
            .MinimumLevel.ControlledBy(loggingLevelSwitch)
            .Enrich.WithProperty("Application", "MyApp")
            .WriteTo.BrowserConsole(levelSwitch: loggingLevelSwitch);

        try
        {
            var serviceEndPoint = await serviceDiscovery.GetServiceEndPoint("ingest");
            if (!string.IsNullOrWhiteSpace(serviceEndPoint))
            {
                var ingestUrl = serviceEndPoint.TrimEnd('/') + "/ingest";
                configuration = configuration.WriteTo.BrowserHttp($"{ingestUrl}", controlLevelSwitch: loggingLevelSwitch);
                Log.Information("Configured Log for with server side ingest '{IngestUrl}'", ingestUrl);
            }
            else
            {
                Log.Warning("Ingest server endpoint not found");
            }
        }
        catch (Exception e)
        {
            Log.Warning(e, "Error recreating Log for with server side ingest");
        }

        Log.Logger = configuration.CreateLogger();

        return @this;
    }
 }

Results:

The code above works but breaks the NavigationManager. When the NavigateTo is called, nothing happens.

nblumhardt commented 4 years ago

Hi! Thanks for letting me know. I'd be happy to accept a PR if you have any idea why this might be occurring, but I don't have time to investigate personally at the moment.

All the best, Nick

eugeneniemand commented 4 years ago

I have found the same as soon as I use the BrowserHttp sink my navigation just stops. No errors that I can see, nothing printed on the console etc. I dont even know where to start looking. This is my setup and I can see hello browser in the console and in the http endpoint but navigation just does nothing

services.AddSingleton(provider =>
            {
                var config = provider.GetService<IConfiguration>();
                _appConfiguration = config.GetSection("App").Get<AppConfiguration>();

                var levelSwitch = new LoggingLevelSwitch();
                Log.Logger = new LoggerConfiguration()
                    .MinimumLevel.ControlledBy(levelSwitch)
                    .Enrich.WithProperty("InstanceId", Guid.NewGuid().ToString("n"))
                    .WriteTo.BrowserConsole()
                    .WriteTo.BrowserHttp($"{_appConfiguration.ApiBaseUrl}logIngest", controlLevelSwitch: levelSwitch)
                    .CreateLogger();

                Log.Information("Hello, browser!");

                return _appConfiguration;
            });
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="2.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" />
    <PackageReference Include="Microsoft.Authentication.WebAssembly.Msal" Version="3.2.0" />
    <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" />
    <PackageReference Include="Serilog.Sinks.BrowserConsole" Version="1.0.0-dev-00012" />
    <PackageReference Include="Serilog.Sinks.BrowserHttp" Version="1.0.0-dev-00012" />
    <PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
tusanesen commented 4 years ago

Hi.. I think the reason is that WebAssemblyHostBuilder.CreateDefault() is being called inside BrowserHttp configuration and this causes services not built correctly. I worked around the problem by calling WebAssemblyHostBuilder.CreateDefault() again after configuring BrowserHttp.

So its called 3 times in my workaround: first, to have builder to get config file which is needed to configure BrowserHttp, second BrowserHttp itself (just to get BaseAddess, which may not be necessary), third to build everything else last time to start up correctly. hope it helps.. thanks.

nblumhardt commented 4 years ago

Thanks for the analysis, @tusanesen :+1: ... wonder if we can fix this, using some other API, now that Blazor has matured a bit?

eugeneniemand commented 4 years ago

@tusanesen do you care to provide a simple sample of how you do this please

Larswa commented 3 years ago

I can confirm I am seeing the same behavior in the .net 5.0.1 release of Blazor.

@eugeneniemand To get around it I do something like this to call the WebAssemblyHostBuilder.CreateDefault() multiple times:

public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder1 = WebAssemblyHostBuilder.CreateDefault(args);

            var apiUrl = builder1.Configuration["ApiUrl"];
            SetupLogger(apiUrl);
            Log.Information("Configuration found: @{apiUrl}", apiUrl);
            Log.Information($"Environment: {builder1.HostEnvironment.Environment}");

            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");
            builder.Services.AddHttpClient<ITrackDataService, TrackDataService>(client => client.BaseAddress = new Uri(apiUrl));
            builder.Services.AddHttpClient<IGameDataService, GameDataService>(client => client.BaseAddress = new Uri(apiUrl));

            await builder.Build().RunAsync();
        }

        private static void SetupLogger(string apiUrl)
        {
            var levelSwitch = new LoggingLevelSwitch();

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.ControlledBy(levelSwitch)
                .Enrich.WithExceptionDetails()
                .Enrich.WithEnvironmentUserName()
                .Enrich.WithMachineName()
                .Enrich.WithProperty("InstanceId", Guid.NewGuid().ToString("n"))
                .WriteTo.BrowserHttp(controlLevelSwitch: levelSwitch, endpointUrl: $"{apiUrl}/ingest")
                .WriteTo.BrowserConsole()
                .CreateLogger();

            Log.Information("Browser says hi... ");
        }
    }