Как мне писать логи из Startup.cs

Для отладки основного приложения.net, которое не запускается при запуске, я бы хотел записывать логи из файла startup.cs. У меня есть настройки ведения журнала в файле, который можно использовать в остальной части приложения вне файла startup.cs, но я не уверен, как записывать логи из самого файла startup.cs.

15 ответов

Решение

Это значительно изменилось с выпуском 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: напрямую использовать журнал (например, 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

Чтобы настроить 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");

Официальное решение в настоящее время - настроить локальный 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

Для .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;

Используя ответ Рольфса, я поместил это в свой Startup ctor:

      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...
}

Ни один из приведенных выше ответов не помог мне. Я использую NLog и даже создаю новую коллекцию ServiceCollection, вызывая.CreateBuilder() для любой коллекции служб, создавая службу ведения журнала... ничего из этого не будет записывать в файл журнала во время ConfigureServices.

Проблема в том, что ведение журнала на самом деле не происходит до тех пор, пока ServiceCollection не построена, и она не создается во время ConfigureServices.

В основном я просто хочу (мне нужно) регистрировать, что происходит во время запуска, в методе расширения конфигурации, потому что единственный уровень, на котором у меня возникают проблемы, - это PROD, где я не могу подключить отладчик.

Решение, которое сработало для меня, заключалось в использовании старого метода.NET Framework NLog:private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();Это право добавлено в класс метода расширения, и я смог записывать в журнал ("журнал") во время ConfigureServices и после.

Я понятия не имею, действительно ли это хорошая идея для выпуска в производственный код (я не знаю, будут ли ILogger, управляемый.NET, и этот NLog.ILogger конфликтовать в какой-либо момент), но мне это нужно было только для того, чтобы увидеть, что происходит на.

Для.NET Core 3.0 в официальных документах есть следующее: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0

Запись журналов до завершения настройки контейнера DI в Startup.ConfigureServices метод не поддерживается:

  • Инъекция логгера в 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'.

Я использую решение, избегающее сторонних регистраторов, реализующих "буфер регистратора" с интерфейсом 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())

Основной код:

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

Я не мог заставить работать ILogger, но, возможно, это потому, что это не контроллер. Больше информации добро пожаловать!

Refs:

Принимаете ли вы решения о том, какие службы вы используете во время выполнения, которые хотите регистрировать? Или вы принимаете решения о том, как настроены те сервисы, которые вы хотите регистрировать?

Другими словами;

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

Если это только последнее, вы можете перенести реализацию этих лямбда-функций в 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метод становится слишком сложным, возможно, вы захотите создать такие сервисы. Тем не менее, для каждого типа опций нужно добавить много шаблонов. Существует также эквивалентное сокращение для внедрения других сервисов в конфигурационную лямбду;

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

Это сработало для меня

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

Мне удалось сделать это, статически создавая регистратор с Nlog в файле, а затем использовать это в методах запуска.

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

Я нашел очень простую реализацию:

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

Простое использование Console.WriteLine работало даже в Docker.

Просто используйте строку ниже для входа в Startup.cs

Log.Information("App started.");
Другие вопросы по тегам