Startup.cs内からログを書き込む方法


109

起動時に失敗する.netコアアプリをデバッグするために、startup.csファイル内からログを書き込みたいと思います。私は、startup.csファイル以外のアプリの残りの部分で使用できるファイル内でログ設定を行っていますが、startup.csファイル自体からログを書き込む方法がわかりません。

回答:


175

.Net Core 3.1

残念ながら、ASP.NET Core 3.0では、状況が少し異なります。デフォルトのテンプレートは、HostBuilder(の代わりにWebHostBuilder)を使用して、Webアプリケーションに限定されない、いくつかの異なるアプリケーションをホストできる新しい汎用ホストをセットアップします。この新しいホストの一部は、以前はWebホストに存在していた2番目の依存関係注入コンテナの削除でもあります。これは、最終的にはIConfigurationStartupクラス以外の依存関係を注入できないことを意味します。したがって、ConfigureServicesメソッドの実行中はログに記録できません。ただし、ロガーをConfigureメソッドに挿入してそこに記録することはできます。

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

    // …
}

内で絶対にログを記録する必要がある場合は、クラスにロガーを挿入できるレガシーを作成するをConfigureServices引き続き使用できます。Webホストは将来のある時点で削除される可能性が高いことに注意してください。そのため、にログインしなくても機能する解決策を見つける必要があります。WebHostBuilderWebHostStartupConfigureServices


.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

2
@Wellspring 3.0では、ConfigureServices実行時にロガーがまだ実際には存在しないため、これは「困難」です。そのため、まだロガーがないため、その時点ではログに記録できません。プラス面としては、ロガーはConfigureServicesすべて同じDIコンテナー(実際には良いことです)であるため、ロガーを内で構成する機能を提供します。–どうしてもログを記録する必要がある場合は、たとえば、情報を個別に(リストなどで)収集し、ロガーが利用可能になったらすぐにログアウトできます。
突く

中で.NET 3.1、あなたは現在の中にログインCAN ConfigureServices方法なしに戻って落ちますWebHostBuilder。下記の使用の答え:stackoverflow.com/a/61488490/2877982
Aage

1
@Aageこれにはいくつかの欠点がありますが、完全なロギング構成を繰り返す必要があり、ロギング構成もアプリケーション構成(appsettingsなどで構成されたログレベルなど)を反映しないため、通常は2番目のロギングインフラストラクチャをセットアップします。DIのセットアップ中にログを完全に回避する方法を探すことをお勧めします。
突く

@poke私はこれについて考えていませんでした。本当に、私はカスタム依存関係を登録するだけでなく、明示的なコントローラーの構築を自分の中に配線したいStartup.csので(依存関係を忘れるとコンパイラエラーが発生します)。したがって、これらのロガーを解決する必要があります。しかし、これは少しハックかもしれません、はい。
年齢は

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

出力:

セリログ

asp.net-coreアプリケーションでSerilogをセットアップするには、GitHubのSerilog.AspNetCoreパッケージを確認してください。


Option2:このように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.");
    }
}

このリンクで利用可能な完全な詳細


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

4

.NET Core 3.0の場合、公式ドキュメントには次のように記載されています。https//docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/? view = aspnetcore-3.0#create-logs-in-startup

Startup.ConfigureServicesメソッドでDIコンテナーのセットアップが完了する前にログを書き込むことはサポートされていません。

  • 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'.

3

.net core 3.1によると、LogFactoryを使用してロガーを直接作成できます。

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

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

3

公式の解決策は現在、次のようにローカルの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


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以降のログ( "the"ログ)に書き込むことができました。

これが実際に製品コードにリリースするのが良いアイデアかどうかはわかりません(.NET制御のILoggerとこのNLog.ILoggerがいつ競合するかはわかりません)。オン。


-1

私は、ファイルにNlogを含むロガーを静的に作成することによってこれを行うことができ、起動メソッド内でこれを使用します。

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

1
これは機能しますが、ログには、ASP.NET Coreに付属する標準インターフェイスと、組み込みまたはサードパーティの依存関係注入(DI)を使用することをお勧めします。インターフェイスを使用することで、a)必要に応じて提供されたログを置き換えることができます。b)ILogger <TController>をサービスとして挿入するクラス(コントローラーなど)をテストするときに、モックが簡単になります。
マンフレッド、

私はまったく同じことについて自分の答えを投稿した後、これを初めて見ました。@Manfredが機能する別の方法はありません。System.IO.File.Write()メソッド以外だと思います。
emery.noel

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.