Làm cách nào để viết nhật ký từ trong Startup.cs


118

Để gỡ lỗi một ứng dụng lõi .net không thành công khi khởi động, tôi muốn ghi nhật ký từ trong tệp startup.cs. Tôi có thiết lập ghi nhật ký trong tệp có thể được sử dụng trong phần còn lại của ứng dụng bên ngoài tệp startup.cs, nhưng không chắc chắn về cách ghi nhật ký từ chính tệp startup.cs.

Câu trả lời:


185

.Net Core 3.1

Thật không may, đối với ASP.NET Core 3.0, tình hình lại khác một chút. Các mẫu mặc định sử dụng HostBuilder(thay vì WebHostBuilder) để thiết lập một máy chủ lưu trữ chung mới có thể lưu trữ một số ứng dụng khác nhau, không giới hạn ở các ứng dụng web. Một phần của máy chủ mới này cũng là việc loại bỏ vùng chứa chèn phụ thuộc thứ hai đã tồn tại trước đó cho máy chủ web. Điều này cuối cùng có nghĩa là bạn sẽ không thể đưa bất kỳ phụ thuộc nào ngoài lớp IConfigurationvào Startuplớp. Vì vậy, bạn sẽ không thể đăng nhập trong quá trình ConfigureServicesnày. Tuy nhiên, bạn có thể đưa trình ghi nhật ký vào Configurephương thức và ghi vào đó:

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

    // …
}

Nếu bạn thực sự cần đăng nhập ConfigureServices, thì bạn có thể tiếp tục sử dụng WebHostBuildercái sẽ tạo ra kế thừa WebHostcó thể đưa trình ghi nhật ký vào Startuplớp. Lưu ý rằng có khả năng máy chủ web sẽ bị xóa vào một thời điểm nào đó trong tương lai. Vì vậy, bạn nên cố gắng tìm một giải pháp phù hợp với mình mà không cần phải đăng nhập ConfigureServices.


.NET Core 2.x

Điều này đã thay đổi đáng kể với việc phát hành ASP.NET Core 2.0. Trong ASP.NET Core 2.x, ghi nhật ký được tạo tại trình tạo máy chủ. Điều này có nghĩa là ghi nhật ký có sẵn thông qua DI theo mặc định và có thể được đưa vào Startuplớp:

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

        // …
    }
}

4
CẢM ƠN BẠN. Thật đáng kinh ngạc khi bạn có thể đốt cháy bao nhiêu thời gian để tìm kiếm câu trả lời cho những câu hỏi đơn giản. @poke cảm ơn bạn (một lần nữa) vì đã thông báo cho tôi những lựa chọn của tôi. Nơi mà bạn đã có được thông tin này? Tôi đã xác nhận rằng tôi có thể đăng nhập mọi thứ trong Cấu hình, điều này tốt hơn là chọc (dự định chơi chữ) vào mắt bằng một cây nhọn nhưng có lẽ không tuyệt vời bằng việc có thể thực hiện trong ConfigureServices. Trong trường hợp của tôi, tôi muốn ghi lại cho dù tôi có cài đặt env hay không, thậm chí có thể đăng chúng vào nhật ký. Không có con xúc xắc? Haizz ... không biết tại sao lại khó như vậy. Nhưng ít nhất, nhờ bài viết này, tôi biết mình có thể và không thể làm gì.
Wellspring

3
@Wellspring Trong 3.0, nó là "khó" vì vào thời điểm ConfigureServiceschạy, trình ghi nhật ký vẫn chưa thực sự tồn tại. Vì vậy, bạn sẽ không thể đăng nhập vào thời điểm đó chỉ đơn giản là vì chưa có trình ghi nhật ký. Về mặt tích cực, điều đó vẫn cung cấp cho bạn khả năng định cấu hình trình ghi nhật ký trong ConfigureServicesvì tất cả đều là cùng một vùng chứa DI (thực sự là một điều tốt). - Nếu bạn thực sự cần đăng nhập mọi thứ, ví dụ, bạn có thể thu thập thông tin một cách riêng biệt (ví dụ như trong danh sách) và sau đó đăng xuất ngay khi có trình ghi nhật ký.
poke

