Purview.Logging.SourceGenerator 0.8.1-prerelease

This is a prerelease version of Purview.Logging.SourceGenerator.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Purview.Logging.SourceGenerator --version 0.8.1-prerelease
NuGet\Install-Package Purview.Logging.SourceGenerator -Version 0.8.1-prerelease
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Purview.Logging.SourceGenerator" Version="0.8.1-prerelease" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Purview.Logging.SourceGenerator --version 0.8.1-prerelease
#r "nuget: Purview.Logging.SourceGenerator, 0.8.1-prerelease"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Purview.Logging.SourceGenerator as a Cake Addin
#addin nuget:?package=Purview.Logging.SourceGenerator&version=0.8.1-prerelease&prerelease

// Install Purview.Logging.SourceGenerator as a Cake Tool
#tool nuget:?package=Purview.Logging.SourceGenerator&version=0.8.1-prerelease&prerelease

Purview Logging Source Generator

.NET Logging Source Generator, used for generating LoggerMessage-based High Performance logging from a custom interface.

The interface-based approach has a few key benefits:

  • allows better testing through the use of mocks and assertions in your tests
  • interfaces and their methods are also more readable than LogXXX and strings.
  • natively supports DI.

How to

Reference the source generator in your CSPROJ file:

<ItemGroup>
	<PackageReference Include="Purview.Logging.SourceGenerator" Version="0.8.1-prerelease" />
</ItemGroup>

Create an interface (public or internal), make sure the name ends with any of the following (case-sensitive):

  • Log
  • Logs
  • Logger

Call services.AddLog<TInterfaceType>() on your DI registration and you're good to go! Inject or resolve as you see fit.

Currently you must have the Microsoft.Extensions.DepdencyInjection and Microsoft.Extensions.Logging packages installed along with the Purview.Logging.SourceGenerator package.

Quick demo:

Define the interface:

public interface IBasicLogger
{
	IDisposable BeginProcessing();

	void OperationPart1(string aStringParam);

	void OperationPart2(int anIntParam);

	void OperationPart3(SomeData aComplexTypeParam);

	void CompletedProcessing(TimeSpan duration);

	void FailedToProcess(Exception ex);
}

Notice here we're also using IDisposable for [scoped](Logging in .NET | Microsoft Docs)-supported logging.

Register with DI

services.AddLog<ITestLogger>() // this is an auto-generated extension method.

Use... !

// inject or resolve the IBasicLogger.

IBasicLogger logger = ...;

var contextId = Guid.NewGuid();
using (logger.BeginProcessing(contextId))
{
	// Do stuff...
	logger.OperationPart1("Some Param...!");

	// Do more stuff...
	logger.OperationPart2(99);

	// Do even more stuff...
	logger.OperationPart3(new SomeData {
		ACount = 1,
		Payload = "abc123"
	});
	
    // Completed...
	logger.CompletedProcessing(TimeSpan.FromSeconds(1.1));

	try
	{
		throw new InvalidOperationException("For Completeness we'll raise this too.");
	}
	catch (Exception ex)
	{
		logger.FailedToProcess(ex);
	}
}

See the output

Microsoft.Extensions.Logging.Console
info: LoggerTest.IBasicLogger[2]
      => BeginProcessing: fd0d99c9-bbf6-42e9-a90f-e99b5f217a89
      OperationPart1: Some Param...!
info: LoggerTest.IBasicLogger[3]
      => BeginProcessing: fd0d99c9-bbf6-42e9-a90f-e99b5f217a89
      OperationPart2: 99
info: LoggerTest.IBasicLogger[4]
      => BeginProcessing: fd0d99c9-bbf6-42e9-a90f-e99b5f217a89
      OperationPart3: Count: 1 @ 04/01/2022 15:21:29 +00:00: abc123
info: LoggerTest.IBasicLogger[5]
      => BeginProcessing: fd0d99c9-bbf6-42e9-a90f-e99b5f217a89
      CompletedProcessing: 00:00:01.1000000
fail: LoggerTest.IBasicLogger[6]
      => BeginProcessing: fd0d99c9-bbf6-42e9-a90f-e99b5f217a89
      FailedToProcess
      System.InvalidOperationException: For Completeness we'll raise this too.
Serilog.Extensions.Logger + Serilog.Sinks.Console
[15:13:17 INF] OperationPart1: Some Param...!
[15:13:17 INF] OperationPart2: 99
[15:13:17 INF] OperationPart3: Count: 1 @ 04/01/2022 15:21:30 +00:00: abc123
[15:13:17 INF] CompletedProcessing: 00:00:01.1000000
[15:13:17 ERR] FailedToProcess
System.InvalidOperationException: For Completeness we'll raise this too.

Log Event Configuration

By default each assembly where a logging interface is defined get two attributes generated that can be used to control the log event:

  1. DefaultLogLevelAttribute - use on an interface to control the default log level - system-wide, the default is Information.
  2. LogEventAttributte - use to configure individual log events, including Event Id, Event Name, Log Level and Message Template.

I was hoping to get the DefaultLogLevelAttribute working as an assembly attribute too to define the default at the assembly level, but haven't find a way of making this work yet so for now it's on a per-interface basis.

If no log level is defined (via the LogEventAttribute) and the method contains an Exception parameter, the level is automatically set to Error. That parameter is also passed to the Exception parameter of the Define method of the LoggerMessage.

Extensions

The generated classes are partial, and match the interfaces accessibility modifier (public or internal), their name is the interface name, with the I removed and Core suffixed to the end - simply as a means of preventing clashes.

It does mean you can extend the class if you really need too:

public interface IImportantLogger {  }	// Your interface.

public partial class ImportantLoggerCore : IImportantLogger {} // Generated logger.

partial class ImportantLoggerCore 
{
	public void MyAdditionalMethod()
	{
	     // ... 
	}
}

Notes

This project is very early days - code is very messy at the moment, and it doesn't have much in the way of testing currently. All this is in-part because Source Generators in incredibly hard to debug currently. As I get time, I'll improve the codebase and testability of the whole project.

There is a demo project. It's a bit of a mish-mash at the moment... I'll tidy it up later!

The history of this project was a little interesting, I've been doing this for years, but using C# generated at runtime and creating a dynamic assembly to enable this behaviour. Using Source Generators was a natural step forward.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.9.5.2-prerelease 6,364 1/3/2023
0.9.5.1-prerelease 99 1/3/2023
0.9.5-prerelease 106 1/3/2023
0.9.4.1-prerelease 2,991 5/3/2022
0.9.4-prerelease 135 4/15/2022
0.9.3.6-prerelease 105 4/4/2022
0.9.3.5-prerelease 123 3/30/2022
0.9.3.4-prerelease 139 1/20/2022
0.9.3.3-prerelease 162 1/19/2022
0.9.3.2-prerelease 121 1/14/2022
0.9.3.1-prerelease 124 1/13/2022
0.9.3-prerelease 123 1/12/2022
0.9.2.1-prerelease 120 1/11/2022
0.9.2-prerelease 129 1/11/2022
0.9.1-prerelease 123 1/10/2022
0.9.0-prerelease 179 1/8/2022
0.8.3-prerelease 122 1/6/2022
0.8.2-prerelease 143 1/6/2022
0.8.1-prerelease 165 1/4/2022