How do I write logs from within Startup.cs?

前端 未结 10 1264
伪装坚强ぢ
伪装坚强ぢ 2020-12-01 04:16

In order to debug a .NET Core app which is failing on startup, I would like to write logs from within the startup.cs file. I have logging setup within the file that can be u

相关标签:
10条回答
  • 2020-12-01 04:28

    I use a solution avoiding 3rd party loggers implementing a "logger buffer" with ILogger interface.

    public class LoggerBuffered : ILogger
    {
        class Entry
        {
            public LogLevel _logLevel;
            public EventId  _eventId;
            public string   _message;
        }
        LogLevel            _minLogLevel;
        List<Entry>         _buffer;
        public LoggerBuffered(LogLevel minLogLevel)
        {
            _minLogLevel = minLogLevel;
            _buffer = new List<Entry>();
        }
        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
    
        public bool IsEnabled(LogLevel logLevel)
        {
            return logLevel >= _minLogLevel;
        }
    
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (IsEnabled(logLevel)) {
                var str = formatter(state, exception);
                _buffer.Add(new Entry { _logLevel = logLevel, _eventId = eventId, _message = str });
            }
        }
        public void CopyToLogger (ILogger logger)
        {
            foreach (var entry in _buffer)
            {
                logger.Log(entry._logLevel, entry._eventId, entry._message);
            }
            _buffer.Clear();
        }
    }
    

    Usage in startup.cs is easy, of course you get log output after call of Configure. But better than nothing. :

    public class Startup
    {
    ILogger         _logger;
    
    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        _logger = new LoggerBuffered(LogLevel.Debug);
        _logger.LogInformation($"Create Startup {env.ApplicationName} - {env.EnvironmentName}");
    
    }
    
    public void ConfigureServices(IServiceCollection services)
    {
        _logger.LogInformation("ConfigureServices");
        services.AddControllersWithViews();
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
    {
        (_logger as LoggerBuffered).CopyToLogger(logger);
        _logger = logger;   // Replace buffered by "real" logger
        _logger.LogInformation("Configure");
    
        if (env.IsDevelopment())
    
    0 讨论(0)
  • 2020-12-01 04:28

    None of the existing answers worked for me. I'm using NLog, and even building a new ServiceCollection, calling .CreateBuilder() on any service collection, creating a logging service ... none of that would write to a log file during ConfigureServices.

    The problem is that logging isn't really a thing until after the ServiceCollection is built, and it's not built during ConfigureServices.

    Basically, I just want (need) to log what's going on during startup in a configuration extension method, because the only tier I'm having a problem on is PROD, where I can't attach a debugger.

    The solution that worked for me was using the old .NET Framework NLog method:

    private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
    

    Added that right to the extension method class, and I was able to write to a log ("the" log) during ConfigureServices and after.

    I have no idea if this is a good idea to actually release into production code (I don't know if the .NET controlled ILogger and this NLog.ILogger will conflict at any point), but I only needed it to see what was going on.

    0 讨论(0)
  • 2020-12-01 04:36

    Option 1: Directly use log (e.g. Serilog) in startup-

    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            Log.Logger = new LoggerConfiguration()
               .MinimumLevel.Debug()
               .WriteTo.RollingFile(Path.Combine(env.ContentRootPath, "Serilog-{Date}.txt"))
               .CreateLogger();
    
            Log.Information("Inside Startup ctor");
            ....
        }
    
        public void ConfigureServices(IServiceCollection services)
        {
            Log.Information("ConfigureServices");
            ....
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            Log.Information("Configure");
            ....
        }
    

    Output:

    To setup Serilog in asp.net-core application, check out the Serilog.AspNetCore package on GitHub.


    Option2: Configure logging in program.cs like this-

    var host = new WebHostBuilder()
                .UseKestrel()
                .ConfigureServices(s => {
                    s.AddSingleton<IFormatter, LowercaseFormatter>();
                })
                .ConfigureLogging(f => f.AddConsole(LogLevel.Debug))
                .UseStartup<Startup>()
                .Build();
    
    host.Run();
    

    User loggerFactory in startup like this-

    public class Startup
    {
        ILogger _logger;
        IFormatter _formatter;
        public Startup(ILoggerFactory loggerFactory, IFormatter formatter)
        {
            _logger = loggerFactory.CreateLogger<Startup>();
            _formatter = formatter;
        }
    
        public void ConfigureServices(IServiceCollection services)
        {
            _logger.LogDebug($"Total Services Initially: {services.Count}");
    
            // register services
            //services.AddSingleton<IFoo, Foo>();
        }
    
        public void Configure(IApplicationBuilder app, IFormatter formatter)
        {
            // note: can request IFormatter here as well as via constructor
            _logger.LogDebug("Configure() started...");
            app.Run(async (context) => await context.Response.WriteAsync(_formatter.Format("Hi!")));
            _logger.LogDebug("Configure() complete.");
        }
    }
    

    Complete details available on this link

    0 讨论(0)
  • 2020-12-01 04:37

    Just use the line below for logging in Startup.cs

    Log.Information("App started.");
    
    0 讨论(0)
  • 2020-12-01 04:39

    For .NET Core 3.0 the official docs has this to say: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup

    Writing logs before completion of the DI container setup in the Startup.ConfigureServices method is not supported:

    • Logger injection into the Startup constructor is not supported.
    • Logger injection into the Startup.ConfigureServices method signature is not supported

    But as they say in the docs you can configure a service that depends on ILogger, so if you wrote a class StartupLogger:

    public class StartupLogger
    {
        private readonly ILogger _logger;
    
        public StartupLogger(ILogger<StartupLogger> logger)
        {
            _logger = logger;
        }
    
        public void Log(string message)
        {
            _logger.LogInformation(message);
        }
    }
    

    Then in Startup.ConfigureServices add the service, then you need to build the service provider to get access to the DI container:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton(provider =>
        {
            var service = provider.GetRequiredService<ILogger<StartupLogger>>();
            return new StartupLogger(service);
        });
        var logger = services.BuildServiceProvider().GetRequiredService<StartupLogger>();
        logger.Log("Startup.ConfigureServices called");
    }
    

    Edit: this produces a compiler warning, for the sake of debugging your StartUp class this should be OK but not for production:

    Startup.cs(39, 32): [ASP0000] Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.

    0 讨论(0)
  • 2020-12-01 04:44

    I have managed to do this by statically creating a logger with NLog in the file, then use this within the startup methods.

    private readonly NLog.Logger _logger = new NLog.LogFactory().GetCurrentClassLogger();
    
    0 讨论(0)
提交回复
热议问题