Skip to main content

Adding logging

· 2 min read

Adding logging for a project should be simple, right ?

After all, we have the ADR and we have ILogger from Microsoft and the NLOG Framework

TITLE: ADR 001 - Engine for documentation

Status : accepted

Context

Need to have a static documentation site with blog also .

In those days there are many engines on the market. And I can built one ;-) .

Read https://docusaurus.io/docs#comparison-with-other-tools . The only that seems to fit the bill are REACT based - I cannot afford learning VUE after Angular and React.

The problem with REACT is that he is going also to server side ...

Decision

Docusaurus it is because

  1. Apparently is it the best on REACT
  2. I know REACT
  3. It has BLOG and Documentation

Consequences

The build process will be somehow different.

Could be hard to transfer what I have written to another engine

ADR based on

https://github.com/joelparkerhenderson/architecture-decision-record/tree/main/locales/en/templates/decision-record-template-by-michael-nygard

Implementation

First, add to the constructor of each class the Ilogger parameter.

Then , to automatically instrument each method of class with logging , I use https://www.nuget.org/packages/rscg_decorator

namespace syncPowershellObjects;

static internal class GlobalLogging
{
public static void LogData(this ILogger logger, MethodRecognizer recognizer ,Exception? ex=null,[CallerMemberName] string name = "")
{
//TB: 2024-01-01 add activity tracing
//TB: 2024-01-01 add named parameters for logging
if (logger == null) return;
if (ex != null)
{
logger.LogError($"{name} {recognizer.UniqueId}");
if (recognizer.ValueTypeParameters.Count > 0)
logger.LogError($"{recognizer.ValueTypeParametersString}");
}
else
{

logger.LogInformation(ex, $"{name} {recognizer.UniqueId}");
if (recognizer.ValueTypeParameters.Count > 0)
logger.LogInformation(ex,$"{recognizer.ValueTypeParametersString}");
}

}
}

And in the class


public partial class ReceiveData_Decorator
{
}

partial class ReceiveData : IDecoratorMethodV1
{
public void EndMethod(MethodRecognizer recognizer)
{
logger.LogData(recognizer);
}

public void ExceptionMethod(Exception ex, MethodRecognizer recognizer)
{
logger.LogData(recognizer);
}

public void StartMethod(MethodRecognizer recognizer)
{
logger.LogData(recognizer);

}
}

The first problem is that many classes in NLOG and Microsoft Logging differs just by namespace ( ILogger, LogLevel ) . So you must be careful when write (global ) using namespaces

Second, XUnit has his ITestOutputHelper too - so we have to integrate also this.

To combine multiple loggers, CompositeLogger class is necessary . And also, because BeginScope is IDisposable , also a CompositeDisposable is necessary .

So this is the final code, that integrates OutputHelper as logger

//Divergic.Logging.Xunit
var loggerOutput = outputHelper.BuildLoggerFor<TestUploadAndRetrieve>();
//simple way
//ILoggerFactory factory = LoggerFactory.Create(builder =>builder.AddNLog());
NLog.LogFactory f = new NLog.LogFactory();
NLog.SetupBuilderExtensions.LoadConfigurationFromFile(f.Setup(),"nlog.config");
var config = new NLog.Config.LoggingConfiguration(f);

var target = new XunitLoggerTarget(outputHelper);
config.AddTarget("Xunit", target);

config.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Trace, target));

ILoggerFactory factory = LoggerFactory.Create(b => b.AddNLog(config));
logger = new CompositeLogger(factory.CreateLogger<TestUploadAndRetrieve>(), loggerOutput);

Realise that could be simpler with a simple logger => so a simpler version

NLog.LogFactory f = new NLog.LogFactory();
var config = new NLog.Config.LoggingConfiguration(f);

var target = new XunitLoggerTarget(outputHelper);
config.AddTarget("Xunit", target);

config.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Trace, target));

ILoggerFactory factory = LoggerFactory.Create(b => b.AddNLog(config));
logger = factory.CreateLogger<TestUploadAndRetrieve>();

Adding to console it is for another time