Azureの小ネタ (改)

~Azureネタを中心に、色々とその他の技術的なことなどを~

ASP.NET Core の Request body を記録する

例のごとく備忘録メモ。

よくあるリクエストボディを記録したいというお話で、ググると同様の話題は沢山でてきますが、これといってフレームワークで決まったお作法があるわけではない感じです。

よくある記録先

よくある記録先としては2つ。

  • ILogger
  • Application Insights

Application Insightsへの記録は、ILogger経由でも問題ありませんが、Request Telemetryに記録したい場合などです。

同様の話題、Application InsightへIssueの投稿は繰り返されるらしく、最新のOpenしているIssueはOptionally log POST request body · Issue #1880 · microsoft/ApplicationInsights-dotnet · GitHub これみたいです。

ただ内容通り、 今年はサポートする予定はなく、ILoggerで書いてねと言われています。

Unfortunately, this is not going to be supported by the ApplicationInsights SDK in this year.

将来的にフレームワーク側でサポートを期待したいところです。

ASP.NET Coreとしての問題

リクエストボディの再読み出しはあまりデフォルトではできなくて、あまりお勧めもされていない(と思われます)

Close済みのIssueでMSの人が言及しています。 Telemetry Initializer to add request body content from .net core MVC app to request telemetry properties not working · Issue #686 · microsoft/ApplicationInsights-aspnetcore · GitHub

It is not possible to read the Post/Put body stream twice - so attempting to read it in TelemetryInitializer is not recommended and will fail.

そしてそのサンプルは以下の通りで、Controllerで記録すれば的な感じでコードサンプルが提示されています。

       [HttpPost]
        public IActionResult Create([FromBody] TodoItem item)
        {
            if (item == null)
            {
                return BadRequest();
            }

            RequestTelemetry requestTelemetry = HttpContext.Features.Get<RequestTelemetry>();
            requestTelemetry.Properties.Add("cijokey", item.Name);

            _context.TodoItems.Add(item);
            _context.SaveChanges();

            return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
        }

とはいえ、毎回Controllerの入り口にコレを書くのも面倒ですので、ミドルウェアで処理するのが簡単そうです。

ミドルウェアでの処理

ILoggerを使う場合、InvokeAsyncからILoggerインスタンスが取れますから、適宜記録すればしかるべきところに記録されるでしょう。(コンストラクタインジェクションではないので注意)

        public async Task InvokeAsync(HttpContext context, ILogger<RequestMiddleware> logger)
        {
           ...
        }

今回は、ReqestTelemetryに記録したいのでInvokeAsyncの中に以下のように書きます。

        var requestTelemetry = context.Features.Get<RequestTelemetry>();
        if (requestTelemetry != null)
        {
            var requestBodyString = Encoding.UTF8.GetString(body);
            requestTelemetry.Properties.Add("RequestBody", requestBodyString);
       }

context.Features.Get から RequestTelemetryが取れるようです。これでわざわざITelemetryInitializer を使う必要も無く記録できそうです。

以下がApplication Insgihtに記録した例です。RequestBodyが記録されているの確認できます。

f:id:StateMachine:20200623225850p:plain

こうすることで、RequestTelemetryに記録を統合できる感じになりました。

ILogger経由で trace telemetoryに記録してもいいんですが、ケースバイケースということで。

以上