为了调试启动失败的.net核心应用程序,我想从startup.cs文件中编写日志。我在文件中有日志设置,可以在startup.cs文件之外的应用程序的其余部分中使用,但不知道如何从startup.cs文件本身写入日志。
答案 0 :(得分:55)
随着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");
// …
}
}
答案 1 :(得分:23)
选项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,请查看Serilog.AspNetCore package on GitHub。
选项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.");
}
}
有关此link
的详细信息答案 2 :(得分:9)
在.NET Core 3.1中,您可以直接使用LogFactory创建记录器。
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
答案 3 :(得分:6)
当前的官方解决方案是像这样设置本地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
答案 4 :(得分: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())
答案 5 :(得分:1)
对于.NET Core 3.0,官方文档应这样说:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-the-startup-class
不支持在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);
}
}
然后在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'.
答案 6 :(得分:0)
我已经设法通过在文件中静态创建一个带有Nlog的记录器来完成此操作,然后在启动方法中使用它。
private readonly NLog.Logger _logger = new NLog.LogFactory().GetCurrentClassLogger();
答案 7 :(得分:0)
主要代码:
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,但也许这是因为它不是控制器。更多信息欢迎!
参考文献:
答案 8 :(得分: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是否会在任何时候发生冲突),但是我只需要它来查看发生了什么事。
答案 9 :(得分:0)
使用 Rolf's answer,我把它放在我的启动构造函数中:
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...
}
答案 10 :(得分:-1)
这对我有用
private static readonly Logger logger = LogManager.GetLogger("Audit")
答案 11 :(得分:-2)
只需使用以下行登录Startup.cs
x <- seq_along(dat)
foo <- function(x, lwr, upr) {
y <- x
y[x <= lwr | x > upr] <- mean(dat[x <= lwr | x > upr])
y[x > lwr & x <= upr] <- mean(dat[x > lwr & x <= upr])
y
}
SSE <- function(lwr, upr) {
sum((dat - foo(x, lwr, upr))^ 2)
}
limits <- expand.grid(lwr = x, upr = x)
limits <- limits[limits$lwr <= limits$upr,]
nrow(limits)
SSEvals <- mapply(SSE, limits$lwr, limits$upr)
id <- which(SSEvals == min(SSEvals))
optlims <- limits[id,]
meanouter <- mean(dat[x <= optlims$lwr | x > optlims$upr])
meaninner <- mean(dat[x > optlims$lwr & x <= optlims$upr])
bar <- function(x) {
y <- x
y[x <= optlims$lwr | x > optlims$upr] <- meanouter
y[x > optlims$lwr & x <= optlims$upr] <- meaninner
y
}
plot(dat/100)
curve(bar(x) / 100, add = TRUE)