ในการดีบักแอพ. NET Core ซึ่งล้มเหลวในการเริ่มต้นฉันต้องการเขียนบันทึกจากภายในไฟล์ startup.cs ฉันมีการตั้งค่าการบันทึกภายในไฟล์ที่สามารถใช้ในส่วนที่เหลือของแอพนอกไฟล์ startup.cs แต่ไม่แน่ใจว่าจะเขียนบันทึกจากภายในไฟล์ startup.cs ได้อย่างไร
ในการดีบักแอพ. NET Core ซึ่งล้มเหลวในการเริ่มต้นฉันต้องการเขียนบันทึกจากภายในไฟล์ startup.cs ฉันมีการตั้งค่าการบันทึกภายในไฟล์ที่สามารถใช้ในส่วนที่เหลือของแอพนอกไฟล์ startup.cs แต่ไม่แน่ใจว่าจะเขียนบันทึกจากภายในไฟล์ startup.cs ได้อย่างไร
คำตอบ:
.Net Core 3.1
น่าเสียดายสำหรับ ASP.NET Core 3.0 สถานการณ์แตกต่างไปจากเดิมเล็กน้อย เทมเพลตเริ่มต้นใช้HostBuilder
(แทนWebHostBuilder
) ซึ่งตั้งค่าโฮสต์ทั่วไปใหม่ที่สามารถโฮสต์แอปพลิเคชันต่างๆได้ไม่ จำกัด เฉพาะเว็บแอปพลิเคชัน ส่วนหนึ่งของโฮสต์ใหม่นี้ยังเป็นการลบคอนเทนเนอร์การฉีดพึ่งพาที่สองซึ่งก่อนหน้านี้มีอยู่สำหรับโฮสต์เว็บ ในที่สุดหมายความว่าคุณจะไม่สามารถแทรกการอ้างอิงใด ๆ นอกเหนือจากIConfiguration
ในStartup
คลาสได้ ดังนั้นคุณจะไม่สามารถเข้าสู่ระบบระหว่างConfigureServices
วิธีการได้ อย่างไรก็ตามคุณสามารถฉีดคนตัดไม้ลงในConfigure
วิธีการและเข้าสู่ระบบที่นั่น:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
logger.LogInformation("Configure called");
// …
}
หากคุณจำเป็นต้องเข้าสู่ระบบอย่างแท้จริงConfigureServices
คุณสามารถใช้สิ่งนี้ต่อไปWebHostBuilder
ซึ่งจะสร้างมรดกWebHost
ที่สามารถฉีดคนตัดไม้ลงในStartup
ชั้นเรียนได้ โปรดทราบว่ามีแนวโน้มว่าโฮสต์เว็บจะถูกลบออกในอนาคต 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
ที่เริ่มต้นพร้อมกับโฮสต์" builder.UseStartup(context => new Startup(logger));
- docs.microsoft.com/en-us/aspnet/core/release-notes/…
ตัวเลือกที่ 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");
....
}
เอาท์พุต:
การติดตั้ง Serilog ในการประยุกต์ใช้ asp.net-core ตรวจสอบแพคเกจ Serilog.AspNetCore บน 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.");
}
}
รายละเอียดที่สมบูรณ์มีอยู่ในลิงค์นี้
ใน. NET Core 3.1คุณสามารถสร้างคนตัดไม้ได้โดยตรงโดยใช้ LogFactory
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Startup>();
logger.LogInformation("Example log message");
ILogger logger = LoggerFactory.Create(builder => builder.AddLog4Net()).CreateLogger<Startup>();
. อย่างไรก็ตามหากคุณบันทึกเฉพาะข้อยกเว้นก็จะไม่เปิดเผยมากไปกว่าสิ่งที่ปรากฏใน Windows EventLog (เมื่อใช้ IIS)
ขณะนี้วิธีแก้ปัญหาอย่างเป็นทางการคือการตั้งค่า 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
สำหรับ. NET Core 3.0 เอกสารอย่างเป็นทางการกล่าวถึง: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup
ไม่รองรับการเขียนบันทึกก่อนเสร็จสิ้นการตั้งค่าคอนเทนเนอร์ DI ใน
Startup.ConfigureServices
วิธีนี้:
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] การเรียก 'BuildServiceProvider' จากรหัสแอปพลิเคชันส่งผลให้มีการสร้างสำเนาเพิ่มเติมของบริการซิงเกิลตัน พิจารณาทางเลือกอื่น ๆ เช่นบริการการฉีดการพึ่งพาเป็นพารามิเตอร์เพื่อ 'กำหนดค่า'
ฉันใช้วิธีหลีกเลี่ยงไม่ให้ผู้ตัดไม้บุคคลที่สามใช้ "บัฟเฟอร์คนตัดไม้" กับอินเทอร์เฟซของ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" log) ระหว่าง ConfigureServices และ after
ฉันไม่รู้ว่านี่เป็นความคิดที่ดีที่จะปล่อยลงในรหัสการผลิตจริง ๆ หรือไม่ (ฉันไม่รู้ว่า ILogger ที่ควบคุม. NET และ NLog นี้จะขัดแย้งกันหรือไม่) แต่ฉันต้องการเพียงเพื่อดูว่าเกิดอะไรขึ้น บน.
รหัสหลัก:
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 ให้ทำงานได้ แต่อาจเป็นเพราะมันไม่ใช่ตัวควบคุม ยินดีต้อนรับข้อมูลเพิ่มเติม!
อ้างถึง:
สิ่งนี้ได้ผลสำหรับฉัน
private static readonly Logger logger = LogManager.GetLogger("Audit")
เพียงใช้บรรทัดด้านล่างเพื่อเข้าสู่ระบบ Startup.cs
Log.Information("App started.");
ฉันจัดการได้โดยการสร้างตัวบันทึกแบบคงที่ด้วย NLog ในไฟล์จากนั้นใช้สิ่งนี้ภายในวิธีการเริ่มต้น
private readonly NLog.Logger _logger = new NLog.LogFactory().GetCurrentClassLogger();
System.IO.File.Write()
วิธีการ