简体   繁体   English

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

[英]How do I write logs from within Startup.cs?

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.为了调试在启动时失败的 .NET Core 应用程序,我想从 startup.cs 文件中写入日志。 I have logging setup within the file that can be used in the rest of the app outside the startup.cs file, but not sure how to write logs from within the startup.cs file itself.我在文件中有日志记录设置,可以在 startup.cs 文件之外的应用程序的其余部分中使用,但不确定如何从 startup.cs 文件本身写入日志。

.Net Core 3.1 .Net 核心 3.1

Unfortunately, for ASP.NET Core 3.0, the situation is again a bit different.不幸的是,对于 ASP.NET Core 3.0,情况又有点不同。 The default templates use the HostBuilder (instead of the WebHostBuilder ) which sets up a new generic host that can host several different applications, not limited to web applications.默认模板使用HostBuilder (而不是WebHostBuilder ),它设置了一个新的通用主机,可以托管多个不同的应用程序,不限于 Web 应用程序。 Part of this new host is also the removal of the second dependency injection container that previously existed for the web host.这个新主机的一部分还删除了以前为 Web 主机存在的第二个依赖注入容器。 This ultimately means that you won't be able to inject any dependencies apart from the IConfiguration into the Startup class.这最终意味着您将无法将除IConfiguration之外的任何依赖项注入Startup类。 So you won't be able to log during the ConfigureServices method.因此,您将无法在ConfigureServices方法期间进行记录。 You can, however, inject the logger into the Configure method and log there:但是,您可以将记录器注入到Configure方法中并在那里记录:

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

    // …
}

If you absolutely need to log within ConfigureServices , then you can continue to use the WebHostBuilder which will create the legacy WebHost that can inject the logger into the Startup class.如果您绝对需要ConfigureServices中登录,那么您可以继续使用WebHostBuilder ,它将创建可以将记录器注入Startup类的旧版WebHost Note that it's likely that the web host will be removed at some point in the future.请注意,Web 主机可能会在将来的某个时候被删除。 So you should try to find a solution that works for you without having to log within ConfigureServices .因此,您应该尝试找到适合您的解决方案,而无需登录ConfigureServices


.NET Core 2.x .NET 核心 2.x

This has changed significantly with the release of ASP.NET Core 2.0.随着 ASP.NET Core 2.0 的发布,这种情况发生了显着变化。 In ASP.NET Core 2.x, logging is created at the host builder.在 ASP.NET Core 2.x 中,日志记录是在主机生成器中创建的。 This means that logging is available through DI by default and can be injected into the Startup class:这意味着默认情况下可以通过 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");

        // …
    }
}

Option 1: Directly use log (eg Serilog) in startup-选项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");
        ....
    }

Output:输出:

序列日志

To setup Serilog in asp.net-core application, check out the Serilog.AspNetCore package on GitHub .要在 asp.net-core 应用程序中设置 Serilog,请查看GitHub 上的 Serilog.AspNetCore 包


Option2: Configure logging in program.cs like this-选项 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();

User loggerFactory in startup like this-像这样启动的用户 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.");
    }
}

Complete details available on this link链接上提供了完整的详细信息

In .NET Core 3.1 , you can create a logger directly using LogFactory..NET Core 3.1中,您可以直接使用 LogFactory 创建记录器。

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

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

The official solution is currently to setup a local LoggerFactory like this:目前官方的解决方案是像这样设置一个本地 LoggerFactory:

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

See also: https://github.com/dotnet/aspnetcore/issues/9337#issuecomment-539859667另请参阅: https ://github.com/dotnet/aspnetcore/issues/9337#issuecomment-539859667

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对于 .NET Core 3.0,官方文档有这样的说法: 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:不支持在Startup.ConfigureServices方法中完成 DI 容器设置之前写入日志:

  • Logger injection into the Startup constructor is not supported.不支持将记录器注入Startup构造函数。
  • Logger injection into the Startup.ConfigureServices method signature is not supported不支持将记录器注入Startup.ConfigureServices方法签名

But as they say in the docs you can configure a service that depends on ILogger, so if you wrote a class StartupLogger:但正如他们在文档中所说,您可以配置依赖于 ILogger 的服务,因此如果您编写了一个 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:然后在 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");
}

Edit: this produces a compiler warning, for the sake of debugging your StartUp class this should be OK but not for production:编辑:这会产生编译器警告,为了调试您的 StartUp 类,这应该没问题,但不适用于生产:

Startup.cs(39, 32): [ASP0000] Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Startup.cs(39, 32): [ASP0000] 从应用程序代码中调用“BuildServiceProvider”会导致创建一个额外的单例服务副本。 Consider alternatives such as dependency injecting services as parameters to 'Configure'.考虑替代方案,例如依赖注入服务作为“配置”的参数。

I use a solution avoiding 3rd party loggers implementing a "logger buffer" with ILogger interface.我使用了一种解决方案,避免使用ILogger接口实现“记录器缓冲区”的第 3 方记录器。

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.在 startup.cs 中使用很简单,当然你在调用 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())

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.我正在使用 NLog,甚至构建一个新的 ServiceCollection,在任何服务集合上调用 .CreateBuilder(),创建一个日志服务......在 ConfigureServices 期间,这些都不会写入日志文件。

