如何从Startup.cs中编写日志

Mar*_*man 58 c# asp.net-core

为了调试启动失败的.net核心应用程序,我想从startup.cs文件中编写日志.我在文件中有日志设置,可以在startup.cs文件之外的应用程序的其余部分使用,但不知道如何从startup.cs文件本身写入日志.

pok*_*oke 98

随着ASP.NET Core 2.0的发布,这已经发生了重大变化.在ASP.NET Core 2.0中,将在主机构建器中创建日志记录.这意味着默认情况下可以通过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");

        // …
    }
}
Run Code Online (Sandbox Code Playgroud)

  • **.NET 5**:“将附加参数传递给与主机一起初始化的“Startup””“builder.UseStartup(context =&gt; new Startup(logger));” - https://docs.microsoft.com /en-us/aspnet/core/release-notes/aspnetcore-5.0?view=aspnetcore-5.0#control-startup-class-activation (5认同)
  • @Wellspring 在 3.0 中,这很“困难”,因为当 `ConfigureServices` 运行时,记录器实际上还不存在。因此,此时您将无法登录,因为还没有记录器。从好的方面来说,这仍然使您能够在“ConfigureServices”中配置记录器,因为它都是相同的 DI 容器(这实际上是一件好事)。– 如果您绝对需要记录内容,您可以单独收集信息(例如在列表中),然后在记录器可用时立即将其记录下来。 (3认同)
  • 谢谢。令人惊奇的是,您可以花多长时间寻找简单问题的答案。@poke再次感谢您通知我我的选择。您从哪里获得此信息?我已经确认我可以在Configure中记录东西,这比用锋利的棍棒戳戳眼睛中的戳子更好,但可能不如ConfigureServices期间那样棒。就我而言,我想记录是否有环境设置,甚至可以将它们发布到日志中。没有骰子?感叹……不知道为什么要这么难。但是至少,由于这篇文章,我知道我能做和不能做的。 (2认同)
  • 在“.NET 3.1”中,您当前可以在“ConfigureServices”方法中登录,*无需*依靠“WebHostBuilder”。使用下面的答案:/sf/answers/4304194331/ (2认同)
  • @Aage但这将有几个缺点:您将必须重复完整的日志记录配置,日志记录配置也不会反映您的应用程序配置(例如在appsettings等中配置的日志级别),并且您通常要设置第二个日志记录基础设施。我仍然建议您寻找一种解决方案,如何避免在 DI 设置期间进行日志记录。 (2认同)
  • @Pang这是一个有趣的新事物,但考虑到日志记录基础设施仅在启动实例化后才由主机创建(和配置),它仍然无法解决鸡和蛋的问题。 (2认同)

San*_*ket 25

选项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");
        ....
    }
Run Code Online (Sandbox Code Playgroud)

输出:

serilog

要在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();
Run Code Online (Sandbox Code Playgroud)

用户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.");
    }
}
Run Code Online (Sandbox Code Playgroud)

链接提供完整的详细信息


Lia*_*ang 16

.NET Core 3.1 中,您可以直接使用 LogFactory 创建记录器。

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

ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
Run Code Online (Sandbox Code Playgroud)


R.T*_*tov 16

对于.Net 6

var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
var logger = ((IApplicationBuilder)app).ApplicationServices.GetService<ILogger<Program>>();
logger.LogInformation("Some logs");
Run Code Online (Sandbox Code Playgroud)

或者更简单的方法:

var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
ILogger logger = app.Logger;
Run Code Online (Sandbox Code Playgroud)

  • 如何在应用程序构建之前获取记录器? (6认同)

Rol*_*sen 9

目前官方的解决方案是像这样设置本地 LoggerFactory:

    using var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder.SetMinimumLevel(LogLevel.Information);
        builder.AddConsole();
        builder.AddEventSourceLogger();
    });
    var logger = loggerFactory.CreateLogger("Startup");
    logger.LogInformation("Hello World");
Run Code Online (Sandbox Code Playgroud)

另见:https : //github.com/dotnet/aspnetcore/issues/9337#issuecomment-539859667


Joh*_*rth 9

使用Rolf 的答案,我将其放入我的 Startup 构造函数中:

private readonly ILogger _logger;

public Startup(IConfiguration configuration)
{
    Configuration = configuration;

    using var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder.SetMinimumLevel(LogLevel.Information);
        builder.AddConsole();
        builder.AddEventSourceLogger();
    });
    _logger = loggerFactory.CreateLogger<Startup>();
}

public void ConfigureServices(IServiceCollection services)
{
    _logger.LogInformation("ConfigureServices...");
    // ...and so on...
}
Run Code Online (Sandbox Code Playgroud)


eme*_*oel 7

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

问题是,只有在 ServiceCollection 构建之后,日志记录才真正成为问题,而且它不是在 ConfigureServices 期间构建的。

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

对我有用的解决方案是使用旧的 .NET Framework NLog 方法:

private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
Run Code Online (Sandbox Code Playgroud)

将这一权利添加到扩展方法类中,并且我能够在ConfigureServices 期间及之后写入日志(“该”日志)。

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


Dyl*_*ard 5

对于 .NET Core 3.0,官方文档是这样说的:https : //docs.microsoft.com/en-us/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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在 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");
}
Run Code Online (Sandbox Code Playgroud)

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

Startup.cs(39, 32): [ASP0000] 从应用程序代码调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。考虑替代方案,例如将依赖注入服务作为“配置”的参数。


小智 5

我使用了一个解决方案,避免使用ILogger接口实现“记录器缓冲区”的 3rd 方记录器。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

在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())
Run Code Online (Sandbox Code Playgroud)