using Microsoft.AspNetCore.Http.Extensions; using Ocelot.RequestId; using Serilog; using System.Diagnostics; using System.Text; using Yitter.IdGenerator; namespace HuiXin.Gateway.Ocelot.Middlewares { public class AccessLoggingMiddleware { private readonly RequestDelegate _next; public AccessLoggingMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var traceIdKey = DefaultRequestIdKey.Value; var traceId = YitIdHelper.NextId().ToString(); context.TraceIdentifier = traceId; context.Request.Headers[traceIdKey] = traceId; var sw = Stopwatch.StartNew(); sw.Start(); var sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine("-------------------Request Start-------------------"); sb.AppendLine(DateTime.Now.ToString()); sb.AppendLine($"追踪id:{traceId}"); sb.AppendLine($"地址:{context.Request.GetDisplayUrl()}"); sb.AppendLine("请求头:"); foreach (var header in context.Request.Headers) { sb.AppendLine($"{header.Key}:{header.Value}"); } sb.AppendLine($"数据:{await GetRequestBody(context.Request)}"); sb.AppendLine("===================Request End==================="); Log.Information(sb.ToString()); sb.Clear(); //Copy a pointer to the original response body stream var originalBodyStream = context.Response.Body; //Create a new memory stream... using var responseBody = new MemoryStream(); //...and use that for the temporary response body context.Response.Body = responseBody; //Continue down the Middleware pipeline, eventually returning to this class await _next(context); //Format the response from the server //var response = await FormatResponse(context.Response); sb.AppendLine(); sb.AppendLine("-------------------Response Start-------------------"); sb.AppendLine(DateTime.Now.ToString()); sb.AppendLine($"追踪id:{traceId}"); sb.AppendLine($"耗时:{sw.ElapsedMilliseconds}毫秒"); sb.AppendLine($"状态码:{context.Response.StatusCode}"); //sb.AppendLine("Headers: "); //foreach (var header in context.Response.Headers) //{ // sb.AppendLine($"{header.Key}:{header.Value}"); //} sb.AppendLine($"数据:{await GetResponseBody(context.Response)}"); sb.AppendLine("===================Response End==================="); //Save log to chosen datastore Log.Information(sb.ToString()); sb.Clear(); //Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client. await responseBody.CopyToAsync(originalBodyStream); } private static async Task GetRequestBody(HttpRequest request) { request.EnableBuffering(); // Leave the body open so the next middleware can read it. using var reader = new StreamReader( request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); var body = await reader.ReadToEndAsync(); // Do some processing with body… //var formattedRequest = $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {body}"; // Reset the request body stream position so the next middleware can read it request.Body.Position = 0; return body; } private static async Task GetResponseBody(HttpResponse response) { //We need to read the response stream from the beginning... response.Body.Seek(0, SeekOrigin.Begin); //...and copy it into a string string text = await new StreamReader(response.Body).ReadToEndAsync(); //We need to reset the reader for the response so that the client can read it. response.Body.Seek(0, SeekOrigin.Begin); //Return the string for the response, including the status code (e.g. 200, 404, 401, etc.) //return $"{response.StatusCode}: {text}"; return text; } static AccessLoggingMiddleware() { // 创建 IdGeneratorOptions 对象,可在构造函数中输入 WorkerId: var options = new IdGeneratorOptions(); // options.WorkerIdBitLength = 10; // 默认值6,限定 WorkerId 最大值为2^6-1,即默认最多支持64个节点。 // options.SeqBitLength = 6; // 默认值6,限制每毫秒生成的ID个数。若生成速度超过5万个/秒,建议加大 SeqBitLength 到 10。 // options.BaseTime = Your_Base_Time; // 如果要兼容老系统的雪花算法,此处应设置为老系统的BaseTime。 // ...... 其它参数参考 IdGeneratorOptions 定义。 // 保存参数(务必调用,否则参数设置不生效): YitIdHelper.SetIdGenerator(options); } } }