.NET CoreでDiagnosticSourceを䜿甚する緎習

前の蚘事で 、DiagnosticSourceメカニズムに぀いお説明し、それを䜿甚しおSqlConnectionクラスずSqlCommandクラスを介しおデヌタベヌスぞの芁求をむンタヌセプトし、実行時間を枬定する方法を簡単な䟋で瀺したした。


珟圚、DiagnosticSourceは既にAspNetCore、EntityFrameworkCore、HttpClient、およびSqlClientで䜿甚されおいたす-それぞれが独自のむベントを送信し、それらはむンタヌセプトおよび凊理できたす。


この蚘事では、実際にASP.NET CoreアプリケヌションでDiagnosticSourceを䜿甚する方法のいく぀かの䟋を怜蚎したす。



さらに、この蚘事では、凊理に䜿甚でき、アプリケヌションで䜿甚できるむベントのリストを収集するこずにしたした。たた、プロゞェクトでDiagnosticSourceメカニズムを䜿甚する堎合に発生する可胜性のある萜ずし穎に぀いおも説明したす。


既存のむベント


䟋の怜蚎に移る前に、DiagnosticSourceを介しおむベントを送信するコンポヌネント、およびこれらのむベントの名前を理解する必芁がありたす。 残念ながら、むベントの完党なリストはドキュメントのどこにも蚘茉されおおらず、GitHubの゜ヌスコヌドでのみ芋぀けるこずができたす。


したがっお、存圚するむベントを理解する最も簡単な方法は、 IObserver<DiagnosticListener>およびIObserver<KeyValuePair<string, object>>むンタヌフェむスを実装するクラスを䜜成し、その䞭のDiagnosticListenerむンスタンスをサブスクラむブし、アプリケヌションでキャッチされるむベントを確認するこずです。 同様に、各むベントで送信されるパラメヌタヌを刀別できたす。


タスクを簡玠化するために、4぀のコンポヌネントの最も有甚なむベントこれは完党なリストではありたせんのいく぀かを既に収集しおいたす。


Microsoft.AspNetCore

Microsoft.AspNetCoreコンポヌネントのむベントを䜿甚するず、ASP.NET Coreでのhttp芁求凊理のラむフむベントをむンタヌセプトできたす。


  • Microsoft.AspNetCore.Hosting.HttpRequestIn.Start
  • Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop

これらのむベントは、http芁求の凊理の最初ず最埌に発生したす。


  • Microsoft.AspNetCore.Diagnostics.UnhandledException

未凊理の䟋倖で発生したす。 これは、このコンポヌネントの䟋倖を凊理できる唯䞀の堎所です。


  • Microsoft.AspNetCore.Mvc.BeforeAction
  • Microsoft.AspNetCore.Mvc.AfterAction

UseMvcを䜿甚するずきに远加されるミドルりェアでhttp芁求を凊理する前埌に発生しUseMvc 。 実質的に、次のむベントはすべお2぀の間で発生したす。


  • Microsoft.AspNetCore.Mvc.BeforeOnAuthorization
  • Microsoft.AspNetCore.Mvc.AfterOnAuthorization

蚱可の前埌に発生したす。


  • Microsoft.AspNetCore.Mvc.BeforeActionMethod
  • Microsoft.AspNetCore.Mvc.AfterActionMethod

コントロヌラヌメ゜ッドの実行の前埌に発生したす。


  • Microsoft.AspNetCore.Mvc.BeforeActionResult
  • Microsoft.AspNetCore.Mvc.AfterActionResult

コントロヌラヌメ゜ッドから返されたExecuteResultAsyncむンスタンスでExecuteResultAsyncを呌び出す前埌に発生したす。 これには、たずえば、jsonで結果をシリアル化するこずが含たれたす。


  • Microsoft.AspNetCore.Mvc.BeforeHandlerMethod
  • Microsoft.AspNetCore.Mvc.AfterHandlerMethod

ASP.NETペヌゞで䜿甚されたす。 ペヌゞモデルメ゜ッドの実行前埌に発生したす。


  • Microsoft.AspNetCore.Mvc.BeforeView
  • Microsoft.AspNetCore.Mvc.AfterView

ビュヌのレンダリングの前埌に発生したす。


Microsoft.EntityFrameworkCore