The problem is that logging isn't really a thing until after the ServiceCollection is built, and it's not built during ConfigureServices.问题是,在构建 ServiceCollection 之前,日志记录并不是真正的事情,而且它不是在 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.基本上,我只想(需要)以配置扩展方法记录启动期间发生的事情,因为我遇到的唯一问题是 PROD,我无法附加调试器。

The solution that worked for me was using the old .NET Framework NLog method:对我有用的解决方案是使用旧的 .NET Framework NLog 方法:

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.将该权利添加到扩展方法类中,并且我能够在 ConfigureServices 期间及之后写入日志(“the”日志)。

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

Using Rolf's answer , I put this in my Startup constructor:使用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...
}

Main code:主要代码:

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 sets up a default console logger. CreateDefaultBuilder设置一个默认的控制台记录器。

... configures the ILoggerFactory to log to the console and debug output ... 将 ILoggerFactory 配置为登录到控制台并调试输出

Startup code:启动代码:

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

I couldn't get the injection of an ILogger to work, but perhaps that's because it's not a controller.我无法让 ILogger 注入工作,但也许那是因为它不是控制器。 More info welcome!欢迎更多信息!

Refs:参考:

For .Net 6对于.Net 6

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

Or even more easy way:或者更简单的方法:

var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
ILogger logger = app.Logger;

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

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

Are you making decisions about which services you are using at runtime that you wish to log?您是否正在决定在运行时使用哪些服务并希望记录? Or are you making decisions about how those services are configured, which you wish to log?或者您是否正在决定如何配置这些服务,您希望记录哪些?

In other words;换句话说;

public void ConfigureServices(IServiceCollection services){
   // Do you really want to log something here?
   services.AddRazorPages(options => {
       // Or would you be satisfied by logging something here?
   });
}

If it is only the latter, you can move the implementation of these lambda functions into an IConfigureOptions<T> service, allowing you to inject other services.如果只是后者,您可以将这些 lambda 函数的实现移动到IConfigureOptions<T>服务中,从而允许您注入其他服务。 Continuing the above example, you could create the following;继续上面的示例,您可以创建以下内容;

public class ConfigureRazorPagesOptions : IConfigureOptions<RazorPagesOptions>
{
    private readonly ILogger<ConfigureRazorPagesOptions> logger;
    public ConfigureRazorPagesOptions(ILogger<ConfigureRazorPagesOptions> logger)
    {
        this.logger = logger;
    }

    public void Configure(RazorPagesOptions options)
    {
        logger.LogInformation("Now I can log here!");
    }
}

public void ConfigureServices(IServiceCollection services){
   services.AddRazorPages();
   services.AddSingleton<IConfigureOptions<RazorPagesOptions>, ConfigureRazorPagesOptions>();
}

If your .ConfigureServices method is getting too complicated, you might want to create such services.如果您的.ConfigureServices方法变得过于复杂,您可能需要创建此类服务。 However, that's a lot of boilerplate to add for each options type.但是,要为每种选项类型添加大量样板。 There is also an equivalent shorthand, to inject other services into a configuration lamda;还有一个等效的简写,将其他服务注入到配置 lamda 中;

services.AddOptions<RazorPagesOptions>()
    .Configure<ILogger<RazorPagesOptions>>((options, logger) => { 
        logger.LogInformation("I can log here too!");
    });

这对我有用

private static readonly Logger logger = LogManager.GetLogger("Audit")

I found a very easy implementation:我发现了一个非常简单的实现:

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllersWithViews();

     var conn = Configuration.GetValue("conn", Configuration.GetConnectionString("Conn"));

     Console.WriteLine($@"System starting at {DateTime.Now}");

     Console.WriteLine($@"Database: {conn}");
}

Just using Console.WriteLine worked, even on Docker.只需使用Console.WriteLine 即可,即使在 Docker 上也是如此。

只需使用下面的行登录 Startup.cs

Log.Information("App started.");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 Blazor 从 .CS 文件而不是 .RAZOR 文件访问在 Startup.cs 中注释的范围服务中的数据? - How do I access data within a scoped service, annotated in Startup.cs, from a .CS file NOT a .RAZOR file using Blazor? 如何检索Program.cs中返回的访问令牌以在Startup.cs中可用? - How do I retrieve an access token returned in Program.cs to be available in Startup.cs? 如何解决从 Program.cs 调用 Startup.cs? - How to solve calling Startup.cs from Program.cs? 从Startup.Cs中的Configure方法中获取应用程序目录 - Get Application Directory from within Configure method in Startup.Cs 如何在我的 startup.cs 中使用 appsettings.json 中的值? - 不实例化但实际使用该值 - How do I use a value from appsettings.json in my startup.cs ? - Not instantiate but actually use the value 如何更新 startup.cs 中的服务 top 在 dotnet3.1 中使用 ITableEntity 和 TableClient 作为服务 - How do i update a service in startup.cs top use ITableEntity and TableClient as service in dotnet3.1 如何在 Startup.cs 中指定对两个基本路径的支持 - How do I specify support for two base paths in Startup.cs 在ITHit WebDAV服务器中,如何使用在startup.cs中配置的CORS允许的来源列表 - In ITHit WebDAV server how do I make it use the CORS allowed origins list configured in startup.cs 如何从 class 库中获取 IServiceProvider,其中 serviceProvider 构建在 startup.cs 中? - How can I get the IServiceProvider from a class library where the serviceProvider is builded in startup.cs? 如何从Startup.cs获取所有服务 - How to get all the Services from Startup.cs
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM