Як писати журнали в Startup.cs


117

Для налагодження основного додатка .net, який не працює під час запуску, я хотів би писати журнали з файлу startup.cs. Я налаштував ведення журналу у файлі, який можна використовувати в решті програми поза файлом startup.cs, але не знаю, як писати журнали з самого файлу startup.cs.

Відповіді:


183

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

        // …
    }
}

4
ДЯКУЮ. Дивно, скільки часу ви можете спалити, шукаючи відповіді на прості запитання. @poke дякую (ще раз), що повідомили мені, які у мене є варіанти. Де ви взяли цю інформацію? Я підтвердив, що можу реєструвати речі в Configure, що є кращим, ніж тикання (каламбур) в око гострим палицею, але, можливо, не настільки дивовижне, як можливість під час ConfigureServices. У моєму випадку я хотів би зареєструвати, чи не отримав я налаштування env, можливо, навіть опублікувати їх у журналі. Немає кісток? Зітхання ... не впевнений, чому це має бути так важко. Але принаймні, завдяки цій публікації я знаю, що можу, а що не можу робити.
Wellspring

3
@Wellspring У версії 3.0 це «важко», оскільки до моменту ConfigureServicesзапуску реєстратор насправді ще не існує. Отже, ви не зможете ввійти в цей момент просто тому, що ще немає реєстратора. Позитивом є те, що це все ще дає вам можливість налаштувати реєстратор всередині, ConfigureServicesоскільки це все той же контейнер DI (що насправді добре). - Якщо вам абсолютно потрібно зареєструвати матеріали, ви можете, наприклад, зібрати інформацію окремо (наприклад, у списку), а потім вийти з неї, як тільки реєстратор стане доступним.
пхати

В .NET 3.1даний час ви МОЖЕТЕ входити до ConfigureServicesметоду, не повертаючись до WebHostBuilder. Використовуйте відповідь нижче: stackoverflow.com/a/61488490/2877982
Aage

2
@Aage У цього буде кілька недоліків: Вам доведеться повторити повну конфігурацію ведення журналу, конфігурація реєстрації також не відображатиме конфігурацію вашого додатка (наприклад, рівні журналів, налаштовані в налаштуваннях додатків тощо), і ви, як правило, налаштовуєте другу інфраструктуру реєстрації. Я все одно пропоную вам шукати рішення, як уникнути реєстрації під час налаштування DI там.
тикати

@poke Я не думав про це. Дійсно, я просто хочу, щоб явна конструкція контролера була підключена до мого Startup.cs(тому я отримую помилки компілятора, забувши залежність), а не лише реєструвати власні залежності. Тому мені потрібно вирішити ці реєстратори. Але це може бути трохи хакі, так.
Aage

37

Варіант 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();

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

Повна інформація доступна за цим посиланням


7

Відповідно до .net core 3.1 , ви можете створити реєстратор безпосередньо за допомогою LogFactory.

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

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

Для log4net це зводиться до ILogger logger = LoggerFactory.Create(builder => builder.AddLog4Net()).CreateLogger<Startup>();. Однак якщо ви реєструєте лише винятки з журналу, це не виявить набагато більше, ніж те, що вже відображається в Журналі подій Windows (при використанні IIS).
Луї Сомерс,

6

Для .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конструктор не підтримується.
  • Введення реєстратора в 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] Виклик 'BuildServiceProvider' із коду програми призводить до створення додаткової копії служб-одиночок. Розглянемо альтернативи, такі як служби ін’єкцій залежності, як параметри для «Налаштування».


5

В даний час офіційним рішенням є встановлення локальної 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

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

1

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

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 працювати, але, можливо, це тому, що це не контролер. Більше інформації ласкаво просимо!

Посилання:


0

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

Проблема полягає в тому, що ведення журналу насправді не є предметом, доки не буде побудовано ServiceCollection, і воно не створюється під час ConfigureServices.

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

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

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


-1

Мені вдалося це зробити, статично створивши реєстратор з Nlog у файлі, а потім використати це в методах запуску.

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

1
Поки це працює, я рекомендую використовувати для реєстрації стандартні інтерфейси, що постачаються з ASP.NET Core та ін'єкцією залежностей (DI) - або вбудованою, або сторонньою. Використовуючи інтерфейси, ви можете а) замінити наданий журнал, якщо це необхідно, і б) їх легше знущатись, коли ви тестуєте класи (наприклад, контролери), де ви вводите ILogger <TController> як послугу.
Манфред

Я щойно побачив це вперше, розмістивши власну відповідь на те саме. @Manfred просто не існує іншого способу, який працює. Я думаю, крім System.IO.File.Write()методів.
emery.noel

-2

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

Log.Information("App started.");
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.