Bên trong .NET 3.1bạn hiện tại CÓ THỂ đăng nhập vào ConfigureServicesphương thức mà không cần quay lại WebHostBuilder. Sử dụng câu trả lời bên dưới: stackoverflow.com/a/61488490/2877982
Aage

2
@Aage Tuy nhiên, điều này sẽ có một số nhược điểm: Bạn sẽ phải lặp lại cấu hình ghi nhật ký đầy đủ của mình, cấu hình ghi nhật ký cũng sẽ không phản ánh cấu hình ứng dụng của bạn (ví dụ: cấp nhật ký được định cấu hình trong cài đặt ứng dụng, v.v.) và bạn thường đang thiết lập cơ sở hạ tầng ghi nhật ký thứ hai. Tôi vẫn khuyên bạn nên tìm giải pháp để tránh ghi nhật ký trong quá trình thiết lập DI hoàn toàn ở đó.
poke

@poke Tôi đã không nghĩ về điều này. Thực sự tôi chỉ muốn xây dựng bộ điều khiển rõ ràng có dây trong của tôi Startup.cs(vì vậy tôi gặp lỗi trình biên dịch khi quên một phụ thuộc) thay vì chỉ đăng ký các phụ thuộc tùy chỉnh. Do đó tôi cần phải giải quyết các log này. Nhưng điều này có thể hơi khó hiểu, vâng.
Aage

37

Tùy chọn 1: Trực tiếp sử dụng nhật ký (ví dụ: Serilog) trong khởi động-

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

Đầu ra:

serilog

Để thiết lập Serilog trong ứng dụng asp.net-core, hãy xem gói Serilog.AspNetCore trên GitHub .


Option2: Định cấu hình đăng nhập program.cs như thế này-

var host = new WebHostBuilder()
            .UseKestrel()
            .ConfigureServices(s => {
                s.AddSingleton<IFormatter, LowercaseFormatter>();
            })
            .ConfigureLogging(f => f.AddConsole(LogLevel.Debug))
            .UseStartup<Startup>()
            .Build();

host.Run();

User logFactory khi khởi động như thế này-

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

Toàn bộ chi tiết có sẵn trên liên kết này


7

Theo .net core 3.1 , Bạn có thể tạo một trình ghi nhật ký trực tiếp bằng LogFactory.

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

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

Đối với log4net, nó tổng hợp thành ILogger logger = LoggerFactory.Create(builder => builder.AddLog4Net()).CreateLogger<Startup>();. Tuy nhiên, nếu bạn chỉ ghi nhật ký các ngoại lệ, nó sẽ không tiết lộ nhiều hơn những gì đã hiển thị trong Windows EventLog (khi sử dụng IIS).
Louis Somers

6

Đối với .NET Core 3.0, tài liệu chính thức có nội dung này: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup

Việc ghi nhật ký trước khi hoàn thành thiết lập vùng chứa DI trong Startup.ConfigureServicesphương thức không được hỗ trợ:

  • StartupKhông hỗ trợ chèn trình ghi nhật ký vào phương thức khởi tạo.
  • Chèn trình ghi nhật Startup.ConfigureServiceský vào chữ ký phương thức không được hỗ trợ

Nhưng như họ nói trong tài liệu, bạn có thể định cấu hình một dịch vụ phụ thuộc vào ILogger, vì vậy nếu bạn đã viết một lớp StartupLogger:

public class StartupLogger
{
    private readonly ILogger _logger;

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

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

Sau đó, trong Startup.ConfigureServices thêm dịch vụ, sau đó bạn cần xây dựng nhà cung cấp dịch vụ để có quyền truy cập vào vùng chứa 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");
}

