public SentryTracingMiddleware( RequestDelegate next, Func <IHub> getHub, IOptions <SentryAspNetCoreOptions> options) { _next = next; _getHub = getHub; _options = options.Value; }
/// <summary> /// Initializes a new instance of the <see cref="SentryMiddleware"/> class. /// </summary> /// <param name="next">The next.</param> /// <param name="getHub">The sentry Hub accessor.</param> /// <param name="options">The options for this integration</param> /// <param name="hostingEnvironment">The hosting environment.</param> /// <param name="logger">Sentry logger.</param> /// <exception cref="ArgumentNullException"> /// next /// or /// sentry /// </exception> public SentryMiddleware( RequestDelegate next, Func <IHub> getHub, IOptions <SentryAspNetCoreOptions> options, IHostingEnvironment hostingEnvironment, ILogger <SentryMiddleware> logger) { _next = next ?? throw new ArgumentNullException(nameof(next)); _getHub = getHub ?? throw new ArgumentNullException(nameof(getHub)); _options = options.Value; _hostingEnvironment = hostingEnvironment; _logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="SentryMiddleware"/> class. /// </summary> /// <param name="next">The next.</param> /// <param name="sentry">The sentry.</param> /// <param name="options">The options for this integration</param> /// <param name="hostingEnvironment">The hosting environment.</param> /// <param name="logger">Sentry logger.</param> /// <exception cref="ArgumentNullException"> /// next /// or /// sentry /// </exception> public SentryMiddleware( RequestDelegate next, IHub sentry, SentryAspNetCoreOptions options, IHostingEnvironment hostingEnvironment, ILogger <SentryMiddleware> logger) { _next = next ?? throw new ArgumentNullException(nameof(next)); _sentry = sentry ?? throw new ArgumentNullException(nameof(sentry)); _options = options; _hostingEnvironment = hostingEnvironment; _logger = logger; }
private static void SetBody(Scope scope, HttpContext context, SentryAspNetCoreOptions options) { var extractors = context.RequestServices.GetService <IEnumerable <IRequestPayloadExtractor> >(); if (extractors == null) { return; } var dispatcher = new RequestBodyExtractionDispatcher(extractors, options, () => options.MaxRequestBodySize); var body = dispatcher.ExtractPayload(new HttpRequestAdapter(context.Request)); if (body != null) { scope.Request.Data = body; } }
private static void SetEnv(Scope scope, HttpContext context, SentryAspNetCoreOptions options) { scope.Request.Method = context.Request.Method; // Logging integration, if enabled, sets the following tag which ends up as duplicate // to Request.Url. Prefer the interface value and remove tag. var host = context.Request.Host.Host; if (context.Request.Host.Port != null) { host += $":{context.Request.Host.Port}"; } scope.Request.Url = $"{context.Request.Scheme}://{host}{context.Request.Path}"; scope.UnsetTag("RequestPath"); scope.Request.QueryString = context.Request.QueryString.ToString(); foreach (var requestHeader in context.Request.Headers) { if (options?.SendDefaultPii != true // Don't add headers which might contain PII && (requestHeader.Key == HeaderNames.Cookie || requestHeader.Key == HeaderNames.Authorization)) { continue; } scope.Request.Headers[requestHeader.Key] = requestHeader.Value; } // TODO: Hide these 'Env' behind some extension method as // these might be reported in a non CGI, old-school way if (options?.SendDefaultPii == true && context.Connection.RemoteIpAddress?.ToString() is { } ipAddress) { scope.Request.Env["REMOTE_ADDR"] = ipAddress; } scope.Request.Env["SERVER_NAME"] = Environment.MachineName; scope.Request.Env["SERVER_PORT"] = context.Connection.LocalPort.ToString(); if (context.Response.Headers.TryGetValue("Server", out var server)) { scope.Request.Env["SERVER_SOFTWARE"] = server; } }
/// <summary> /// Initializes a new instance of the <see cref="SentryMiddleware"/> class. /// </summary> /// <param name="next">The next.</param> /// <param name="getHub">The sentry Hub accessor.</param> /// <param name="options">The options for this integration</param> /// <param name="hostingEnvironment">The hosting environment.</param> /// <param name="logger">Sentry logger.</param> /// <exception cref="ArgumentNullException"> /// next /// or /// sentry /// </exception> public SentryMiddleware( RequestDelegate next, Func <IHub> getHub, IOptions <SentryAspNetCoreOptions> options, IHostingEnvironment hostingEnvironment, ILogger <SentryMiddleware> logger) { _next = next ?? throw new ArgumentNullException(nameof(next)); _getHub = getHub ?? throw new ArgumentNullException(nameof(getHub)); _options = options.Value; var hub = _getHub(); foreach (var callback in _options.ConfigureScopeCallbacks) { hub.ConfigureScope(callback); } _hostingEnvironment = hostingEnvironment; _logger = logger; }
/// <summary> /// Initializes a new instance of the <see cref="SentryMiddleware"/> class. /// </summary> /// <param name="next">The next.</param> /// <param name="hubAccessor">The sentry Hub accessor.</param> /// <param name="options">The options for this integration</param> /// <param name="hostingEnvironment">The hosting environment.</param> /// <param name="logger">Sentry logger.</param> /// <exception cref="ArgumentNullException"> /// next /// or /// sentry /// </exception> public SentryMiddleware( RequestDelegate next, Func <IHub> hubAccessor, IOptions <SentryAspNetCoreOptions> options, IHostingEnvironment hostingEnvironment, ILogger <SentryMiddleware> logger) { _next = next ?? throw new ArgumentNullException(nameof(next)); _hubAccessor = hubAccessor ?? throw new ArgumentNullException(nameof(hubAccessor)); _options = options?.Value; if (_options != null) { var hub = _hubAccessor(); foreach (var callback in _options.ConfigureScopeCallbacks) { hub.ConfigureScope(callback); } } _hostingEnvironment = hostingEnvironment; _logger = logger; }
/// <summary> /// Populates the scope with the HTTP data /// </summary> /// <remarks> /// NOTE: The scope is applied to the event BEFORE running the event processors/exception processors. /// The main Sentry SDK has processors which run right before any additional processors to the Event /// </remarks> public static void Populate(this Scope scope, HttpContext context, SentryAspNetCoreOptions options) { // With the logger integration, a BeginScope call is made with RequestId. That ends up adding // two tags with the same value: RequestId and TraceIdentifier if (!scope.Tags.TryGetValue("RequestId", out var requestId) || requestId != context.TraceIdentifier) { scope.SetTag(nameof(context.TraceIdentifier), context.TraceIdentifier); } if (options?.SendDefaultPii == true && !scope.HasUser()) { var userFactory = context.RequestServices?.GetService <IUserFactory>(); if (userFactory != null) { scope.User = userFactory.Create(context); } } try { SetBody(scope, context, options); } catch (Exception e) { options?.DiagnosticLogger?.LogError("Failed to extract body.", e); } SetEnv(scope, context, options); // TODO: From MVC route template, ideally // TODO: optionally get transaction from request through a dependency //scope.Transation = context.Request.Path; // TODO: Get context stuff into scope //context.Session //context.Response //context.Items }
/// <summary> /// Populates the scope with the HTTP data /// </summary> /// <remarks> /// NOTE: The scope is applied to the event BEFORE running the event processors/exception processors. /// The main Sentry SDK has processors which run right before any additional processors to the Event /// </remarks> public static void Populate(this Scope scope, HttpContext context, SentryAspNetCoreOptions options) { // With the logger integration, a BeginScope call is made with RequestId. That ends up adding // two tags with the same value: RequestId and TraceIdentifier if (!scope.Tags.TryGetValue("RequestId", out var requestId) || requestId != context.TraceIdentifier) { scope.SetTag(nameof(context.TraceIdentifier), context.TraceIdentifier); } if (options?.SendDefaultPii == true && !scope.HasUser()) { var userFactory = context.RequestServices?.GetService <IUserFactory>(); if (userFactory != null) { scope.User = userFactory.Create(context); } } try { SetBody(scope, context, options); } catch (Exception e) { options?.DiagnosticLogger?.LogError("Failed to extract body.", e); } SetEnv(scope, context, options); // Extract the route data try { var routeData = context.GetRouteData(); if (routeData != null) { var controller = routeData.Values["controller"]?.ToString(); var action = routeData.Values["action"]?.ToString(); var area = routeData.Values["area"]?.ToString(); if (controller != null) { scope.SetTag("route.controller", controller); } if (action != null) { scope.SetTag("route.action", action); } if (area != null) { scope.SetTag("route.area", area); } scope.Transaction = area == null ? $"{controller}.{action}" : $"{area}.{controller}.{action}"; } } catch (Exception e) { // Suppress the error here; we expect an ArgumentNullException if httpContext.Request.RouteValues is null from GetRouteData() // TODO: Consider adding a bool to the Sentry options to make route data extraction optional in case they don't use a routing middleware? options?.DiagnosticLogger?.LogDebug("Failed to extract route data.", e); } // TODO: Get context stuff into scope //context.Session //context.Response //context.Items }
/// <summary> /// Populates the scope with the HTTP data /// </summary> /// <remarks> /// NOTE: The scope is applied to the event BEFORE running the event processors/exception processors. /// The main Sentry SDK has processors which run right before any additional processors to the Event /// </remarks> public static void Populate(this Scope scope, HttpContext context, SentryAspNetCoreOptions options) { // Not to throw on code that ignores nullability warnings. // ReSharper disable ConditionIsAlwaysTrueOrFalse if (scope is null || context is null || options is null) { return; } // ReSharper restore ConditionIsAlwaysTrueOrFalse // With the logger integration, a BeginScope call is made with RequestId. That ends up adding // two tags with the same value: RequestId and TraceIdentifier if (!scope.Tags.TryGetValue("RequestId", out var requestId) || requestId != context.TraceIdentifier) { scope.SetTag(nameof(context.TraceIdentifier), context.TraceIdentifier); } if (options.SendDefaultPii && !scope.HasUser()) { var userFactory = context.RequestServices.GetService <IUserFactory>(); var user = userFactory?.Create(context); if (user != null) { scope.User = user; } } try { SetBody(scope, context, options); } catch (Exception e) { options.LogError("Failed to extract body.", e); } SetEnv(scope, context, options); // Extract the route data try { var routeData = context.GetRouteData(); var controller = routeData.Values["controller"]?.ToString(); var action = routeData.Values["action"]?.ToString(); var area = routeData.Values["area"]?.ToString(); if (controller != null) { scope.SetTag("route.controller", controller); } if (action != null) { scope.SetTag("route.action", action); } if (area != null) { scope.SetTag("route.area", area); } // Transaction Name may only be available afterward the creation of the Transaction. // In this case, the event will update the transaction name if captured during the // pipeline execution, allowing it to match the correct transaction name as the current // active transaction. if (string.IsNullOrEmpty(scope.TransactionName)) { scope.TransactionName = context.TryGetTransactionName(); } } catch (Exception e) { // Suppress the error here; we expect an ArgumentNullException if httpContext.Request.RouteValues is null from GetRouteData() // TODO: Consider adding a bool to the Sentry options to make route data extraction optional in case they don't use a routing middleware? options.LogDebug("Failed to extract route data.", e); } // TODO: Get context stuff into scope //context.Session //context.Response //context.Items }