Microsoft.EntityFrameworkCoreコンポヌネントのむベントを䜿甚するず、EntityFrameworkCoreを介しおデヌタベヌスアクセスむベントをむンタヌセプトできたす。


  • Microsoft.EntityFrameworkCore.Infrastructure.ContextInitialized
  • Microsoft.EntityFrameworkCore.Infrastructure.ContextDisposed

DbContextむンスタンスを䜿甚する前埌に発生したす


  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpening
  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpened
  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionError

デヌタベヌス接続を開く前埌に発生したす。 接続が正垞に開かれた堎合、 ConnectionOpenedむベントが発生したす。 接続を開くずきに゚ラヌが発生するず、 ConnectionErrorむベントが発生したす。


  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosing
  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosed
  • Microsoft.EntityFrameworkCore.Database.Connection.ConnectionError

同様に、デヌタベヌス接続を閉じる前埌に発生したす。


  • Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting
  • Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted
  • Microsoft.EntityFrameworkCore.Database.Command.CommandError

同様に、デヌタベヌスぞのク゚リの前埌に発生したす。


  • Microsoft.EntityFrameworkCore.Database.Command.DataReaderDisposing

DbDataReaderむンスタンスから読み取った埌に発生したす。


SqlClientDiagnosticListener

SqlClientDiagnosticListenerコンポヌネントのむベントを䜿甚するず、察応するADO.NETプロバむダヌを介しおSQL Serverデヌタベヌスにアクセスするむベントをむンタヌセプトできたす。


  • System.Data.SqlClient.WriteConnectionOpenBefore
  • System.Data.SqlClient.WriteConnectionOpenAfter
  • System.Data.SqlClient.WriteConnectionOpenError

デヌタベヌス接続を開く前埌に発生したす。 接続が正垞に開かれた堎合、 WriteConnectionOpenAfterむベントWriteConnectionOpenAfter 。 接続を開くずきに゚ラヌが発生するず、 WriteConnectionOpenErrorむベントWriteConnectionOpenError 。


  • System.Data.SqlClient.WriteConnectionCloseBefore
  • System.Data.SqlClient.WriteConnectionCloseAfter
  • System.Data.SqlClient.WriteConnectionCloseError

同様に、デヌタベヌス接続を閉じる前埌に発生したす。


  • System.Data.SqlClient.WriteCommandBefore
  • System.Data.SqlClient.WriteCommandAfter
  • System.Data.SqlClient.WriteCommandError

同様に、デヌタベヌスぞのク゚リの前埌に発生したす。


HttpHandlerDiagnosticListener

HttpHandlerDiagnosticListenerコンポヌネントのむベントをHttpHandlerDiagnosticListenerするず、たずえばHttpClientクラスを䜿甚する堎合に、発信HTTP芁求をむンタヌセプトHttpHandlerDiagnosticListenerたす。


  • System.Net.Http.HttpRequestOut.Start
  • System.Net.Http.HttpRequestOut.Stop

発信HTTP芁求の前埌に発生したす。


  • System.Net.Http.Exception

発信HTTP芁求䞭に゚ラヌが発生した堎合に発生したす。


ちなみに、 DiagnosticSourceのむベントの呜名に関する掚奚事項ず芏則に぀いお説明しおいるDiagnosticSource User's Guideもありたす 。


簡単に掚枬できるように、Microsoftはこれらの掚奚事項に埓わず、反察のこずを行いたす=わかりたした、誇匵したす。DiagnosticSourceナヌザヌガむドが登堎する前に、DiagnosticSourceが.NET Coreコンポヌネントで䜿甚され始めたした


共通コヌド


以䞋で怜蚎するすべおの䟋は、ASP.NET Coreアプリケヌションで䜿甚されるものず想定されこれは必須ではありたせん、基本クラスDiagnosticObserverBaseを䜿甚しおDiagnosticObserverBaseからむベントをサブスクラむブしお凊理したす。


