Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the recommended approach for .NET Core 2.0? #11

Closed
amoerie opened this issue Oct 20, 2017 · 10 comments
Closed

What's the recommended approach for .NET Core 2.0? #11

amoerie opened this issue Oct 20, 2017 · 10 comments

Comments

@amoerie
Copy link

amoerie commented Oct 20, 2017

The Program class looks significantly different in .NET Core 2.0 compared to 1.0, so are the docs still relevant now?

See https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#update-main-method-in-programcs for changes to the typical Program.cs file.

@Chris-ZA
Copy link

I’m using the approach described here: https://nblumhardt.com/2017/08/use-serilog/

That’s the Serilog creators blog.

I’ve made some small modifications to it to add Seq support and other config by way of a config file but the core implementation is pretty much what’s in that post and I’m really happy with the results.

Let me know if you want me to post my config and I’ll do it when I’m at my laptop again - using my phone to post this.

Regards,

Chris

@amoerie
Copy link
Author

amoerie commented Oct 20, 2017

I got it working by updating the NuGet package to the latest pre-release and using the overload which takes a lambda. That seemed to be the least intrusive way of setting it up. I'm hesitant to add try catch statements around the entire application as that complicates development and doesn't really feel right, although I can't really put to words why.

Anyway, just a little heads up for anyone else: the GitHub readme does not correspond to the latest stable NuGet release, because the default branch here is 'dev', which contains alpha releases.

To the maintainers of this repo, can you maybe add a warning about this to the readme? Or maybe set the default branch back to master? Thanks!

@nblumhardt
Copy link
Member

I'm hesitant to add try catch statements around the entire application as that complicates development and doesn't really feel right, although I can't really put to words why.

When you've deployed the app, and it won't start up because of some missing DLL or other configuration issue, you'll be glad that try/catch block is there collecting and logging why ;-)

@nblumhardt
Copy link
Member

#12 created to release the version with the new overload. Any feedback on how it's working for you would be great, thanks @amoerie 👍

@nblumhardt
Copy link
Member

I've updated the main README with the 2.0 Program.cs syntax. We'll see how the 2.1 release goes, pushing #12 through now. Cheers!

@amoerie
Copy link
Author

amoerie commented Oct 23, 2017

Thanks for the quick response! The prerelease already seemed to be working fine, so I'm sure the 2.1 release will do too!

@amoerie
Copy link
Author

amoerie commented Oct 26, 2017

To anyone reading this in the future, this is what I ended up with:

  • Environment check to only wrap the application with try catch in Production. In development environments it is better to leave exceptions uncatched so Visual Studio can break when and where they happen.
  • Capturing of startup errors (In my tests the try catch did not trigger without configuring this)
  • Only showing startup error details in development (full details are always logged to Serilog)
  • Includes IConfigurationRoot fix as mentioned in The sample web app does not allow extra configuration sections to be loaded from the Startup class #13
  • No usage of the static Log class. It may still be implicitly assigned somewhere inside Serilog, but I wanted to avoid any static calls
  • Try to use the built in provider disposal instead of Log.CloseAndFlush
  • Log statements in Program.cs with a contextual logger

Full code:

using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;

namespace Whatever {
  public class Program {
    public static int Main(string[] args) {
      var environment = Environment();
      var configuration = BuildConfiguration(args, environment);
      var logger = BuildLogger(configuration);
      return string.Equals(environment, EnvironmentName.Development, StringComparison.OrdinalIgnoreCase)
        ? MainDevelopment(configuration, logger)
        : MainProduction(configuration, logger);
    }

    static int MainDevelopment(IConfigurationRoot configuration, ILogger logger) {
      var programLogger = logger.ForContext<Program>();
      programLogger.Information("Starting web host in development mode");
      BuildWebHost(configuration, logger, detailedErrors: true).Run();
      programLogger.Information("Web host terminated gracefully");
      (logger as IDisposable)?.Dispose();
      return 0;
    }

    static int MainProduction(IConfigurationRoot configuration, ILogger logger) {
      var programLogger = logger.ForContext<Program>();
      try {
        programLogger.Information("Starting web host in production mode");
        BuildWebHost(configuration, logger, detailedErrors: false).Run();
        programLogger.Information("Web host terminated gracefully");
        return 0;
      }
      catch (Exception exception) {
        programLogger.Fatal(exception, "Web host terminated unexpectedly");
        return 1;
      }
      finally {
        (logger as IDisposable)?.Dispose();
      }
    }

    static IWebHost BuildWebHost(IConfigurationRoot configuration, ILogger logger, bool detailedErrors) =>
      new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .CaptureStartupErrors(true)
        .UseSetting(WebHostDefaults.DetailedErrorsKey, detailedErrors.ToString())
        .UseConfiguration(configuration)
        .ConfigureServices(s => s.AddSingleton(configuration)) // allow IConfigurationRoot injection in Startup
        .UseStartup<Startup>()
        .UseSerilog(logger, dispose: true)
        .Build();

    static IConfigurationRoot BuildConfiguration(string[] args, string environment) => new ConfigurationBuilder()
      .SetBasePath(Directory.GetCurrentDirectory())
      .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
      .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
      .AddCommandLine(args)
      .Build();

    static ILogger BuildLogger(IConfiguration configuration) => new LoggerConfiguration()
      .ReadFrom.Configuration(configuration)
      .Enrich.FromLogContext()
      .CreateLogger();

    static string Environment() => System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
      ?? EnvironmentName.Production;
  }
}

@joshmouch
Copy link

@amoerie Thank you for that code. I've experienced various issues since I started using ASP.Net core that can only be resolved by looking at stdOut. I think this is a step in the right direction for logging these types of issues better.

@a13531a
Copy link

a13531a commented Mar 8, 2018

I'm hesitant to add try catch statements around the entire application as that complicates development and doesn't really feel right, although I can't really put to words why.

When you've deployed the app, and it won't start up because of some missing DLL or other configuration issue, you'll be glad that try/catch block is there collecting and logging why ;-)

Or rethrowing of exception seems to eliminate this hesitation @amoerie

try
    {
        Log.Information("Starting web host");

        var host = WebHostBuilder.CreateDefault()
            .UseStartup<Startup>()
            .UseSerilog()  // <- The magic
            .Build();

        host.Run();

        return 0;
    }
    catch (Exception ex)
    {
        Log.Fatal(ex, "Host terminated unexpectedly");
        throw;
    }
    finally
    {
        Log.CloseAndFlush();
    }

@amoerie
Copy link
Author

amoerie commented Mar 21, 2018

That's actually still annoying during development because Visual Studio fails to properly trace it back to the original place where the exception was thrown. I still prefer to not have a try catch around everything in development environments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants