2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【.NET7 】使い回せるロギングミドルウェアつくた

Posted at

環境

  • .NET7

仕様

APIのリクエストとレスポンスをログ上に書き出す仕様で作成
GET以外のログを書き出す
認証されているユーザ名も書き出す

流れ

  • NuGetにてパッケージ追加
    • NLog
    • NLog.Web.AspNetCore
  • nlog.config ファイルを作成
  • LoggingMiddlewareファイル作成
  • Program.csにミドルウェア追加!

nlog.config ファイルを作成

<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true">
	<extensions>
		<add assembly="NLog.Web.AspNetCore" />
	</extensions>
	<variable name="layoutDefine" value="${longdate} [${level:padding=-5}] ${message} ${exception:format=tostring}" />
	<targets async="true">
		<target xsi:type="File" name="log" fileName="${basedir}/../logs/log-${shortdate}.log" layout="${layoutDefine}" />
	</targets>
	<rules>
		<logger name="Microsoft.*" maxLevel="Info" final="true" />
		<logger name="*" levels="Info,Error,Fatal,Warn" writeTo="log" />
	</rules>
</nlog>

${basedir}ってとこがよくわからんくて調べるの大変だった

- LoggingMiddlewareファイル作成


using System.Security.Claims;
using System.Text;
using NLog;
using NLog.Web;

namespace {プロジェクト名}.Common.Middleware;

public class LoggingMiddleware
{
	private readonly RequestDelegate _next;

	public LoggingMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task Invoke(HttpContext context)
	{
		var user = context.Request.HttpContext.User.FindFirst(ClaimTypes.UserData)?.Value ?? "unknown";
		var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();

		context.Request.EnableBuffering();

		var request = context.Request;
		if (request.Method != "GET")
		{
			/*
			* リクエスト
			*/
			using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
			{
				var body = await reader.ReadToEndAsync();
				var _body = body.Replace(" ", string.Empty).Replace(Environment.NewLine, string.Empty);  //空白削除
				logger.Info(message: $"{user} {request.Method} {request.Path} {_body}");
				reader.BaseStream.Position = 0;
			}

			/*
			* レスポンス
			* MEMO: 標準のストリームでは再読み込み(バッファリング)できないため差し替える 
			*/
			//標準ストリームは一時退避
			var originBodyStream = context.Response.Body;
			try
			{
				using (var memoryStream = new MemoryStream())
				{
					//ここで差し替え
					context.Response.Body = memoryStream;
					//ここでコントローラのメソッドが実行される
					await _next(context);
					//シーク位置を最初に戻す
					memoryStream.Position = 0;

					var body = await new StreamReader(memoryStream).ReadToEndAsync();
					//シーク位置を最初に戻す
					memoryStream.Position = 0;

					if (IsSuccess(context.Response.StatusCode))
					{
						logger.Info($"{user} Success");
					}
					else
					{
						logger.Info($"{user} Failure {body}");
					}

					//ここで元のストリームを戻す
					await memoryStream.CopyToAsync(originBodyStream);
				}
			}
			catch
			{
				context.Response.Body = originBodyStream;
			}
		}
		else
		{
			//GETは何も処理なし
			await _next(context);
		}
	}

	private static bool IsSuccess(int statusCode)
	{
		return 200 <= statusCode && statusCode <= 299;
	}
}

public static class LoggingMiddlewareExtensions
{
	public static IApplicationBuilder UseLogging(this IApplicationBuilder builder)
	{
		return builder.UseMiddleware<LoggingMiddleware>();
	}
}

今回はGET以外のHTTPメソッドのリクエストとレスポンスをログに書き出す仕様で作った

Program.csにミドルウェア追加!

app.UseAuthentication();
app.UseAuthorization();

app.UseLogging();   //認証情報を取得するため認証ミドルウェアより後に追加

app.MapControllers();

app.Run();
2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?