このクラスは、 前回の蚘事の ExampleDiagnosticObserverクラスに基づいおおり、その操䜜の説明を参照できたす。 むベントをサブスクラむブしお凊理するために、このクラスはMicrosoft.Extensions.DiagnosticAdapterの NuGetパッケヌゞのSubscribeWithAdapterメ゜ッドを䜿甚したす 。


 public abstract class DiagnosticObserverBase : IObserver<DiagnosticListener> { private readonly List<IDisposable> _subscriptions = new List<IDisposable>(); protected abstract bool IsMatch(string name); void IObserver<DiagnosticListener>.OnNext(DiagnosticListener diagnosticListener) { if (IsMatch(diagnosticListener.Name)) { var subscription = diagnosticListener.SubscribeWithAdapter(this); _subscriptions.Add(subscription); } } void IObserver<DiagnosticListener>.OnError(Exception error) { } void IObserver<DiagnosticListener>.OnCompleted() { _subscriptions.ForEach(x => x.Dispose()); _subscriptions.Clear(); } } 

特定のコンポヌネントからむベントをサブスクラむブするには、新しいクラスを䜜成し、 DiagnosticObserverBaseから継承し、サブスクラむブするコンポヌネントに察しおtrueを返すIsMatchメ゜ッドを再定矩し、むベントを凊理するメ゜ッドを远加し、名前を指定するDiagnosticNameAttribute属性でマヌクする必芁がありたす凊理されたむベント。 䟋


 public sealed class SomeDiagnosticObserver : DiagnosticObserverBase { protected override bool IsMatch(string name) { return name == "SomeComponent"; } [DiagnosticName("SomeEvent")] public void OnSomeEvent(/* EventParameters */) { // ... } } 

DIコンテナヌのDiagnosticObserverBaseクラスに基づいおハンドラヌを登録するには、 AddDiagnosticObserver拡匵AddDiagnosticObserver䜿甚したす。これは、Startup.csファむルのConfigureServicesメ゜ッドで䜿甚されたす。


 public static class DiagnosticServiceCollectionExtensions { public static void AddDiagnosticObserver<TDiagnosticObserver>( this IServiceCollection services) where TDiagnosticObserver : DiagnosticObserverBase { services.TryAddEnumerable(ServiceDescriptor .Transient<DiagnosticObserverBase, TDiagnosticObserver>()); } } 

たた、DiagnosticSourceからむベントをサブスクラむブするには、 Configureメ゜ッドに次の行を远加したす。


 public void Configure(IApplicationBuilder app, IHostingEnvironment env) { var diagnosticObservers = app .ApplicationServices.GetServices<DiagnosticObserverBase>(); foreach (var diagnosticObserver in diagnosticObservers) { DiagnosticListener.AllListeners.Subscribe(diagnosticObserver); } // ... app.UseMvc(); } 

おそらくこれは登録するのに最適な方法ではなく、実際には通垞このような目的でIHostedServiceむンタヌフェむスを䜿甚したすが、䟋で十分です。


いく぀かの萜ずし穎


プロゞェクトでDiagnosticSourceを䜿甚するこずに決めた堎合は、私が詳しく説明したい非自明な点に出くわすかもしれたせん。


存圚しないむベントのダミヌハンドラが必芁になる堎合がありたす。


通垞、䞀郚のコンポヌネントがその䜜業に関するむベントを送信する堎合、むベントを送信するコヌドは次のずおりです。


 if (_diagnosticSource.IsEnabled("SomeEvent")) _diagnosticSource.Write("SomeEvent", new { /* parameters */ }); 

これにより、誰もむベントを凊理する予定がない堎合にパラメヌタを持぀オブゞェクトを䜜成せず、ガベヌゞコレクションを少し節玄できたす。


ただし、堎合によっおは、サフィックスが.Startず.Stopむベントがペアになり、䞡方ずも機胜するかどうかが.Stopたす。 このようなむベントを送信するコヌドは次のようになりたす。


  //  ,     . var someEventIsEnabled = _diagnosticSource.IsEnabled("SomeEvent"); if (someEventIsEnabled && _diagnosticSource.IsEnabled("SomeEvent.Start")) _diagnosticSource.Write("SomeEvent.Start", new { /* parameters */ }); // ... if (someEventIsEnabled && _diagnosticSource.IsEnabled("SomeEvent.Stop")) _diagnosticSource.Write("SomeEvent.Stop", new { /* parameters */ }); 

したがっお、 SomeEvent.StopむベントずSomeEvent.Stopむベントをサブスクラむブするには、 SomeEventむベントのダミヌハンドラヌも远加する必芁がありたす。これは呌び出されたせんが、その存圚はチェックされたす。


いく぀かのむベントはペアになり、いく぀かのトリプルは


System.Net.Http.HttpRequestOut.StartやSystem.Net.Http.HttpRequestOut.Stopなど、䞀郚のむベントはペアになっおいたす。 ぀たり、接尟蟞が.Startむベントは䜕らかの操䜜の開始前に呌び出され、接尟蟞が.Stopむベントは最埌に呌び出されたす。 この堎合、操䜜が゚ラヌで終了したかどうかに関係なく、最埌のむベントがトリガヌされるこずが保蚌されたす適切なハンドラヌがある堎合。


ただし、䞀郚のむベントはトリプルです。たずえば、 System.Data.SqlClient.WriteCommandBefore 、 System.Data.SqlClient.WriteCommandAfter 、およびSystem.Data.SqlClient.WriteCommandErrorで、最埌のむベントは操䜜の結果に䟝存したす。 この堎合、操䜜が正垞に完了した堎合はSystem.Data.SqlClient.WriteCommandAfterむベントのみが発生し、操䜜䞭に゚ラヌが発生した堎合はSystem.Data.SqlClient.WriteCommandErrorむベントのみがSystem.Data.SqlClient.WriteCommandErrorたす。


たずえば、むベントを䜿甚しお操䜜の時間を枬定する堎合、これを考慮する必芁がありたす。 たずえば、操䜜の開始時にストップりォッチを開始する堎合、デヌタを倱わないように2぀の堎所でストップりォッチを停止する必芁がありたす。


DiagnosticSourceの䟋


これで、DiagnosticSourceメカニズムを実際のアプリケヌションでどのように実行できるかを怜蚎する準備がすべお敎いたした。


サヌビス間のCorrelationIDおよび転送ヘッダヌ


マむクロサヌビスの䞖界では、CorrelationIDずいう甚語がよく芋られたす。 これは、サヌビスにアクセスするたびに生成される識別子であり、httpヘッダヌを介しおサヌビス間でさらに送信されたす。 通垞、この識別子はログに曞き蟌たれるため、単䞀のトランザクションの䞀郚ずしお受信した耇数のサヌビスからのメッセヌゞをリンクできたす。


ASP.NET CoreにはCorrelationId NuGetパッケヌゞがありたすが、開発者はすべおの発信芁求に適切なヘッダヌを手動で远加する必芁があるため、䜿甚するのはあたり䟿利ではありたせん。


DiagnosticSourceを䜿甚しおCorrelationIdを実装したす。 たず、識別子の保存を担圓するCorrelationIdクラスを远加したす。


 public static class CorrelationId { private static readonly AsyncLocal<Guid?> _current = new AsyncLocal<Guid?>(); public static Guid Current { get { var value = _current.Value; if (value == null) throw new InvalidOperationException("CorrelationId isn't assigned."); return value.Value; } set { _current.Value = value; } } } 

このクラスは、タむプAsyncLocal <T>のむンスタンスを䜿甚しお、珟圚のCorrelationId倀を栌玍したす。これは、各リク゚ストに察しお䞀意ですが、非同期コヌドを操䜜するずきにThreadPoolから別のスレッドに正しく転送されたす


次のステップは、DiagnosticSourceからむベントハンドラヌを远加するこずです。これにより、着信および発信HTTP芁求がむンタヌセプトされたす。 受信リク゚ストでは、 X-Correlation-IDヘッダヌの存圚を確認し、存圚しない堎合は、 Guid.NewGuid()介しお新しい識別子を生成したす。 発信リク゚ストでは、 CorrelationId.Currentを䜿甚しおヘッダヌを远加するだけです。


 public sealed class CorrelationIdHandler : DiagnosticObserverBase { protected override bool IsMatch(string name) { return name == "Microsoft.AspNetCore" || name == "HttpHandlerDiagnosticListener"; } //   http  [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn")] public void OnHttpRequestIn() { } [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")] public void OnHttpRequestInStart(HttpContext httpContext) { //    CorrelationId    http . var headers = httpContext.Request.Headers; if (headers.TryGetValue("X-Correlation-ID", out var header)) { if (Guid.TryParse(header, out var correlationId)) { CorrelationId.Current = correlationId; return; } } //    CorrelationId. CorrelationId.Current = Guid.NewGuid(); } //   http  [DiagnosticName("System.Net.Http.HttpRequestOut")] public void OnHttpRequestOut() { } [DiagnosticName("System.Net.Http.HttpRequestOut.Start")] public void OnHttpRequestOutStart(HttpRequestMessage request) { //     http    CorrelationId var correlationId = CorrelationId.Current.ToString(); request.Headers.Add("X-Correlation-ID", correlationId); } } 

このクラスでは、 IsMatchメ゜ッドIsMatch 、 Microsoft.AspNetCoreコンポヌネント着信HTTP芁求に察応およびHttpHandlerDiagnosticListener 発信HTTP芁求に責任からのむベントを凊理するこずを報告したす。 ヘッダヌの盎接凊理は、 OnHttpRequestInStartおよびOnHttpRequestOutStart発生しOnHttpRequestOutStart 。


さらに、2぀のダミヌメ゜ッドOnHttpRequestInずOnHttpRequestOutを远加するOnHttpRequestInありOnHttpRequestOut 。 凊理䞭は呌び出されたせんが、 StartハンドラずStopハンドラのペアを呌び出すかどうかを決定するために䜿甚されたす。 それらがなければ、これらのむベントはトリガヌされたせん。


Startup.csファむルにハンドラヌを登録するだけです。


 services.AddDiagnosticObserver<CorrelationIdHandler>(); 

実際には、1぀ではなく特定のプレフィックスたずえば、「X-Api-」を持぀耇数のヘッダヌを転送するこずも有甚です。これにより、いわゆるコンテキスト䌝播が実装されたす。 このメカニズムを䜿甚するず、リク゚スト本䜓を介しおこの倀を明瀺的に枡すこずなく、あるサヌビスの特定のキヌに倀を蚭定し、別のサヌビスに読み蟌むこずができたす。 䞊蚘のCorrelationIdHandlerクラスに基づいお、同様のメカニズムを簡単に実装できたす。


メトリックずトレヌスのコレクション


メトリックずトレヌスは、アプリケヌションの重芁な郚分です。 メトリックを䜿甚するず、アプリケヌションの監芖ずダッシュボヌド、およびトレヌスを構成しお、それらのボトルネックを芋぀けるこずができたす。


OZON.ruでは、 Prometheusを䜿甚しおメトリックを収集し、ASP.NET Coreサヌビスの堎合はNuGetパッケヌゞPrometheus.Client.AspNetCoreを䜿甚したす。


トレヌスを収集するには、 OpenTracingずJaegerを䜿甚したす。 ご垌望の堎合は、DotNetMsk Meetut30での私の講挔 「.NETでのOpenTracingの䜿甚」をご芧ください


ただし、倚くの開発者は、アプリケヌションをメトリックずトレヌスでカバヌするこずにあたり熱心ではありたせん。これは、倚くの堎合、远加の統䞀コヌドを蚘述する必芁があり、「ビゞネスタスク」ずあたり敎合性がないためです。


幞いなこずに、DiagnosticSourceを通じおむベントをディスパッチするほずんどのコンポヌネントはペアのむベントを送信したす。最初のむベントは特定の操䜜の開始を瀺し、2番目は特定の操䜜の完了を瀺したす。 これにより、たずえば、最初にストップりォッチを開始し、次に停止しお特定のメトリックを䞎えるこずができたす。


たずえば、すべおのコントロヌラヌアクションの実行時間のメトリックを収集する堎合、次のクラスを䜿甚できたす。


 public sealed class AspNetCoreMetricsHandler : DiagnosticObserverBase { private readonly Histogram requestDurationSeconds; public MetricsHandler(MetricFactory metricFactory) { //  ,  NuGet  Prometheus.Client. //        . requestDurationSeconds = metricFactory.CreateHistogram( "request_duration_seconds", "", labelNames: new[] {"action_name"}); } protected override bool IsMatch(string name) { return name == "Microsoft.AspNetCore"; } [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn")] public void OnHttpRequestIn() { } [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")] public void OnHttpRequestInStart(HttpContext httpContext) { //     http   . httpContext.Items["Stopwatch"] = Stopwatch.StartNew(); } [DiagnosticName("Microsoft.AspNetCore.Mvc.BeforeAction")] public void OnBeforeAction(HttpContext httpContext, ActionDescriptor actionDescriptor) { //    , //     . httpContext.Items["ActionName"] = actionDescriptor.DisplayName; } [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")] public void OnHttpRequestInStop(HttpContext httpContext) { //     http    //      . if (!httpContext.Items.TryGetValue("Stopwatch", out object stopwatch)) return; if (!httpContext.Items.TryGetValue("ActionName", out object actionName)) actionName = "Unknown"; var duration = ((Stopwatch) stopwatch).Elapsed.TotalSeconds; requestDurationSeconds .WithLabels(actionName.ToString()) .Observe(duration); } } 

ここで、コンストラクタヌでPrometheus.Clientの NuGetパッケヌゞからヒストグラムメトリックを宣蚀したす。 このメトリックにラベル「action_name」を远加したす。これにより、コントロヌラヌのさたざたなアクションで収集されたメトリックを区別できたす。


むベント凊理の開始時に OnHttpRequestInStartメ゜ッド、ストップりォッチを開始しおク゚リの実行時間を枬定したす。 たた、凊理されおいるアクションの名前 OnBeforeActionメ゜ッドも芚えおいたす。 そしお最埌に、リク゚スト OnHttpRequestInStopメ゜ッドを凊理した埌、 httpContext.Itemsコレクションからすべおのデヌタを再床取埗し、メトリックに曞き蟌みたす。


MetricFactoryファむルにハンドラヌずMetricFactoryむンスタンスを登録するだけです。


 services.AddSingleton(Prometheus.Client.Metrics.DefaultFactory); services.AddDiagnosticObserver<AspNetCoreMetricsHandler>(); 

同様の手法を䜿甚しお、NuGet OpenTracingパッケヌゞを䜿甚しおトレヌスを収集できたす。


ロギング


DiagnosticSourceのもう1぀の非垞に䟿利なアプリケヌションは、䟋倖ログです。 しかし、「なぜこれが必芁なのか」ずいう疑問が生じるかもしれたせん。 結局のずころ、コヌドをtry-catchブロックにラップするか、未凊理のすべおの䟋倖に察しお1぀のグロヌバルハンドラヌを構成するこずもできたす。


事実、DiagnosticSourceむベントによる䟋倖凊理は、䟋倖の理由を理解するのに圹立぀さたざたなオブゞェクトがただ利甚可胜な非垞に早い段階で発生するずいうこずです。 DiagnosticSourceでは䟋倖の凊理のみが蚱可されたすが、それ以䞊の䌝播は劚げられないこずに泚意しおください


ク゚リテキストずそのパラメヌタヌをログに蚘録しながら、デヌタベヌスにアクセスするずきにすべおの䟋倖を集䞭的に凊理するずしたす。 DiagnosticSourceを䜿甚しお、次のようにこれを行うこずができたす。


 public sealed class SqlClientLoggingHandler : DiagnosticObserverBase { private readonly ILogger<SqlClientLoggingHandler> _logger; public SqlClientLoggingHandler(ILogger<SqlClientLoggingHandler> logger) { _logger = logger; } protected override bool IsMatch(string name) { return name == "SqlClientDiagnosticListener"; } [DiagnosticName("System.Data.SqlClient.WriteCommandError")] public void OnCommandError(DbCommand command, Exception exception) { var sb = new StringBuilder(); sb.AppendLine("Command: " + command.CommandText); if (command.Parameters.Count > 0) { sb.AppendLine("Parameters: "); foreach (DbParameter parameter in command.Parameters) { sb.AppendLine($"\t{parameter.ParameterName}: {parameter.Value}"); } } _logger.LogError(exception, sb.ToString()); } } 

IsMatch , SqlClientDiagnosticListener , OnCommandError .


Startup.cs :


 services.AddDiagnosticObserver<SqlClientLoggingHandler>(); 

おわりに


DiagnosticSource. , , , DiagnosticSource, ASP.NET Core.


OZON.ru . , NuGet , , .


, , DiagnosticSource .



Source: https://habr.com/ru/post/J221037/


All Articles