Skip to content

Commit

Permalink
Imported Tingle.Extensions.Serilog (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
mburumaxwell authored Sep 19, 2023
1 parent d0d6b69 commit 686bd48
Show file tree
Hide file tree
Showing 20 changed files with 532 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"Serilog"
]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This repository contains projects/libraries for adding useful functionality to d
|`Tingle.Extensions.JsonPatch`|[![NuGet](https://img.shields.io/nuget/v/Tingle.Extensions.JsonPatch.svg)](https://www.nuget.org/packages/Tingle.Extensions.JsonPatch/)|JSON Patch (RFC 6902) support for .NET to easily generate JSON Patch documents using `System.Text.Json` for client applications. See [docs](./src/Tingle.Extensions.JsonPatch/README.md).|
|`Tingle.Extensions.PhoneValidators`|[![NuGet](https://img.shields.io/nuget/v/Tingle.Extensions.PhoneValidators.svg)](https://www.nuget.org/packages/Tingle.Extensions.PhoneValidators/)|Convenience for validation of phone numbers either via attributes or resolvable services. See [docs](./src/Tingle.Extensions.PhoneValidators/README.md).|
|`Tingle.Extensions.Processing`|[![NuGet](https://img.shields.io/nuget/v/Tingle.Extensions.Processing.svg)](https://www.nuget.org/packages/Tingle.Extensions.Processing/)|Helpers for making processing of bulk in memory tasks. See [docs](./src/Tingle.Extensions.Processing/README.md).|
|`Tingle.Extensions.Serilog`|[![NuGet](https://img.shields.io/nuget/v/Tingle.Extensions.Serilog.svg)](https://www.nuget.org/packages/Tingle.Extensions.Serilog/)|Extensions for working with [Serilog](https://serilog.net/). Including easier registration when working with different host setups, and general basics. See [docs](./src/Tingle.Extensions.Serilog/README.md) and [sample](./samples/SerilogSample).|

### Issues & Comments

Expand Down
21 changes: 21 additions & 0 deletions Tingle.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PhoneVali
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Processing", "src\Tingle.Extensions.Processing\Tingle.Extensions.Processing.csproj", "{A803DE4B-B050-48F2-82A1-8E947D8FB96C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.Serilog", "src\Tingle.Extensions.Serilog\Tingle.Extensions.Serilog.csproj", "{29035EF2-2391-4441-AAC5-85AA43586EEB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{815F0941-3B70-4705-A583-AF627559595C}"
ProjectSection(SolutionItems) = preProject
tests\Directory.Build.props = tests\Directory.Build.props
Expand Down Expand Up @@ -57,6 +59,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.PhoneVali
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Processing.Tests", "tests\Tingle.Extensions.Processing.Tests\Tingle.Extensions.Processing.Tests.csproj", "{978023EA-2ED5-4A28-96AD-4BB914EF2BE5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.Serilog.Tests", "tests\Tingle.Extensions.Serilog.Tests\Tingle.Extensions.Serilog.Tests.csproj", "{8E611861-09A3-4AE4-8392-E7CB9BE02B3B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}"
ProjectSection(SolutionItems) = preProject
samples\Directory.Build.props = samples\Directory.Build.props
Expand All @@ -72,6 +76,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataProtectionMongoDBSample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpAuthenticationSample", "samples\HttpAuthenticationSample\HttpAuthenticationSample.csproj", "{37ED98F0-3129-49B8-BF60-3BDEBBB34822}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerilogSample", "samples\SerilogSample\SerilogSample.csproj", "{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "samples\TokensSample\TokensSample.csproj", "{AC04C113-8F75-43BA-8FEE-987475A87C58}"
EndProject
Global
Expand Down Expand Up @@ -192,6 +198,18 @@ Global
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC04C113-8F75-43BA-8FEE-987475A87C58}.Release|Any CPU.Build.0 = Release|Any CPU
{29035EF2-2391-4441-AAC5-85AA43586EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29035EF2-2391-4441-AAC5-85AA43586EEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29035EF2-2391-4441-AAC5-85AA43586EEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29035EF2-2391-4441-AAC5-85AA43586EEB}.Release|Any CPU.Build.0 = Release|Any CPU
{8E611861-09A3-4AE4-8392-E7CB9BE02B3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E611861-09A3-4AE4-8392-E7CB9BE02B3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E611861-09A3-4AE4-8392-E7CB9BE02B3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E611861-09A3-4AE4-8392-E7CB9BE02B3B}.Release|Any CPU.Build.0 = Release|Any CPU
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -225,6 +243,9 @@ Global
{ACED6271-2F75-4E3B-BB79-9D40B74D6A51} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{37ED98F0-3129-49B8-BF60-3BDEBBB34822} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{AC04C113-8F75-43BA-8FEE-987475A87C58} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{29035EF2-2391-4441-AAC5-85AA43586EEB} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{8E611861-09A3-4AE4-8392-E7CB9BE02B3B} = {815F0941-3B70-4705-A583-AF627559595C}
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B9323FCA-8E8B-4176-A463-87D202EC4552}
Expand Down
34 changes: 34 additions & 0 deletions samples/SerilogSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var host = Host.CreateDefaultBuilder(args)
.UseSerilog(builder =>
{
builder.ConfigureSensitiveDataMasking(options =>
{
options.ExcludeProperties.Add("SomeNecessaryProperty");
});
})
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();

await host.RunAsync();

class Worker : BackgroundService
{
private readonly ILogger logger;

public Worker(ILogger<Worker> logger)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time:R}", DateTimeOffset.Now);
await Task.Delay(2000, stoppingToken);
}
}
}
11 changes: 11 additions & 0 deletions samples/SerilogSample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"SerilogSample": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}
14 changes: 14 additions & 0 deletions samples/SerilogSample/SerilogSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<UserSecretsId>cfddbd80-2b06-4021-9fae-d2f1ecca48af</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Tingle.Extensions.Serilog\Tingle.Extensions.Serilog.csproj" />
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions samples/SerilogSample/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
8 changes: 8 additions & 0 deletions samples/SerilogSample/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
14 changes: 14 additions & 0 deletions src/Tingle.Extensions.Serilog/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;
using Serilog.Events;

namespace Microsoft.Extensions.Configuration;

internal static class ConfigurationExtensions
{
public static LogEventLevel GetDefaultEventLevelForProvider(this IConfiguration configuration, string providerName)
{
return Enum.TryParse<LogLevel>(configuration[$"Logging:{providerName}:LogLevel:Default"], ignoreCase: true, out var parsed)
? parsed.ToLogEventLevel()
: LogEventLevel.Verbose;
}
}
37 changes: 37 additions & 0 deletions src/Tingle.Extensions.Serilog/ConvertedSerilogSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Configuration;

namespace Tingle.Extensions.Serilog;

internal class ConvertedSerilogSettings : ILoggerSettings
{
private readonly IConfiguration configuration;

public ConvertedSerilogSettings(IConfiguration configuration)
{
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}

public void Configure(LoggerConfiguration loggerConfiguration)
{
const string DefaultKey = "Default";

// configure provider-agnostic levels
var children = configuration.GetSection("LogLevel").GetChildren();
foreach (var child in children)
{
var source = child.Key;
var level = Enum.Parse<LogLevel>(child.Value!).ToLogEventLevel();
if (string.Equals(source, DefaultKey, StringComparison.OrdinalIgnoreCase))
{
loggerConfiguration.MinimumLevel.Is(level);
}
else
{
loggerConfiguration.MinimumLevel.Override(source, level);
}
}
}
}
52 changes: 52 additions & 0 deletions src/Tingle.Extensions.Serilog/EnvironmentEnricher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Microsoft.Extensions.Hosting;
using Serilog.Core;
using Serilog.Events;
using System.Reflection;

namespace Tingle.Extensions.Serilog;

internal class EnvironmentEnricher : ILogEventEnricher
{
private readonly IHostEnvironment environment;

private LogEventProperty? applicationName, applicationVersion;
private LogEventProperty? environmentName, machineName;

public EnvironmentEnricher(IHostEnvironment environment)
{
this.environment = environment ?? throw new ArgumentNullException(nameof(environment));
}

public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
applicationName ??= propertyFactory.CreateProperty(
name: "ApplicationName",
value: environment.ApplicationName);

applicationVersion ??= propertyFactory.CreateProperty(
name: "ApplicationVersion",
value: GetVersion());

environmentName ??= propertyFactory.CreateProperty(
name: "EnvironmentName",
value: environment.EnvironmentName);

machineName ??= propertyFactory.CreateProperty(
name: "MachineName",
value: Environment.MachineName);

logEvent.AddPropertyIfAbsent(applicationName);
logEvent.AddPropertyIfAbsent(applicationVersion);
logEvent.AddPropertyIfAbsent(environmentName);
logEvent.AddPropertyIfAbsent(machineName);
}

private static string GetVersion()
{
var assembly = Assembly.GetEntryAssembly()!;
var attr = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
return attr != null && !string.IsNullOrWhiteSpace(attr.InformationalVersion)
? attr.InformationalVersion
: assembly.GetName().Version!.ToString(3);
}
}
48 changes: 48 additions & 0 deletions src/Tingle.Extensions.Serilog/IHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.Hosting;

/// <summary>Extension methods for <see cref="IHostBuilder"/>.</summary>
public static class IHostBuilderExtensions
{
/// <summary>
/// Add Serilog services via <see cref="SerilogBuilder"/>.
/// This replaces the default <see cref="ILoggerProvider"/>.
/// </summary>
/// <param name="builder">The <see cref="IHostBuilder"/> builder to configure.</param>
/// <param name="setupAction">An optional action for setting up Serilog.</param>
/// <remarks>
/// By default, this sets up destructuring of simple exceptions, sensitive data masking, reading of log levels
/// from the <c>Logging:</c> configuration section, writing to console, debug and SEQ.
/// For more specifics you can inspect <see cref="SerilogBuilder"/>.
/// The logger will be shut down when application services are disposed.
/// <br/>
/// <br/>
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
/// </remarks>
public static IHostBuilder UseSerilog(this IHostBuilder builder, Action<SerilogBuilder> setupAction)
=> builder.UseSerilog((_, builder) => setupAction?.Invoke(builder));

/// <summary>
/// Add Serilog services via <see cref="SerilogBuilder"/>.
/// This replaces the default <see cref="ILoggerProvider"/>.
/// </summary>
/// <param name="builder">The <see cref="IHostBuilder"/> builder to configure.</param>
/// <param name="setupAction">An optional action for setting up Serilog.</param>
/// <remarks>
/// By default, this sets up destructuring of simple exceptions, sensitive data masking, reading of log levels
/// from the <c>Logging:</c> configuration section, writing to console, debug and SEQ.
/// For more specifics you can inspect <see cref="SerilogBuilder"/>.
/// The logger will be shut down when application services are disposed.
/// <br/>
/// <br/>
/// A <see cref="HostBuilderContext"/> is supplied so that configuration and hosting information can be used.
/// </remarks>
public static IHostBuilder UseSerilog(this IHostBuilder builder, Action<HostBuilderContext, SerilogBuilder>? setupAction = null)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.ConfigureServices((context, services) => services.AddSerilog(builder => setupAction?.Invoke(context, builder)));
}
}
49 changes: 49 additions & 0 deletions src/Tingle.Extensions.Serilog/IServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>Extension methods for <see cref="IServiceCollection"/>.</summary>
public static class IServiceCollectionExtensions
{
/// <summary>
/// Add Serilog services via <see cref="SerilogBuilder"/>.
/// This replaces the default <see cref="ILoggerProvider"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> instance to add services to.</param>
/// <returns>An <see cref="SerilogBuilder"/> to continue setting up Serilog.</returns>
/// <remarks>
/// By default, this sets up destructuring of simple exceptions, sensitive data masking, reading of log levels
/// from the <c>Logging:</c> configuration section, writing to console, debug and SEQ.
/// For more specifics you can inspect <see cref="SerilogBuilder"/>.
/// The logger will be shut down when application services are disposed.
/// </remarks>
public static SerilogBuilder AddSerilog(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);
return new SerilogBuilder(services);
}

/// <summary>
/// Add Serilog services via <see cref="SerilogBuilder"/>.
/// This replaces the default <see cref="ILoggerProvider"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> instance to add services to.</param>
/// <param name="setupAction">An optional action for setting up Serilog.</param>
/// <returns></returns>
/// <remarks>
/// By default, this sets up destructuring of simple exceptions, sensitive data masking, reading of log levels
/// from the <c>Logging:</c> configuration section, writing to console, debug and SEQ.
/// For more specifics you can inspect <see cref="SerilogBuilder"/>.
/// The logger will be shut down when application services are disposed.
/// </remarks>
public static IServiceCollection AddSerilog(this IServiceCollection services, Action<SerilogBuilder>? setupAction = null)
{
ArgumentNullException.ThrowIfNull(services);

var builder = services.AddSerilog();

setupAction?.Invoke(builder);

return services;
}
}
22 changes: 22 additions & 0 deletions src/Tingle.Extensions.Serilog/LoggingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Serilog.Events;

namespace Microsoft.Extensions.Logging;

internal static class LoggingExtensions
{
public static LogEventLevel ToLogEventLevel(this LogLevel level)
{
// https://github.com/serilog/serilog-extensions-logging/blob/e25ed7ddfd0c664bd3a7e9cdbeeec6f87ff12964/src/Serilog.Extensions.Logging/Extensions/Logging/LevelConvert.cs#L35-L54
return level switch
{
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Critical => LogEventLevel.Fatal,
LogLevel.None => LogEventLevel.Fatal,
_ => LogEventLevel.Verbose,
};
}
}
15 changes: 15 additions & 0 deletions src/Tingle.Extensions.Serilog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Tingle.Extensions.Serilog

This library provides convenience extensions and logic for registering Serilog in applications with support for various hosts while setting up basic thins to match the default logging setup in the framework.

This includes:

- Destructuring is enabled/added by default
- Sensitive data masking is enabled/added by default.
- Console and Debug are registered by default.
- Enrichment of the current environment based on `IHostEnvironment`
- Conversion of log levels hence you can continue to use the `Logging` section.
- [SEQ](https://datalust.co/seq) is added by default when `Logging:Seq:ServerUrl` or `Seq:ServerUrl` configuration is available. (Use `Logging:Seq:ApiKey` or `Seq:ApiKey` to set the key).
- Registration via `IServiceCollection` and `IHostBuilder` (with the defaults above)

Consult the [sample](https://github.com/tinglesoftware/dotnet-extensions/tree/main/samples/SerilogSample) or the [builder](https://github.com/tinglesoftware/dotnet-extensions/blob/main/src/Tingle.Extensions.Serilog/SerilogBuilder.cs) for more on how to use or what happens underneath.
Loading

0 comments on commit 686bd48

Please sign in to comment.