Startup.cs içinden nasıl günlük yazabilirim?


120

Başlangıçta başarısız olan bir .NET Core uygulamasında hata ayıklamak için, logları startup.cs dosyası içinden yazmak istiyorum. Başlatma.cs dosyasının dışında uygulamanın geri kalanında kullanılabilen dosya içinde günlük kaydı ayarım var, ancak günlükleri startup.cs dosyasının içinden nasıl yazacağımı bilmiyorum.

Yanıtlar:


189

.Net Çekirdek 3.1

Ne yazık ki ASP.NET Core 3.0 için durum yine biraz farklı. Varsayılan şablonlar , web uygulamalarıyla sınırlı olmayan birkaç farklı uygulamayı barındırabilen yeni bir genel ana bilgisayar kuran HostBuilder(yerine WebHostBuilder) öğesini kullanır . Bu yeni ana bilgisayarın bir parçası, daha önce web barındırıcısı için var olan ikinci bağımlılık ekleme kabının kaldırılmasıdır. Bu, sonuçta IConfiguration, Startupsınıfın dışında herhangi bir bağımlılığı enjekte edemeyeceğiniz anlamına gelir . Böylece ConfigureServicesyöntem sırasında oturum açamayacaksınız . Bununla birlikte, kaydediciyi Configureyönteme enjekte edebilir ve orada oturum açabilirsiniz:

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

    // …
}

Kesinlikle giriş yapmanız gerekiyorsa , günlükçüyü sınıfa enjekte edebilecek mirası oluşturacak olanı ConfigureServiceskullanmaya devam edebilirsiniz . Web barındırıcısının gelecekte bir noktada kaldırılmasının muhtemel olduğunu unutmayın. Bu nedenle, oturum açmanıza gerek kalmadan sizin için çalışan bir çözüm bulmaya çalışmalısınız .WebHostBuilderWebHostStartupConfigureServices


.NET Core 2.x

Bu, ASP.NET Core 2.0'ın yayımlanmasıyla önemli ölçüde değişti. ASP.NET Core 2.x'te, ana bilgisayar oluşturucuda günlük oluşturulur. Bu, günlük kaydının varsayılan olarak DI aracılığıyla kullanılabileceği ve Startupsınıfa eklenebileceği anlamına gelir :

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
TEŞEKKÜR EDERİM. Basit sorulara cevap aramak için ne kadar zaman harcayabileceğiniz şaşırtıcı. @poke seçeneklerimin ne olduğunu bana bildirdiğiniz için (tekrar) teşekkür ederim. Bu bilgiyi nereden aldın? Configure'da bir şeyler kaydedebileceğimi doğruladım; bu, göze keskin bir sopayla dürtmeye (cezalandırma amaçlı) tercih edilir, ancak belki de ConfigureServices sırasında yapabildiğim kadar harika değildir. Benim durumumda, env ayarlarına sahip olup olmadığımı, hatta belki bunları günlüğe göndermek istiyorum. Nafile? Ah ... bunun neden bu kadar zor olduğundan emin değilim. Ama en azından bu yazı sayesinde ne yapıp ne yapamayacağımı biliyorum.
Wellspring

3
@ Wellspring 3.0'da, bu "zor" dur çünkü ConfigureServicesçalışmalara kadar kaydedici henüz mevcut değil. Bu nedenle, henüz bir günlük kaydedici olmadığı için bu noktada oturum açamayacaksınız. Artı tarafta, bu size yine de içindeki logger'ı yapılandırma yeteneği verir, ConfigureServicesçünkü hepsi aynı DI kapsayıcıdır (bu aslında iyi bir şeydir). - Bir şeyleri kesinlikle günlüğe kaydetmeniz gerekiyorsa, örneğin bilgileri ayrı olarak (örneğin bir listede) toplayabilir ve ardından kaydedici kullanılabilir olur olmaz oturumu kapatabilirsiniz.
dürtmek

İçinizde .NET 3.1şu anda ConfigureServicesyöntemde geri dönmeden oturum açabilirsiniz WebHostBuilder. Aşağıdaki yanıtı kullanın: stackoverflow.com/a/61488490/2877982
Aage

2
@Aage Bunun birkaç dezavantajı olsa da: Tüm günlük yapılandırmanızı tekrarlamanız gerekecek, günlük yapılandırması uygulama yapılandırmanızı da yansıtmayacaktır (örneğin, uygulama ayarlarında yapılandırılan günlük seviyeleri vb.) Ve genellikle ikinci bir günlük altyapısı kuruyorsunuz. Yine de, orada DI kurulumu sırasında oturum açmaktan nasıl kaçınabileceğiniz bir çözüm aramanızı öneririm.
dürtmek

1
.NET 5 : " Startupana bilgisayarla birlikte başlatılan ek parametreleri ilet " builder.UseStartup(context => new Startup(logger));- docs.microsoft.com/en-us/aspnet/core/release-notes/…
Pang

37

Seçenek 1: Başlangıçta doğrudan günlüğü (örn. Serilog) kullanın

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

Çıktı:

serilog

Serilog'u asp.net-core uygulamasında kurmak için GitHub'daki Serilog.AspNetCore paketine bakın .


Seçenek2: Program.cs'de oturum açmayı şu şekilde yapılandırın :

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

host.Run();

Kullanıcı loggerFactory başlangıçta böyle-

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

