RJCP.Diagnostics.Trace
0.2.1
dotnet add package RJCP.Diagnostics.Trace --version 0.2.1
NuGet\Install-Package RJCP.Diagnostics.Trace -Version 0.2.1
<PackageReference Include="RJCP.Diagnostics.Trace" Version="0.2.1" />
paket add RJCP.Diagnostics.Trace --version 0.2.1
#r "nuget: RJCP.Diagnostics.Trace, 0.2.1"
// Install RJCP.Diagnostics.Trace as a Cake Addin #addin nuget:?package=RJCP.Diagnostics.Trace&version=0.2.1 // Install RJCP.Diagnostics.Trace as a Cake Tool #tool nuget:?package=RJCP.Diagnostics.Trace&version=0.2.1
RJCP.Diagnostics.Trace
This assembly is to assist with logging be extending common operations on top of
System.Diagnostic
.
Projects using this library allow for easy compatibility between .NET Framework and .NET Core when porting, and allow easy reconfiguration of the trace sources.
1. Motivation
1.1. Intended Use Cases
The software I develop is primarily console applications, windows forms applications, and reusable libraries. This library is intended primarily for these scenarios.
1.2. .NET Framework works with .NET Core
This library is intended for usage in your own .NET Framework and .NET Standard libraries.
With .NET 2.0 to .NET 4.8, the .NET Framework offered the TraceListener
and
TraceSource
which read a configuration file and provided a static singleton to
obtain logging. With the advent of .NET Core, this was replaced with the
ILoggerFactory
and ILogger
using dependency injection, making it for library
designers and console applications more complicated. Further, compatibility was
lost with .NET Core that no longer would properly instantiate a TraceListener
from the application config.
This library provides a LogSource
class, that when your library uses, will
receive a TraceListener
. The TraceListener
is the lowest common denominator
for both .NET Framework and .NET Core.
Users of .NET Standard can then use the LogSource.SetLoggerFactory
or
LogSource.SetLogger
with a category name that the library can use to get the
user desired logging, resulting in common code for both frameworks, reducing
fragmentation.
2. Examples of Usage
2.1. .NET Framework and .NET Core Code
Your code should get the TraceListener
from the LogSource
.
var logger = new LogSource("MyCategory");
logger.TraceEvent(TraceEventType.Information, "Log Message");
2.2. .NET Framework Client
There is nothing in particular that needs to be done, other than to define the
trace listener in the app.config
file.
<configuration>
<system.diagnostics>
<sources>
<source name="MyCategory" switchValue="Information">
<listeners>
<clear/>
<add name="consoleListener"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener" />
</sharedListeners>
</system.diagnostics>
</configuration>
2.3. .NET Core Client
Because .NET Core doesn't read the configuration file, the TraceListener
will
only ever instantiate the DefaultTraceListener
and logging won't work. We need
to create an ILoggerFactory
. The easiest way to do this in a console
application:
internal static ILoggerFactory GetConsoleFactory()
{
return LoggerFactory.Create(builder => {
builder
.AddFilter("MyCategory", LogLevel.Debug)
.AddConsole();
});
}
And then assign it:
LogSource.SetLoggerFactory(GetConsoleFactory());
2.3.1. Reading from a Configuration File
The previous section can be modified slightly to read from a configuration file:
internal static ILoggerFactory GetConsoleFactory()
{
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, false)
.Build();
return LoggerFactory.Create(builder => {
builder
.AddConfiguration(config.GetSection("Logging"))
.AddConsole();
});
}
And then assign it:
LogSource.SetLoggerFactory(GetConsoleFactory());
2.3.2. Unit Testing with NUnit 3.x
Your unit tests may want to provide its own logging levels independent of the object being tested. If your .NET core initializes and sets the factory similar to the previous section, all code will use that factory for logging.
In .NET Core NUnit projects, you could define a file called
TestSetupFixture.cs
that is similar to:
namespace App {
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using RJCP.CodeQuality.NUnitExtensions.Trace;
using RJCP.Diagnostics.Trace;
[SetUpFixture]
public class TestSetupFixture {
[OneTimeSetUp]
public void GlobalSetup() {
GlobalLogger.Initialize();
}
}
internal static class GlobalLogger {
static GlobalLogger() {
ILoggerFactory factory = LoggerFactory.Create(builder => {
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("RJCP.Diagnostics.Log", LogLevel.Debug)
.AddNUnitLogger();
});
LogSource.SetLoggerFactory(factory);
}
// Just calling this method will result in the static constructor being executed.
public static void Initialize() {
/* Can be empty, reference will initialize static constructor */
}
}
}
So long as GlobalLogger.Initialize()
is called before the code being tested
can initialize the LogSource.SetLoggerFactory
, it will work.
3. Known Issues
3.1. Using ILogger with NUnit Tests
Using the .NET Core ConsoleLogger
will not work in NUnit Test cases. Use my
RJCP.DLL.CodeQuality
library to get a NUnitLogger
.
The ConsoleLogger
doesn't work in NUnit, as it creates a thread in the
background that does the logging. This thread maintains a handle to the console,
and is incompatible with the way that NUnit also tries to capture th console and
redirect itself. That's why the workaround for console logging was to completely
instantiate the factory on every test case, which is inefficient, but would
reset the background thread allowing NUnit to capture the console again at the
start of each test. But it still didn't solve the race conditions that might
sometimes occur due to console logging actually being done in another thread,
and so some console output might actually be captured for a different test case.
See GitHub Issue #3919.
4. Release History
4.1. Version 0.2.1
Quality:
- Add reference to README.md in NuGet package (DOTNET-810)
- Trace: Upgrade to .NET Core 6.0 (DOTNET-936, DOTNET-942, DOTNET-945, DOTNET-959)
- Update to .NET 8.0 (DOTNET-982, DOTNET-983, DOTNET-989, DOTNET-990)
4.2. Version 0.2.0
- Initial Version
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Framework | net40 is compatible. net403 was computed. net45 was computed. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.0
- RJCP.Core.SysCompat (>= 0.2.0)
-
net6.0
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.4)
- RJCP.Core.SysCompat (>= 0.2.0)
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- RJCP.Core.SysCompat (>= 0.2.0)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on RJCP.Diagnostics.Trace:
Package | Downloads |
---|---|
RJCP.SerialPortStream
An independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability. |
|
RJCP.IO.Device
Get Windows Device Information. |
|
RJCP.Diagnostics.Log
Parse external log streams. |
|
RJCP.Diagnostics.Log.Dlt
An AutoSAR DLT v1 Decoder and Encoder. |
|
RJCP.Diagnostics.CrashReporter
Library tools to allow developers to capture debug information. |
GitHub repositories
This package is not used by any popular GitHub repositories.