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

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

121911 次浏览

选项1:直接使用log(例如Serilog)在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");
....
}

输出:

serilog

要在asp.net-core应用程序中设置Serilog,请检查Serilog。AspNetCore包在GitHub上. exe


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

完整的细节可在此链接

.Net Core 3.1

不幸的是,对于ASP。NET Core 3.0,情况又有点不同。默认模板使用HostBuilder(而不是WebHostBuilder),它设置了一个新的通用主机,可以托管几个不同的应用程序,而不限于web应用程序。这个新主机的一部分还删除了之前为web主机存在的第二个依赖注入容器。这最终意味着你不能向Startup类中注入除IConfiguration之外的任何依赖项。所以你不能在ConfigureServices方法期间记录日志。然而,你可以将记录器注入Configure方法并在那里记录:

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


// …
}

如果你绝对要在ConfigureServices内使用需要进行日志记录,那么你可以继续使用WebHostBuilder,它将创建遗留的WebHost,该遗留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");


// …
}
}

主要代码:

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注入工作,但这可能是因为它不是一个控制器。更多信息欢迎!

参考文献:

对于。net Core 3.0,官方文档是这样说的:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup

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

  • 不支持记录器注入Startup构造函数。
  • 不支持记录器注入Startup.ConfigureServices方法签名

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

public class StartupLogger
{
private readonly ILogger _logger;


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


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

然后在启动。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]从应用程序代码中调用'BuildServiceProvider'会导致创建一个额外的单例服务副本。考虑将依赖注入服务作为“Configure”的参数等替代方案。

我使用了一个避免第三方记录器实现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())

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

问题在于,直到构建ServiceCollection之后,日志才真正成为一件事,并且它不是在ConfigureServices期间构建的。

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

对我来说有效的解决方案是使用旧的。net Framework NLog方法:

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

将该权利添加到扩展方法类,并且我能够写入日志("the"在ConfigureServices期间和之后。

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

目前的官方解决方案是建立一个本地LoggerFactory,就像这样:

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

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

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

Log.Information("App started.");

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

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


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

这对我很有效

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

使用罗尔夫的回答,我把它放在我的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...
}

我发现了一个非常简单的实现:

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

只要使用控制台。WriteLine就可以了,即使在Docker上也是如此。

您是否正在决定在运行时使用哪些希望记录日志的服务?或者您正在决定如何配置这些服务,您希望记录哪些服务?

换句话说;

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

如果只是后者,你可以将这些lambda函数的实现移动到IConfigureOptions<T>服务中,允许你注入其他服务。继续上面的示例,您可以创建以下内容;

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

如果你的.ConfigureServices方法变得太复杂,你可能想要创建这样的服务。但是,对于每个选项类型,都需要添加大量的样板文件。还有一个等效的简写,将其他服务注入到配置lamda中;

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

对于net 6

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

或者更简单的方法:

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