Bu bağlantıda tüm ayrıntılar mevcuttur


8

In NET Çekirdek 3.1 , doğrudan LogFactory kullanarak bir logger oluşturabilir.

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

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

Log4net için kaynar ILogger logger = LoggerFactory.Create(builder => builder.AddLog4Net()).CreateLogger<Startup>();. Ancak, yalnızca istisnaları günlüğe kaydederseniz, Windows Olay Günlüğü'nde (IIS kullanırken) zaten görünenden çok daha fazlasını açığa çıkarmayacaktır.
Louis Somers

6

.NET Core 3.0 için resmi dokümanlar şunu söylemelidir: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.0#create-logs-in-startup

Startup.ConfigureServicesYöntemde DI kapsayıcı kurulumunun tamamlanmasından önce günlüklerin yazılması desteklenmez:

  • Yapıcıya kaydedici ekleme Startupdesteklenmez.
  • Startup.ConfigureServicesYöntem imzasına kaydedici enjeksiyonu desteklenmez

Ancak belgelerde dedikleri gibi, ILogger'a bağlı bir hizmeti yapılandırabilirsiniz, bu nedenle bir StartupLogger sınıfı yazarsanız:

public class StartupLogger
{
    private readonly ILogger _logger;

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

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

Ardından Startup.ConfigureServices içinde hizmeti ekleyin, ardından DI kapsayıcısına erişmek için hizmet sağlayıcıyı oluşturmanız gerekir:

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

Düzenleme: Bu, bir derleyici uyarısı oluşturur, Başlangıç ​​sınıfınızda hata ayıklama uğruna bu tamam olmalıdır, ancak üretim için olmamalıdır:

Startup.cs (39, 32): [ASP0000] Uygulama kodundan 'BuildServiceProvider'ın çağrılması, tekli hizmetlerin ek bir kopyasının oluşturulmasına neden oluyor. Bağımlılık enjekte etme hizmetleri gibi alternatifleri 'Yapılandırmak' için parametreler olarak düşünün.



4

ILogger arabirimiyle bir "günlükçü arabelleği" uygulayan 3. taraf kaydedicilerden kaçınan bir çözüm kullanıyorum .

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 dosyasında kullanım kolaydır, tabii ki Configure çağrısından sonra log çıktısı alırsınız. Ama hiç yoktan iyidir. :

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

Ana kod:

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 , varsayılan bir konsol kaydedici ayarlar.

... ILoggerFactory'yi konsola günlüğe kaydetmek ve çıktıda hata ayıklamak için yapılandırır

Başlangıç ​​kodu:

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

Çalışmak için bir ILogger enjeksiyonunu sağlayamadım, ama belki de bunun bir denetleyici olmadığı içindir. Daha fazla bilgi hoş geldiniz!

Referanslar:


0

Mevcut cevapların hiçbiri benim için işe yaramadı. NLog kullanıyorum ve hatta yeni bir ServiceCollection oluşturuyorum, herhangi bir hizmet koleksiyonunda .CreateBuilder () 'ı çağırıyorum, bir günlük kaydı hizmeti oluşturuyorum ... Bunların hiçbiri ConfigureServices sırasında bir günlük dosyasına yazmaz.

Sorun, günlüğe kaydetmenin ServiceCollection oluşturulana kadar gerçekten bir şey olmaması ve ConfigureServices sırasında oluşturulmamasıdır.

Temel olarak, bir yapılandırma uzantısı yönteminde başlatma sırasında neler olup bittiğini günlüğe kaydetmek istiyorum (ihtiyacım var), çünkü sorun yaşadığım tek katman bir hata ayıklayıcı ekleyemediğim PROD.

Benim için çalışan çözüm, eski .NET Framework NLog yöntemini kullanıyordu:

private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

Bu hak uzantı yöntemi sınıfına eklendi ve ConfigureServices sırasında ve sonrasında bir günlüğe ("günlük") yazabildim.

Bunun üretim koduna gerçekten yayılmasının iyi bir fikir olup olmadığı hakkında hiçbir fikrim yok (.NET'in ILogger'ı kontrol edip etmediğini ve bu NLog.ILogger'ın herhangi bir noktada çakışacağını bilmiyorum), ancak sadece ne olduğunu görmek için ihtiyacım vardı üzerinde.


-1

Bunu, dosyada NLog ile statik olarak bir kaydedici oluşturarak ve ardından bunu başlangıç ​​yöntemlerinde kullanarak yapmayı başardım.

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

1
Bu işe yararken, günlük kaydı için ASP.NET Core ve bağımlılık ekleme (DI) - yerleşik veya 3. taraf - ile birlikte gelen standart arabirimleri kullanmanızı öneririm. Arayüzleri kullanarak a) gerekirse sağlanan günlük kaydını değiştirebilir ve b) hizmet olarak ILogger <TController> enjekte ettiğiniz sınıfları (örn. Kontrolörler) test ettiğinizde alay etmeleri daha kolaydır.
Manfred

Bunu aynı şeye kendi cevabımı gönderdikten sonra ilk kez gördüm. @Manfred işe yarayan başka bir yol yok. System.IO.File.Write()Yöntemlerden başka sanırım .
emery.noel

-2

Startup.cs'de oturum açmak için aşağıdaki satırı kullanmanız yeterlidir.

Log.Information("App started.");
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.