Check log message in unit test by Nlog C# - c#

I am looking for a way to test Log message by using Nlog
Typically, all of my classes has a private member _logger
Ex:
using NLog;
public class MyClass {
private readonly Logger _logger;
public MyClass(){
this._logger = LogManager.GetCurrentClassLogger();
}
}
Whenever I need to log the message, I'll call
_logger.info('message')
Since C# isn't allowed me to inject the Log object dynamically, I was wondering how to test the message in the unit test? I am thinking somehow this way.
[TestMethod]
public void TestLogMessage() {
var testGerrits = MyClass();
// mock the logger?
Assert.AreEqual(logger[0], "expected message");
}

Related

How to make Microsoft.Extensions.Logging available for all classes

I just started learning C#, and am redoing past Java projects. I am trying to use Microsoft.Extensions.Logging, and I want to be able to make it available for all my classes in my console application.
Examples I referred to creates a LoggerFactory in the Main() method:
https://www.tutorialsteacher.com/core/fundamentals-of-logging-in-dotnet-core
https://thecodeblogger.com/2021/05/11/how-to-enable-logging-in-net-console-applications/
How can I make MEL loggers available for all classes similar to how log4net/serilog does it? I did refer to microsoft documentation but I m not very familiar with Dependency Injection in C#.
I could use log4net instead, however I saw a question thread on SO, that suggested it's better to program to an logging abstraction, as you can easily change logging providers later on depending on your needs.
ie:
class MyDomain
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private void SomeFunc()
{
_logger.Trace("this is a test");
}
}
You can create an interface that defines the methods that you want to use for logging.
eg
public interface ILog
{
void LogInformation(string message);
void LogWarning(string message);
void LogError(string message);
}
Then you can implement this into a class where you are using Microsoft.Extensions.Logging to log.
public class Log : ILog
{
private readonly ILogger _logger;
public Logger(ILogger<Logger> logger)
{
_logger = logger;
}
public void LogInformation(string message)
{
_logger.LogInformation(message);
}
}
Now you can use ILog in all the classes and in the future if you want to change the logging provider you can.
Note: This is a simple implementation. you can make this more dynamic depending on your needs.

PostSharp - logger as an aspect argument

Want to use PostSharp diagnostics with an aspect for exception handling encapsulated in a library assembly.
At the same time trying to setup and initialize a Serilog logger in the consumer assembly of that diagnostics library.
[AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.After,
typeof(AddContextOnExceptionAttribute))]
[PSerializable]
public sealed class ReportAndSwallowExceptionAttribute : OnExceptionAspect
{
public ILogger TheLogger { get; set; }
public ReportAndSwallowExceptionAttribute(ILogger logger)
{
TheLogger = logger;
}
public override void OnException(MethodExecutionArgs args)
{
TheLogger.Error("Error happened ...");
}
}
In the main class:
class Program
{
// below line is the critical part which seems ILogger is not allowed
[ReportAndSwallowException(Log.Logger)]
public static void TryExceptionMethod()
{
throw new NotImplementedException();
}
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.File(#"HERE\Logs\log-x.log",
rollingInterval: RollingInterval.Day)
.CreateLogger();
Console.WriteLine("Hello World!");
TryExceptionMethod();
}
}
Seems passing ILogger to that attribute is illegal, how can I achieve this scenario?
Current Error:
Error CS0181: Attribute constructor parameter 'logger' has type 'ILogger', which is not a valid attribute parameter type
Think this error needs a constant to be solved, but the main question is how to achieve this scenario: have the logger in the consumer proj, have the aspects in a library.
The easiest option here is that your aspect directly references Log.Logger.
There are several other more complex options that are documented here: https://doc.postsharp.net/consuming-dependencies

NLog EventProperties in .NET Core

