| | | 1 | | using System.Diagnostics; |
| | | 2 | | using Microsoft.AspNetCore.Http; |
| | | 3 | | using Microsoft.Extensions.Logging; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Util.WebApi.Middleware; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Ensures every request is associated with a W3C-format <see cref="Activity"/>, propagating or creating one as |
| | | 9 | | /// needed, and exposes its trace id on <see cref="HttpContext.TraceIdentifier"/>, <c>HttpContext.Items["TraceId"]</c> |
| | | 10 | | /// and the response's <c>traceparent</c> header. |
| | | 11 | | /// </summary> |
| | | 12 | | /// <param name="next">The next middleware in the pipeline.</param> |
| | | 13 | | /// <param name="logger">Used to log the start and end of each request's trace.</param> |
| | 1 | 14 | | public class TraceActivityMiddleware(RequestDelegate next, ILogger<TraceActivityMiddleware> logger) : WebApiMiddleware |
| | | 15 | | { |
| | | 16 | | private const string TraceParentHeader = "traceparent"; |
| | | 17 | | |
| | | 18 | | static TraceActivityMiddleware() |
| | 1 | 19 | | { |
| | 1 | 20 | | Activity.DefaultIdFormat = ActivityIdFormat.W3C; |
| | 1 | 21 | | Activity.ForceDefaultIdFormat = true; |
| | 1 | 22 | | } |
| | | 23 | | |
| | | 24 | | /// <summary>Attaches the current (or a newly created) trace activity to the context and response, then invokes the |
| | | 25 | | /// <param name="context">The current HTTP context.</param> |
| | | 26 | | public async Task InvokeAsync(HttpContext context) |
| | 1 | 27 | | { |
| | 1 | 28 | | ArgumentNullException.ThrowIfNull(context); |
| | | 29 | | |
| | 1 | 30 | | var createdActivity = false; |
| | 1 | 31 | | var activity = Activity.Current; |
| | | 32 | | |
| | 1 | 33 | | if (activity == null) |
| | 1 | 34 | | { |
| | 1 | 35 | | activity = new Activity("ServerReceive").SetIdFormat(ActivityIdFormat.W3C).Start(); |
| | 1 | 36 | | createdActivity = true; |
| | 1 | 37 | | } |
| | | 38 | | |
| | 1 | 39 | | var traceId = activity.TraceId.ToString(); |
| | | 40 | | |
| | 1 | 41 | | context.TraceIdentifier = traceId; |
| | 1 | 42 | | context.Items["TraceId"] = traceId; |
| | | 43 | | |
| | | 44 | | |
| | 1 | 45 | | var tp = $"00-{activity.TraceId}-{activity.SpanId}-{(activity.Recorded ? "01" : "00")}"; |
| | | 46 | | |
| | 1 | 47 | | context.Response.Headers[TraceParentHeader] = tp; |
| | | 48 | | |
| | 1 | 49 | | logger.LogTrace("Started request with TraceId {TraceId}", traceId); |
| | | 50 | | |
| | | 51 | | try |
| | 1 | 52 | | { |
| | 1 | 53 | | await next(context); |
| | 1 | 54 | | } |
| | | 55 | | finally |
| | 1 | 56 | | { |
| | 1 | 57 | | logger.LogTrace("Ending request with TraceId {TraceId}", traceId); |
| | | 58 | | |
| | 1 | 59 | | if (createdActivity && Activity.Current == activity) |
| | 1 | 60 | | { |
| | 1 | 61 | | activity.Stop(); |
| | 1 | 62 | | } |
| | 1 | 63 | | } |
| | 1 | 64 | | } |
| | | 65 | | } |