Skip to content

Commit

Permalink
Merge pull request #44 from serilog/dev
Browse files Browse the repository at this point in the history
5.1.0 Release
  • Loading branch information
QuantumNightmare authored Feb 4, 2022
2 parents 423c279 + 264a9ac commit 477c4ed
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,6 @@ UpgradeLog*.htm
# Microsoft Fakes
FakesAssemblies/
.vs/

# Rider settings
.idea/
91 changes: 84 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ A Serilog sink that writes events to Raygun

## Usage

Add the [Serilog](https://www.nuget.org/packages/serilog/) and [Serilog.Sinks.Raygun](https://www.nuget.org/packages/Serilog.Sinks.Raygun) nuget packages.
Via the package manager, or via the command line:

```powershell
dotnet add package Serilog
dotnet add package Serilog.Sinks.Raygun
```

Then setup the logger configuration; inside the `Program.Main()`, `Global.asax.Application_Start`, etc.

```csharp
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
Expand All @@ -23,6 +33,34 @@ Log.Logger = new LoggerConfiguration()
.CreateLogger();
```

When configuring using a JSON configuration file use the following example.

```json
{
"Serilog": {
"Using": [
"Serilog.Sinks.Raygun"
],
"WriteTo": [
{
"Name": "Raygun",
"Args": {
"applicationKey": "RaygunAPIKey",
"userNameProperty": "CustomUserNameProperty",
"applicationVersionProperty": "CustomAppVersionProperty",
"restrictedToMinimumLevel": "Error",
"ignoredFormFieldNames": ["ignoreField1", "ignoreField2"],
"tags": ["globalTag1", "globalTag2"],
"groupKeyProperty": "CustomGroupKeyProperty",
"tagsProperty": "CustomTagsProperty",
"userInfoProperty": "CustomUserInfoProperty"
}
}
]
}
}
```

### applicationKey
`type: string`

Expand Down Expand Up @@ -133,21 +171,60 @@ var userInfo = new RaygunIdentifierMessage("12345")

Log.ForContext("CustomUserInfoProperty", userInfo, true).Error(new Exception("random error"), "other information");
```
## Enrich with HTTP request and response data

## Raygun4Net features configured via RaygunSettings
_Note: This is only valid for .NET Standard 2.0 and above projects. In full framework ASP.NET applications the HTTP request and response are available to Raygun4Net through the `HttpContext.Current` accessor.
In .NET Core this is not available so you'll need to add the Serilog enricher using the `WithHttpDataForRaygun` method to capture the HTTP request and response data._

This sink wraps the [Raygun4Net](https://github.com/MindscapeHQ/raygun4net) provider to build a crash report from an Exception and send it to Raygun. This makes the following Raygun4Net features available to you. To use these features, you need to add RaygunSettings to your configuration as explained below which is separate to the Serilog configuration.
### Configuration

**.NET Core**

Add a RaygunSettings block to your appsettings.config file where you can populate the settings that you want to use.
All parameters to `WithHttpDataForRaygun` are optional.

```csharp
Log.Logger = new LoggerConfiguration()
.WriteTo.Raygun("RaygunAPIKey")
.Enrich.WithHttpDataForRaygun(
new HttpContextAccessor(),
LogEventLevel.Error,
RaygunSettings)
.CreateLogger();
```
"RaygunSettings": {
"Setting": "Value"

When configuring using a JSON configuration file use the following example.

```json
{
"Serilog": {
"Using": [
"Serilog.Sinks.Raygun"
],
"Enrich": [
{
"Name": "WithHttpDataForRaygun",
"Args": {
"RaygunSettings": {
"IsRawDataIgnored": true,
"UseXmlRawDataFilter": true,
"IsRawDataIgnoredWhenFilteringFailed": true,
"UseKeyValuePairRawDataFilter": true,
"IgnoreCookieNames": ["CookieName"],
"IgnoreHeaderNames": ["HeaderName"],
"IgnoreFormFieldNames": ["FormFieldName"],
"IgnoreQueryParameterNames": ["QueryParameterName"],
"IgnoreSensitiveFieldNames": ["SensitiveFieldNames"],
"IgnoreServerVariableNames": ["ServerVariableName"]
}
}
}
]
}
}
```

## Raygun4Net features configured via RaygunSettings

This sink wraps the [Raygun4Net](https://github.com/MindscapeHQ/raygun4net) provider to build a crash report from an Exception and send it to Raygun. This makes the following Raygun4Net features available to you. To use these features, you need to add RaygunSettings to your configuration as explained below which is separate to the Serilog configuration.

**.NET Framework**

Add the following section within the configSections element of your app.config or web.config file.
Expand Down
30 changes: 29 additions & 1 deletion src/Serilog.Sinks.Raygun/LoggerConfigurationRaygunExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
using Serilog.Configuration;
using Serilog.Events;
using Serilog.Sinks.Raygun;
#if NETSTANDARD2_0
using Microsoft.AspNetCore.Http;
using Mindscape.Raygun4Net.AspNetCore;
#endif

namespace Serilog
{
Expand All @@ -39,7 +43,8 @@ public static class LoggerConfigurationRaygunExtensions
/// <param name="tags">Specifies the tags to include with every log message. The log level will always be included as a tag.</param>
/// <param name="ignoredFormFieldNames">Specifies the form field names which to ignore when including request form data.</param>
/// <param name="groupKeyProperty">The property containing the custom group key for the Raygun message.</param>
/// <param name="tagsProperty">The property where additional tags are stored when emitting log events</param>
/// <param name="tagsProperty">The property where additional tags are stored when emitting log events.</param>
/// <param name="userInfoProperty">The property containing the RaygunIdentifierMessage structure used to populate user details.</param>
/// <returns>Logger configuration, allowing configuration to continue.</returns>
/// <exception cref="ArgumentNullException">A required parameter is null.</exception>
public static LoggerConfiguration Raygun(
Expand All @@ -62,5 +67,28 @@ public static LoggerConfiguration Raygun(
new RaygunSink(formatProvider, applicationKey, wrapperExceptions, userNameProperty, applicationVersionProperty, tags, ignoredFormFieldNames, groupKeyProperty, tagsProperty, userInfoProperty),
restrictedToMinimumLevel);
}

#if NETSTANDARD2_0
/// <summary>
/// Add the <see cref="RaygunClientHttpEnricher"/> to the enrichment configuration.
/// </summary>
/// <param name="enrich"></param>
/// <param name="httpContextAccessor">Optional HttpContext accessor that provides access to the current requests HttpContext.</param>
/// <param name="restrictedToMinimumLevel">Optional <see cref="LogEventLevel"/> to enrich log events. Defaults to LogEventLevel.Error.</param>
/// <param name="raygunSettings">Optional <see cref="RaygunSettings"/> that is used to apply http data filtering.</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static LoggerConfiguration WithHttpDataForRaygun(
this LoggerEnrichmentConfiguration enrich,
IHttpContextAccessor httpContextAccessor = null,
LogEventLevel restrictedToMinimumLevel = LogEventLevel.Error,
RaygunSettings raygunSettings = null)
{
if (enrich == null)
throw new ArgumentNullException(nameof(enrich));

return enrich.With(new RaygunClientHttpEnricher(httpContextAccessor ?? new HttpContextAccessor(), restrictedToMinimumLevel, raygunSettings ?? new RaygunSettings()));
}
#endif
}
}
11 changes: 6 additions & 5 deletions src/Serilog.Sinks.Raygun/Serilog.Sinks.Raygun.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,25 @@
<PackageTags>serilog sink raygun</PackageTags>
<Copyright>Copyright © Serilog Contributors 2017-2020</Copyright>
<Description>Serilog event sink that writes to the Raygun service.</Description>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionPrefix>5.1.0</VersionPrefix>
<RootNamespace>Serilog</RootNamespace>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
<PackageReference Include="Mindscape.Raygun4Net" Version="5.12.0" />
<PackageReference Include="Mindscape.Raygun4Net" Version="5.13.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="Mindscape.Raygun4Net" Version="5.12.0" />
<PackageReference Include="Mindscape.Raygun4Net" Version="5.13.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="mindscape.Raygun4Net.AspNetCore" Version="6.4.0" />
<PackageReference Include="mindscape.Raygun4Net.AspNetCore" Version="6.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.1.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="2.6.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections;
using System.Linq;
using Serilog.Events;

namespace Serilog.Sinks.Raygun
{
public static class LogEventPropertyExtensions
{
public static string AsString(this LogEventProperty property)
{
var scalar = property.Value as ScalarValue;
return scalar?.Value != null ? property.Value.ToString("l", null) : null;
}

public static int AsInteger(this LogEventProperty property, int defaultIfNull = 0)
{
var scalar = property.Value as ScalarValue;
return scalar?.Value != null ? int.TryParse(property.Value.ToString(), out int result) ? result : defaultIfNull : defaultIfNull;
}

public static IDictionary AsDictionary(this LogEventProperty property)
{
if (!(property.Value is DictionaryValue value)) return null;

return value.Elements.ToDictionary(
kv => kv.Key.ToString("l", null),
kv => kv.Value is ScalarValue scalarValue ? scalarValue.Value : kv.Value);
}
}
}
70 changes: 70 additions & 0 deletions src/Serilog.Sinks.Raygun/Sinks/Raygun/RaygunClientHttpEnricher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#if NETSTANDARD2_0
using System;
using Microsoft.AspNetCore.Http;
using Mindscape.Raygun4Net;
using Mindscape.Raygun4Net.AspNetCore;
using Mindscape.Raygun4Net.AspNetCore.Builders;
using Serilog.Core;
using Serilog.Events;

namespace Serilog.Sinks.Raygun
{
public class RaygunClientHttpEnricher : ILogEventEnricher
{
public const string RaygunRequestMessagePropertyName = "RaygunSink_RequestMessage";
public const string RaygunResponseMessagePropertyName = "RaygunSink_ResponseMessage";

readonly IHttpContextAccessor _httpContextAccessor;
private readonly LogEventLevel _restrictedToMinimumLevel;
private readonly RaygunSettings _raygunSettings;

public RaygunClientHttpEnricher(IHttpContextAccessor httpContextAccessor, LogEventLevel restrictedToMinimumLevel, RaygunSettings raygunSettings)
{
_httpContextAccessor = httpContextAccessor;
_restrictedToMinimumLevel = restrictedToMinimumLevel;
_raygunSettings = raygunSettings;
}

public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Level < _restrictedToMinimumLevel)
{
return;
}

if (_httpContextAccessor?.HttpContext == null)
{
return;
}

var options = new RaygunRequestMessageOptions
{
IsRawDataIgnored = _raygunSettings.IsRawDataIgnored,
UseXmlRawDataFilter = _raygunSettings.UseXmlRawDataFilter,
IsRawDataIgnoredWhenFilteringFailed = _raygunSettings.IsRawDataIgnoredWhenFilteringFailed,
UseKeyValuePairRawDataFilter = _raygunSettings.UseKeyValuePairRawDataFilter
};

options.AddCookieNames(_raygunSettings.IgnoreCookieNames ?? Array.Empty<string>());
options.AddHeaderNames(_raygunSettings.IgnoreHeaderNames ?? Array.Empty<string>());
options.AddFormFieldNames(_raygunSettings.IgnoreFormFieldNames ?? Array.Empty<string>());
options.AddQueryParameterNames(_raygunSettings.IgnoreQueryParameterNames ?? Array.Empty<string>());
options.AddSensitiveFieldNames(_raygunSettings.IgnoreSensitiveFieldNames ?? Array.Empty<string>());
options.AddServerVariableNames(_raygunSettings.IgnoreServerVariableNames ?? Array.Empty<string>());

RaygunRequestMessage httpRequestMessage = RaygunAspNetCoreRequestMessageBuilder
.Build(_httpContextAccessor.HttpContext, options)
.GetAwaiter()
.GetResult();

RaygunResponseMessage httpResponseMessage = RaygunAspNetCoreResponseMessageBuilder.Build(_httpContextAccessor.HttpContext);

// The Raygun request/response messages are stored in the logEvent properties collection.
// When the error is sent to Raygun, these messages are extracted from the known properties
// and then removed so as to not duplicate data in the payload.
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(RaygunRequestMessagePropertyName, httpRequestMessage, true));
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(RaygunResponseMessagePropertyName, httpResponseMessage, true));
}
}
}
#endif
Loading

0 comments on commit 477c4ed

Please sign in to comment.