我如何从Startup.cs中写入日志


109

为了调试启动失败的.net核心应用程序,我想从startup.cs文件中写入日志。我在该文件中有日志记录设置,该文件可在startup.cs文件之外的其他应用程序中使用,但是不确定如何从startup.cs文件本身内写入日志。

Answers:


175

.Net Core 3.1

不幸的是,对于ASP.NET Core 3.0,情况再次有所不同。默认模板使用HostBuilder(而不是WebHostBuilder)来设置新的通用主机,该主机可以承载多个不同的应用程序,而不仅限于Web应用程序。此新主机的一部分还包括删除先前为Web主机存在的第二个依赖项注入容器。最终,这意味着您将无法将任何依赖项注入IConfigurationStartup类之外。因此,您将无法在该ConfigureServices方法期间登录。但是,您可以将记录器注入到Configure方法中并在那里记录:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
    logger.LogInformation("Configure called");

    // …
}

如果您绝对需要登录ConfigureServices,则可以继续使用WebHostBuilder,它将创建WebHost可将记录器注入到Startup类中的遗留物。请注意,将来可能会删除Web主机。因此,您无需登录即可尝试找到适合您的解决方案ConfigureServices


.NET Core 2.x

随着ASP.NET Core 2.0的发布,这已经发生了重大变化。在ASP.NET Core 2.x中,日志记录是在主机生成器上创建的。这意味着默认情况下可以通过DI使用日志记录,并且可以将其注入到Startup类中:

public class Startup
{
    private readonly ILogger<Startup> _logger;

    public IConfiguration Configuration { get; }

    public Startup(ILogger<Startup> logger, IConfiguration configuration)
    {
        _logger = logger;
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        _logger.LogInformation("ConfigureServices called");

        // …
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        _logger.LogInformation("Configure called");

        // …
    }
}

4
谢谢。令人惊奇的是,您可以花多长时间寻找简单问题的答案。@poke再次感谢您通知我我的选择。您从哪里获得此信息?我已经确认我可以在Configure中记录东西,这比用尖锐的棍子戳在眼睛上(戳双关)更可取,但可能不如ConfigureServices期间那样棒。就我而言,我想记录是否有环境设置,甚至可以将它们发布到日志中。没有骰子?感叹……不知道为什么要这么难。但是至少,由于这篇文章,我知道我能做和不能做的。
Wellspring

2
@Wellspring在3.0中,它很“困难”,因为到ConfigureServices运行时,记录器实际上还不存在。这样一来,您将无法仅由于尚无记录器而登录。从ConfigureServices好的方面来说,这仍然使您能够在内配置记录器,因为它都是相同的DI容器(这实际上是一件好事)。–如果绝对需要记录内容,则可以例如单独收集信息(例如,在列表中),然后在记录仪可用时将其注销。

.NET 3.1您目前可以登录内ConfigureServices方法没有回落的WebHostBuilder。请在下面使用答案:stackoverflow.com/a/61488490/2877982
Aage

1
@Aage但这有几个缺点:您将不得不重复完整的日志记录配置,日志记录配置也不会反映您的应用程序配置(例如,在appsettings中配置的日志级别等),并且您通常会设置第二个日志记录基础结构。我仍然建议您寻找一种解决方案,如何避免在那里完全进行DI设置期间的日志记录。

@poke我没想过。真的,我只希望在我的计算机中连接显式控制器构造Startup.cs(这样,在忘记依赖项时会出现编译器错误),而不是注册自定义依赖项。因此,我需要解决这些记录器。但这可能有点hacky,是的。
Aage

37

选项1:在启动时直接使用日志(例如Serilog),

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");
        ....
    }

输出:

丝光

要在asp.net-core应用程序中设置Serilog,请在GitHub上检出Serilog.AspNetCore软件包


选项2:像这样配置在program.cs中的登录-

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

host.Run();

像这样的启动用户loggerFactory-

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.");
    }
}

链接上提供了完整的详细信息


4

我使用一种避免第三方记录器通过ILogger接口实现“记录器缓冲区”的解决方案。

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();
    }
}

在startup.cs中的用法很简单,当然,在调用Configure之后,您会获得日志输出。但总比没有好。:

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())

4

对于.NET Core 3.0,官方文档有这样的说法:https : //docs.microsoft.com/zh-cn/aspnet/core/fundamentals/logging/?view= aspnetcore-3.0#create-logs-in- startup

Startup.ConfigureServices不支持在方法中完成DI容器设置之前写入日志:

  • Startup不支持将Logger注入构造函数。
  • Startup.ConfigureServices不支持将Logger注入方法签名

但是正如他们在文档中所说,您可以配置依赖于ILogger的服务,因此,如果您编写了StartupLogger类:

public class StartupLogger
{
    private readonly ILogger _logger;

    public StartupLogger(ILogger<StartupLogger> logger)
    {
        _logger = logger;
    }

    public void Log(string message)
    {
        _logger.LogInformation(message);
    }
}

然后在Startup.ConfigureServices中添加服务,然后您需要构建服务提供程序以访问DI容器:

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");
}

编辑:这会产生一个编译器警告,为了调试您的StartUp类,这应该可以,但不适用于生产环境:

  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'.

3

根据.net core 3.1,您可以直接使用LogFactory创建记录器。

var loggerFactory = LoggerFactory.Create(builder =>
{
     builder.AddConsole();                
});

ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");


1

主要代码:

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

CreateDefaultBuilder设置默认的控制台记录器。

...将ILoggerFactory配置为登录到控制台并调试输出

启动代码:

using Microsoft.Extensions.Logging;
...
public class Startup
{
    private readonly ILogger _logger;

    public Startup(IConfiguration configuration, ILoggerFactory logFactory)
    {
        _logger = logFactory.CreateLogger<Startup>();
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        _logger.LogInformation("hello stackoverflow");
    }

我无法注入ILogger来工作,但这也许是因为它不是控制器。欢迎更多信息!

参考:


0

上面的答案对我都不起作用。我正在使用NLog,甚至构建一个新的ServiceCollection,在任何服务集合上调用.CreateBuilder(),创建一个日志记录服务……在ConfigureServices期间,这些都不会写入日志文件。

问题在于,直到构建ServiceCollection之后,日志记录才真正成为问题,并且在ConfigureServices期间不会进行日志记录。

基本上,我只想(需要)以配置扩展方法记录启动过程中发生的情况,因为我遇到问题的唯一层是PROD,而我无法在其中附加调试器。

对我有用的解决方案是使用旧的.NET Framework NLog方法: private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); 在扩展方法类中添加了该权限,并且我能够在ConfigureServices及其之后写入日志(“ the”日志)。

我不知道将其实际发布到生产代码中是否是一个好主意(我不知道.NET控制的ILogger和此NLog.ILogger是否会在任何时候发生冲突),但是我只需要它来查看发生了什么上。


-1

我设法通过在文件中使用Nlog静态创建一个记录器,然后在启动方法中使用它来做到这一点。

private readonly NLog.Logger _logger = new NLog.LogFactory().GetCurrentClassLogger();

1
在此可行的同时,我建议使用ASP.NET Core和内置的或第3方的依赖项注入(DI)随附的标准接口进行日志记录。通过使用接口,您可以a)替换所需的日志记录,b)在测试将ILogger <TController>作为服务注入的类(例如控制器)时,它们更易于模拟。
曼弗雷德

我发布了完全相同的答案之后,才第一次看到这一点。@Manfred那里没有别的办法。我想除了System.IO.File.Write()方法。
emery.noel

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.