Chỉnh sửa: điều này tạo ra một cảnh báo trình biên dịch, vì lợi ích của việc gỡ lỗi lớp StartUp của bạn, điều này sẽ ổn nhưng không phải cho sản xuất:

Startup.cs (39, 32): [ASP0000] Gọi 'BuildServiceProvider' từ mã ứng dụng dẫn đến một bản sao bổ sung của các dịch vụ singleton được tạo. Hãy xem xét các lựa chọn thay thế như các dịch vụ tiêm phụ thuộc làm tham số để 'Định cấu hình'.



4

Tôi sử dụng một giải pháp tránh trình ghi bên thứ 3 triển khai "bộ đệm ghi nhật ký" với giao diện 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();
    }
}

Cách sử dụng trong startup.cs rất dễ dàng, tất nhiên bạn sẽ nhận được đầu ra nhật ký sau khi gọi Cấu hình. Nhưng còn hơn không. :

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

1

Mã chính:

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 thiết lập trình ghi bảng điều khiển mặc định.

... cấu hình ILoggerFactory để đăng nhập vào bảng điều khiển và gỡ lỗi đầu ra

Mã khởi động:

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

Tôi không thể tiêm ILogger hoạt động, nhưng có lẽ đó là vì nó không phải là bộ điều khiển. Thêm thông tin chào mừng!

Refs:


0

Không có câu trả lời nào ở trên phù hợp với tôi. Tôi đang sử dụng NLog và thậm chí đang xây dựng ServiceCollection mới, gọi .CreateBuilder () trên bất kỳ bộ sưu tập dịch vụ nào, tạo dịch vụ ghi nhật ký ... không cái nào trong số đó sẽ ghi vào tệp nhật ký trong ConfigureServices.

Vấn đề là việc ghi nhật ký thực sự không thành vấn đề cho đến khi ServiceCollection được xây dựng và nó không được xây dựng trong ConfigureServices.

Về cơ bản, tôi chỉ muốn (cần) ghi lại những gì đang diễn ra trong quá trình khởi động trong một phương pháp mở rộng cấu hình, bởi vì cấp duy nhất mà tôi gặp sự cố là PROD, nơi tôi không thể đính kèm trình gỡ lỗi.

Giải pháp phù hợp với tôi là sử dụng phương pháp .NET Framework NLog cũ: private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); Đã thêm quyền đó vào lớp phương thức mở rộng và tôi có thể ghi vào nhật ký ("nhật ký") trong suốt ConfigureServices và sau đó.

Tôi không biết liệu đây có phải là một ý tưởng hay nếu thực sự phát hành vào mã sản xuất (Tôi không biết liệu ILogger có kiểm soát .NET và NLog.ILogger này có xung đột vào bất kỳ lúc nào hay không), nhưng tôi chỉ cần nó để xem điều gì đang xảy ra. trên.


-1

Tôi đã quản lý để làm điều này bằng cách tạo tĩnh một trình ghi nhật ký với Nlog trong tệp, sau đó sử dụng điều này trong các phương pháp khởi động.

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

1
Trong khi điều này hoạt động, tôi khuyên bạn nên sử dụng các giao diện tiêu chuẩn đi kèm với ASP.NET Core và chèn phụ thuộc (DI) - được tích hợp sẵn hoặc bên thứ 3 - để ghi nhật ký. Bằng cách sử dụng các giao diện, bạn có thể a) thay thế nhật ký được cung cấp nếu cần và b) chúng dễ bị bắt chước hơn khi bạn kiểm tra các lớp (ví dụ: bộ điều khiển) nơi bạn đưa ILogger <TController> làm dịch vụ.
Manfred

Tôi chỉ thấy điều này lần đầu tiên sau khi đăng câu trả lời của chính tôi về điều tương tự. @Manfred không có cách nào khác hoạt động. Tôi đoán khác với System.IO.File.Write()các phương pháp.
emery.noel

-2

Chỉ cần sử dụng dòng bên dưới để đăng nhập Startup.cs

Log.Information("App started.");
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.