I'm using ILogger<MyController> to write logs using DI (see step 6)
I'm also using NLog EventProperties
I want to add traceId to all my logs in my controller automatically.
This is working:
logger.Info("Some log. TraceId:{traceId}", 123);
However, then I need to change all my log commands (a lot of them!), which is a pain.
If I do the following, it's not tread safe:
using NLog;
public class MyController : Controller
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public MyConstructor(Apilog apilog)
{
Logger.SetProperty("traceid", apilog.TraceId);
}
}
Is there some way to use SetProperty with ILogger<MyController>?
Or some way of using NLog with SetProperty in a fast and thread safe way?
Many thanks in advance!
When you share loggers between threads, then you could use WithProperty to ensure thread safeness.
Please note that WithProperty/SetProperty is only available on the Logger of NLog. (so not ILogger of Microsoft.Extensions.Logging`). See below.
Example:
using NLog;
public class MyController : Controller
{
private static readonly Logger BaseLogger = LogManager.GetCurrentClassLogger();
private readonly Logger Logger; // non static because of traceid.
public MyConstructor(Apilog apilog)
{
Logger = BaseLogger.WithProperty("traceid", apilog.TraceId);
}
}
Is there some way to use SetProperty with ILogger<MyController>?
There is no SetProperty on the ILogger interface of Microsoft.Extensions.Logging, but you could do this:
using (_logger.BeginScope(new[] { new KeyValuePair<string, object>("traceid", apilog.TraceId) }))
{
_logger.LogDebug("Log message");
}
And use in your config: ${mdlc:traceid}. See more details and options here

Unexpected behavior Log4NetExtension in combination with registered IEnumerable<T> in Unity container

I am using log4net as logging framework and Unity as my IoC container. Next, I have installed the Unity.log4net nuget package. I run into an issue with the logger name when I start using named registrations. I use named registrations to enable constructor injection of an IEnumerable of T.
The code below is a very simple program which shows my problem.
using Container.Activities;
using log4net;
using Unity;
using Unity.log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace Container
{
class Program
{
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();
container.AddNewExtension<Log4NetExtension>();
container.RegisterType<IActivity, ActivityA>(nameof(ActivityA));
container.RegisterType<IActivity, ActivityB>(nameof(ActivityB));
container.RegisterType<IActivityExecutor, ActivityExecutor>();
var logger = LogManager.GetLogger("Test application");
logger.Info("Application started.");
container.Resolve<IActivityExecutor>().Execute();
logger.Info("Application stopped.");
}
}
}
I create an Activity interface and will have multiple implementations of this interface.
namespace Container.Activities
{
public interface IActivity
{
void Execute();
}
}
In this example I have just two implementations, Activity A and B. Both implementations require an log4net.ILog implementation to be injected in the constructor.
using log4net;
namespace Container.Activities
{
public class ActivityA : IActivity
{
private readonly ILog _logger;
public ActivityA(ILog logger)
{
_logger = logger;
}
public void Execute()
{
_logger.Info($"Hello, I am {nameof(ActivityA)}.");
}
}
}
Activity B:
using log4net;
namespace Container.Activities
{
public class ActivityB : IActivity
{
private readonly ILog _logger;
public ActivityB(ILog logger)
{
_logger = logger;
}
public void Execute()
{
_logger.Info($"Hello, I am {nameof(ActivityB)}.");
}
}
}
Next to multiple IActivity instances, I have an IActivityExecutor interface.
namespace Container.Activities
{
public interface IActivityExecutor
{
void Execute();
}
}
The Activity Executor instance should execute all registered IActivity instances. Next it has its own ILog instance injected so it can write to the log file as well.
using log4net;
using System.Collections.Generic;
using System.Linq;
namespace Container.Activities
{
public class ActivityExecutor : IActivityExecutor
{
private readonly ILog _logger;
private readonly IEnumerable<IActivity> _activities;
public ActivityExecutor(ILog logger, IEnumerable<IActivity> activities)
{
_logger = logger;
_activities = activities;
}
public void Execute()
{
_logger.Info($"About to process {_activities.Count()}.");
foreach (var activity in _activities)
{
activity.Execute();
}
}
}
}
The output in my log file becomes as shown below. Note both ActivityA and ActivityB end up in the log file with logger name ActivityExecutor.
2019-03-19 14:43 [Test application] [1] INFO - Application started.
2019-03-19 14:43 [Container.Activities.ActivityExecutor] [1] INFO - About to process 2.
2019-03-19 14:43 [Container.Activities.ActivityExecutor] [1] INFO - Hello, I am ActivityA.
2019-03-19 14:43 [Container.Activities.ActivityExecutor] [1] INFO - Hello, I am ActivityB.
2019-03-19 14:43 [Test application] [1] INFO - Application stopped.
This output is not what I expect. I would expect to see the below log instead. So each activity logs using its own name.
2019-03-19 14:43 [Test application] [1] INFO - Application started.
2019-03-19 14:43 [Container.Activities.ActivityExecutor] [1] INFO - About to process 2.
2019-03-19 14:43 [Container.Activities.ActivityA] [1] INFO - Hello, I am ActivityA.
2019-03-19 14:43 [Container.Activities.ActivityB] [1] INFO - Hello, I am ActivityB.
2019-03-19 14:43 [Test application] [1] INFO - Application stopped.
The root cause for this issue seems to be related to the use of named registrations in my Unity container. If I add another interface parameter to the constructor of the ActivityExecutor and put that before the ILog parameter, the name of that component is used in the log file.
I can remove the ILog from all constructors in this program, and instead call the LogManager to get a logger with the name of each component. However, I prefer to inject the ILog interface when needed and use the Unity.log4net package.
_logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
Am I missing something here?

Using Ninject to fill Log4Net Dependency

I use Ninject as a DI Container in my application. In order to loosely couple to my logging library, I use an interface like this:
public interface ILogger
{
void Debug(string message);
void Debug(string message, Exception exception);
void Debug(Exception exception);
void Info(string message);
...you get the idea
And my implementation looks like this
public class Log4NetLogger : ILogger
{
private ILog _log;
public Log4NetLogger(ILog log)
{
_log = log;
}
public void Debug(string message)
{
_log.Debug(message);
}
... etc etc
A sample class with a logging dependency
public partial class HomeController
{
private ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
When instantiating an instance of Log4Net, you should give it the name of the class for which it will be logging. This is proving to be a challenge with Ninject.
The goal is that when instantiating HomeController, Ninject should instantiate ILog with a "name" of "HomeController"
Here is what I have for config
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
private string GetParentTypeName(IContext context)
{
return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName;
}
}
However the "Name" that is being passed to ILog is not what I'm expecting. I can't figure out any rhyme or reason either, sometimes it's right, most of the time it's not. The Names that I'm seeing are names of OTHER classes which also have dependencies on the ILogger.
I personally have no interest in abstracting away my logger, so my implementation modules reference log4net.dll directly and my constructors request an ILog as desired.
To achieve this, a one line registration using Ninject v3 looks like this at the end of my static void RegisterServices( IKernel kernel ):
kernel.Bind<ILog>().ToMethod( context=>
LogManager.GetLogger( context.Request.Target.Member.ReflectedType ) );
kernel.Get<LogCanary>();
}
class LogCanary
{
public LogCanary(ILog log)
{
log.Debug( "Debug Logging Canary message" );
log.Info( "Logging Canary message" );
}
}
For ease of diagnosing logging issues, I stick the following at the start to get a non-DI driven message too:
public static class NinjectWebCommon
{
public static void Start()
{
LogManager.GetLogger( typeof( NinjectWebCommon ) ).Info( "Start" );
Which yields the following on starting of the app:
<datetime> INFO MeApp.App_Start.NinjectWebCommon - Start
<datetime> DEBUG MeApp.App_Start.NinjectWebCommon+LogCanary - Debug Logging Canary message
<datetime> INFO MeApp.App_Start.NinjectWebCommon+LogCanary - Logging Canary message
The Ninject.Extension.Logging extension already provides all you are implementing yourself. Including support for log4net, NLog and NLog2.
https://github.com/ninject/ninject.extensions.logging
Also you want to use the following as logger type:
context.Request.ParentRequest.ParentRequest.Target.Member.DeclaringType
Otherwise you will get the logger for the service type instead of the implementation type.
The Scope of ILog and ILogger needs to be Transient, otherwise it will just reuse the first logger that it creates. Thanks to #Meryln Morgan-Graham for helping me find that.
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
You are currently binding in Singleton scope, so only one logger is created which will use the name of the first one created. Instead use InTransientScope()
maybe my answer is late but I'm using this format:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILog>()
.ToMethod(c => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType))
.InSingletonScope();
}
For all of you that are still looking for the correct answer, the correct implementation is :
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(x.Request.Target.Member.DeclaringType));
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
}
Emphasis on:
x.Request.Target.Member.DeclaringType
I do like the idea of wrapping the Log4Net in my own interfaces. I don't want to be dependent on Ninjects implementation, because to me that just means I take a dependency on Ninject throughout my application and I thought that was the exact opposite of what dependency injection is for. Decouple from third party services. So I took the original posters code but I changed the following code to make it work.
private string GetParentTypeName(IContext context)
{
var res = context.Request.ParentRequest.ParentRequest.Service.FullName;
return res.ToString();
}
I have to call ParentRequest.ParentRequest so that when I print the layout %logger it will print the class that calls the Log4Net log method instead of the Log4Net class of the method that called the Log method.

Categories