-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
So/review sentry exception logging in the api (#1498)
* Initial config setup * WIP - added http request correlation id in support of Serilog * WIP - logging correlation id - tidy-up * WIP - logging * WIP * WIP - logging correlation ID * Corrected syntax error * Removed async from correlation ID middleware * WIP - building out test coverage * WIP - added code comments as tests are built out * WIP - completed tests for LogEventExtensions * Tests completed * Pushed last tests changes * Removed cofigured file logging * Fixed upsert candidate tests * Removed whitespace * Removed unused correlation Id on PerformContextAdapter * Removed code-smells based on SonarQube recommendations * Corrected attribute usage restrictions on CorrelationIdFilterAttribute * Removed commented out code from Program.cs to keep SonarQube happy * Made changes recommended by MW in associated PR * Corrected a couple of issues flagged by SonarQube --------- Co-authored-by: Spencer O'HEGARTY <[email protected]>
- Loading branch information
1 parent
fb482ad
commit 061590d
Showing
28 changed files
with
1,007 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
GetIntoTeachingApi/CrossCuttingConcerns/Logging/Common/CorrelationPropertyKeys.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace GetIntoTeachingApi.CrossCuttingConcerns.Logging.Common | ||
{ | ||
/// <summary> | ||
/// Shared property keys used to define request correlation specific properties. | ||
/// </summary> | ||
public readonly struct CorrelationPropertyKeys | ||
{ | ||
/// <summary> | ||
/// The correlation Id (GUID) property name key defined for each http request. | ||
/// </summary> | ||
public static readonly string PerRequestCorrelationIdPropertyNameKey = "PerRequestCorrelationId"; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
GetIntoTeachingApi/CrossCuttingConcerns/Logging/HttpContextCorrelationIdProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using Microsoft.AspNetCore.Http.Features; | ||
using Microsoft.AspNetCore.Http; | ||
using System.Diagnostics; | ||
using System; | ||
using GetIntoTeachingApi.CrossCuttingConcerns.Logging.Common; | ||
|
||
namespace GetIntoTeachingApi.CrossCuttingConcerns.Logging | ||
{ | ||
/// <summary> | ||
/// Provides the ability to extract a given correlation Id (if available) from | ||
/// the current HTTP context using the provisioned <see cref="IHttpContextAccessor"/> instance. | ||
/// </summary> | ||
public sealed class HttpContextCorrelationIdProvider : IHttpContextCorrelationIdProvider | ||
{ | ||
private readonly IHttpContextAccessor _httpContextAccessor; | ||
|
||
/// <summary> | ||
/// Provisions the <see cref="IHttpContextAccessor"/> which provides | ||
/// access to the current <see cref="HttpContext"/>, if one is available. | ||
/// </summary> | ||
/// <param name="httpContextAccessor"> | ||
/// The <see cref="IHttpContextAccessor"/> instance used to access the current <see cref="HttpContext"/>. | ||
/// </param> | ||
public HttpContextCorrelationIdProvider(IHttpContextAccessor httpContextAccessor) | ||
{ | ||
_httpContextAccessor = httpContextAccessor; | ||
} | ||
|
||
/// <summary> | ||
/// Attempts to extract the correlation Id from the current <see cref="HttpContext"/>. | ||
/// </summary> | ||
/// <returns> | ||
/// The Correlation Id (GUID), defaults to Empty if no correlation Id is provisioned. | ||
/// </returns> | ||
public Guid GetCorrelationId() | ||
{ | ||
Guid correlationId = Guid.Empty; | ||
HttpContext httpContext = _httpContextAccessor.HttpContext; | ||
|
||
if (httpContext is not null) | ||
{ | ||
IHttpActivityFeature httpActivityFeature = | ||
httpContext.Features.GetRequiredFeature<IHttpActivityFeature>(); | ||
|
||
Activity activity = httpActivityFeature.Activity; | ||
|
||
object httpRequestCorrelationId = | ||
activity.GetTagItem(CorrelationPropertyKeys.PerRequestCorrelationIdPropertyNameKey); | ||
|
||
if (httpRequestCorrelationId is not null) | ||
{ | ||
correlationId = (Guid)httpRequestCorrelationId; | ||
} | ||
} | ||
|
||
return correlationId; | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
GetIntoTeachingApi/CrossCuttingConcerns/Logging/IHttpContextCorrelationIdProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System; | ||
|
||
namespace GetIntoTeachingApi.CrossCuttingConcerns.Logging | ||
{ | ||
/// <summary> | ||
/// Provides the ability to extract a given correlation Id (if available) from the current HTTP context. | ||
/// </summary> | ||
public interface IHttpContextCorrelationIdProvider | ||
{ | ||
/// <summary> | ||
/// Contract for extracting the correlation Id from the current <see cref="HttpContext"/>. | ||
/// </summary> | ||
/// <returns> | ||
/// The Correlation Id (GUID), defaults to Empty if no correlation Id is provisioned. | ||
/// </returns> | ||
Guid GetCorrelationId(); | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
...chingApi/CrossCuttingConcerns/Logging/Serilog/CustomEnrichers/CorrelationIdLogEnricher.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Serilog.Core; | ||
using Serilog.Events; | ||
using System; | ||
|
||
namespace GetIntoTeachingApi.CrossCuttingConcerns.Logging.Serilog.CustomEnrichers | ||
{ | ||
/// <summary> | ||
/// Log event enricher allows associated events to have a | ||
/// correlation Id (GUID) to be assigned to a named property allowing | ||
/// various log events to be aggregated across a single request. | ||
/// </summary> | ||
public class CorrelationIdLogEnricher : ILogEventEnricher | ||
{ | ||
private readonly IHttpContextAccessor _httpContextAccessor; | ||
private readonly IHttpContextCorrelationIdProvider _httpContextCorrelationIdProvider; | ||
|
||
/// <summary> | ||
/// Initialisation requires a <see cref="IHttpContextAccessor"/> which provides | ||
/// access to the current <see cref="HttpContext"/>, if one is available; and a | ||
/// <see cref="IHttpContextCorrelationIdProvider"/> which provides a correlation | ||
/// Id (GUID) used to aggregates log events across a single request. | ||
/// </summary> | ||
/// <param name="httpContextAccessor"> | ||
/// Instance of <see cref="IHttpContextAccessor"/> which provides | ||
/// access to the current <see cref="HttpContext"/>, if available. | ||
/// </param> | ||
/// /// <param name="httpContextCorrelationIdProvider"> | ||
/// Instance of <see cref="IHttpContextCorrelationIdProvider"/> which provides | ||
/// a correlation Id (GUID) used to aggregates log events across a single request. | ||
/// </param> | ||
public CorrelationIdLogEnricher( | ||
IHttpContextAccessor httpContextAccessor, | ||
IHttpContextCorrelationIdProvider httpContextCorrelationIdProvider) | ||
{ | ||
_httpContextAccessor = httpContextAccessor; | ||
_httpContextCorrelationIdProvider = httpContextCorrelationIdProvider; | ||
} | ||
|
||
/// <summary> | ||
/// Call to enrich decorates each 'enriched' log event with additional | ||
/// properties, including the correlation Id derived from the current request. | ||
/// </summary> | ||
/// <param name="logEvent"> | ||
/// The contextual <see cref="LogEvent"/> on which additional | ||
/// properties will be applied/enriched. | ||
/// </param> | ||
/// <param name="propertyFactory"> | ||
/// The <see cref="ILogEventPropertyFactory"/> factory object used to create | ||
/// log event properties from regular .NET objects, applying policies as required. | ||
/// </param> | ||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) | ||
{ | ||
HttpContext httpContext = _httpContextAccessor.HttpContext; | ||
|
||
if (httpContext is not null) | ||
{ | ||
logEvent | ||
.LogProperty(propertyFactory, | ||
LogPropertyKeys.RequestMethodPropertyNameKey, httpContext.Request.Method) | ||
.LogProperty(propertyFactory, | ||
LogPropertyKeys.RequestPathPropertyNameKey, httpContext.Request.Path) | ||
.LogProperty(propertyFactory, | ||
LogPropertyKeys.UserAgentPropertyNameKey, | ||
httpContext.Request.Headers[LogPropertyKeys.UserAgentHeaderNameKey]); | ||
|
||
Guid correlationId = _httpContextCorrelationIdProvider.GetCorrelationId(); | ||
|
||
if (correlationId != Guid.Empty) | ||
{ | ||
logEvent.LogProperty(propertyFactory, | ||
LogPropertyKeys.CorrelationIdNameKey, $"CID-{correlationId}"); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Aggregation of related log property keys used to define a given log property name. | ||
/// </summary> | ||
internal readonly struct LogPropertyKeys | ||
{ | ||
/// <summary> | ||
/// httpContext.Request.Method property name key. | ||
/// </summary> | ||
public static readonly string RequestMethodPropertyNameKey = "RequestMethod"; | ||
/// <summary> | ||
/// httpContext.Request.Path property name key. | ||
/// </summary> | ||
public static readonly string RequestPathPropertyNameKey = "RequestPath"; | ||
/// <summary> | ||
/// httpContext.Request.Headers user agent property name key. | ||
/// </summary> | ||
public static readonly string UserAgentPropertyNameKey = "UserAgent"; | ||
/// <summary> | ||
/// httpContext.Request.Headers user agent header index key. | ||
/// </summary> | ||
public static readonly string UserAgentHeaderNameKey = "User-Agent"; | ||
/// <summary> | ||
/// The specific request correlation Id (GUID). | ||
/// </summary> | ||
public static readonly string CorrelationIdNameKey = "CorrelationId"; | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...ntoTeachingApi/CrossCuttingConcerns/Logging/Serilog/CustomEnrichers/LogEventExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using Serilog.Core; | ||
using Serilog.Events; | ||
using System; | ||
|
||
namespace GetIntoTeachingApi.CrossCuttingConcerns.Logging.Serilog.CustomEnrichers | ||
{ | ||
/// <summary> | ||
/// Extension method used to aggregate the behaviour required to | ||
/// create a new log property, and add to the current <see cref="LogEvent"/> property collection. | ||
/// </summary> | ||
public static class LogEventExtensions | ||
{ | ||
/// <summary> | ||
/// Allows additional properties to be added to a given | ||
/// <see cref="LogEvent"/> property collection. | ||
/// </summary> | ||
/// <param name="logEvent"> | ||
/// The <see cref="LogEvent"/> instance whose properties will be extended. | ||
/// </param> | ||
/// <param name="propertyFactory"> | ||
/// The <see cref="ILogEventPropertyFactory"/> instance used to create log event | ||
/// properties from regular .NET objects,as required. | ||
/// </param> | ||
/// <param name="propertyKey"> | ||
/// The string values used to assign as the property name. | ||
/// </param> | ||
/// <param name="propertyValue"> | ||
/// The object value used to assign as the property value. | ||
/// </param> | ||
/// <returns></returns> | ||
public static LogEvent LogProperty( | ||
this LogEvent logEvent, | ||
ILogEventPropertyFactory propertyFactory, | ||
string propertyKey, | ||
object propertyValue) | ||
{ | ||
ArgumentNullException.ThrowIfNull(logEvent); | ||
ArgumentNullException.ThrowIfNull(propertyFactory); | ||
ArgumentNullException.ThrowIfNull(propertyKey); | ||
ArgumentNullException.ThrowIfNull(propertyValue); | ||
|
||
logEvent.AddPropertyIfAbsent( | ||
propertyFactory.CreateProperty(propertyKey, propertyValue)); | ||
|
||
return logEvent; | ||
} | ||
} | ||
} |
Oops, something went wrong.