为了调试启动失败的.net核心应用程序,我想从startup.cs文件中写入日志。我在该文件中有日志记录设置,该文件可在startup.cs文件之外的其他应用程序中使用,但是不确定如何从startup.cs文件本身内写入日志。
Answers:
.Net Core 3.1
不幸的是,对于ASP.NET Core 3.0,情况再次有所不同。默认模板使用HostBuilder
(而不是WebHostBuilder
)来设置新的通用主机,该主机可以承载多个不同的应用程序,而不仅限于Web应用程序。此新主机的一部分还包括删除先前为Web主机存在的第二个依赖项注入容器。最终,这意味着您将无法将任何依赖项注入IConfiguration
到Startup
类之外。因此,您将无法在该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");
// …
}
}
ConfigureServices
运行时,记录器实际上还不存在。这样一来,您将无法仅由于尚无记录器而登录。从ConfigureServices
好的方面来说,这仍然使您能够在内配置记录器,因为它都是相同的DI容器(这实际上是一件好事)。–如果绝对需要记录内容,则可以例如单独收集信息(例如,在列表中),然后在记录仪可用时将其注销。
.NET 3.1
您目前可以登录内ConfigureServices
方法没有回落的WebHostBuilder
。请在下面使用答案:stackoverflow.com/a/61488490/2877982
Startup.cs
(这样,在忘记依赖项时会出现编译器错误),而不是仅注册自定义依赖项。因此,我需要解决这些记录器。但这可能有点hacky,是的。
选项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.");
}
}
此链接上提供了完整的详细信息
我使用一种避免第三方记录器通过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())
对于.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'.
根据.net core 3.1,您可以直接使用LogFactory创建记录器。
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
当前的官方解决方案是像这样设置本地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
主要代码:
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来工作,但这也许是因为它不是控制器。欢迎更多信息!
参考:
上面的答案对我都不起作用。我正在使用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是否会在任何时候发生冲突),但是我只需要它来查看发生了什么上。
我设法通过在文件中使用Nlog静态创建一个记录器,然后在启动方法中使用它来做到这一点。
private readonly NLog.Logger _logger = new NLog.LogFactory().GetCurrentClassLogger();
System.IO.File.Write()
方法。
只需使用下面的行登录Startup.cs
Log.Information("App started.");