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.
Related
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
Consider the following:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<ILogger, Logger>();
...
}
So I obviously want a logging class within my controller methods, as shown, but I also have many other components that will be common to them - and all of those components will want to have references to my logger. It is a singleton after all (which might be an anti-pattern here, I guess, but anyway).
How should I properly be passing "dependencies within dependencies" within a ConfigureServices method? I could do something like this, I guess:
public void ConfigureServices(IServiceCollection services)
{
Logger l = new Logger();
services.AddSingleton<ILogger, Logger>((provider) =>
{
return l;
});
services.AddScoped<IFoo, Foo>((provider) =>
{
return new Foo(l);
});
...
}
I could do something like that! But it looks and feels like a hack. I'm certain I shouldn't be resolving / making actual concrete constructions in my ConfigureServices method like this, but I don't know of a way to tell my Foo constructor to (like my controller methods) be dependent on ILogger.
What is the intended mechanism for doing this -- or is there a completely different pattern I should consider following?
From what i can tell there are no dependencies inside your dependencies in any kind of "weird" way...
All you need to do is register the implementation for each service like so:
services.AddSingleton<ILogger, Logger>();
When this lines executes you can "request" an ILogger in the definition of a constructor and the framework will handle instantiating (or not,depending on service scope etc) and passing the reference to your calls.
This means that if your IFoo "requests" an ILogger in one of its constructors you dont need to do anything tricky to resolve it, as long as you register IFoo service provider after your ILogger provider. Below is a simple example on the fly, where Foo implementations requests an ILogger in the constructor
public class Foo : IFoo
{
private readonly ILogger<Foo> _logger;
public Foo(ILogger<Foo> logger)
{
_logger = logger;
}
public Task WriteMessage(string message)
{
_logger.LogInformation("Foo.WriteMessage called. Message:{MESSAGE}", message);
return Task.FromResult(0);
}
}
This question is related to Steven’s answer - here. He proposed a very good logger wrapper. I will paste his code below:
public interface ILogger
{
void Log(LogEntry entry);
}
public static class LoggerExtensions
{
public static void Log(this ILogger logger, string message)
{
logger.Log(new LogEntry(LoggingEventType.Information,
message, null));
}
public static void Log(this ILogger logger, Exception exception)
{
logger.Log(new LogEntry(LoggingEventType.Error,
exception.Message, exception));
}
// More methods here.
}
So, my question is what is the proper way to create implementation that proxies to Serilog?
Note: this question is related to this question about log4net but now specific to Serilog.
So, my question is what is the proper way to create implementation that proxies to Serilog?
you should create something like:
public class SerilogAdapter : ILogger
{
private readonly Serilog.ILogger m_Adaptee;
public SerilogAdapter(Serilog.ILogger adaptee)
{
m_Adaptee = adaptee;
}
public void Log(LogEntry entry)
{
if (entry.Severity == LoggingEventType.Debug)
m_Adaptee.Debug(entry.Exception, entry.Message);
if (entry.Severity == LoggingEventType.Information)
m_Adaptee.Information(entry.Exception, entry.Message);
else if (entry.Severity == LoggingEventType.Warning)
m_Adaptee.Warning(entry.Message, entry.Exception);
else if (entry.Severity == LoggingEventType.Error)
m_Adaptee.Error(entry.Message, entry.Exception);
else
m_Adaptee.Fatal(entry.Message, entry.Exception);
}
}
Does that mean that every class that will log sth (so basically every), should have ILogger in its constructor?
As I understand from Stevens answer: Yes, you should do this.
what is the best way to use it later in the code?
If you are using a DI container, then just use the DI container to map ILogger to SerilogAdapter. You also need to register Serilog.ILogger, or just give an instance of Serilog logger to the DI container to inject it to the SerilogAdapter constructor.
If you don't use a DI container, i.e., you use Pure DI, then you do something like this:
Serilog.ILogger log = Serilog.Log.Logger.ForContext("MyClass");
ILogger logging_adapter = new SerilogAdapter(log);
var myobject = new MyClass(other_dependencies_here, logging_adapter);
While registering components in Castle Windsor, how do we bind specific implementation of an interface to a component that has a dependency on that interface. I know in advance which implementation needs to be used by the component.
For example i created a sample console application based on code from several blogs and tutorials.
Following is the code.
public interface IReport
{
void LogReport();
}
public interface ILogger
{
string Log();
}
public class FileLogger : ILogger
{
public string Log()
{
return "Logged data to a file";
}
}
public class DatabaseLogger : ILogger
{
public string Log()
{
return "Logged data to a database";
}
}
public class McAfeeService : IReport
{
private readonly ILogger _logger;
public McAfeeService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("McAfee Scan has " + getLogResult);
}
}
public class NortonService : IReport
{
private readonly ILogger _logger;
public NortonService(ILogger logger)
{
this._logger = logger;
}
public void LogReport()
{
string getLogResult = this._logger.Log();
Console.WriteLine("Norton Scan has " + getLogResult);
}
}
class Program
{
private static IWindsorContainer container;
static void Main(string[] args)
{
// Register components
container = new WindsorContainer();
container.Register(Component.For<IReport>().ImplementedBy<NortonService>());
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
IReport service = container.Resolve<IReport>();
service.LogReport();
Console.ReadLine();
}
}
I would like NortonService to always use a Filelogger and McAfeeService to use a Database Logger.
In the above program i am unable to bind NortonService to FileLogger.
How to do it?
The above answers lead me to inline dependencies and the feature service override
Here is the registration code:
container.Register(Component.For<IReport>().ImplementedBy<NortonService>().Named("nortonService"));
container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
container.Register(Component.For<ILogger>().ImplementedBy<DatabaseLogger>());
container.Register(
Component.For<IReport>().ImplementedBy<McAfeeService>().Named("mcafeeService")
.DependsOn(Dependency.OnComponent<ILogger, DatabaseLogger>())
);
IReport mcafeescan = container.Resolve<IReport>("mcafeeService");
mcafeescan.LogReport();
IReport nortonscan = container.Resolve<IReport>("nortonService");
nortonscan.LogReport();
Output:
McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file
I had a problem very like this, two implementation of one interface and two implementation of another interface. I wanted to force usage of particular implementations of those interfaces.
My class structure looked like this -
I looked at the naming convention, but didn't really like it. Instead I used the following -
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IMessageLoader>().ImplementedBy<MessageLoaderDatabase>()
,Component.For<IMessageLoader>().ImplementedBy<MessageLoaderFile>()
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceDatabase>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderDatabase>())
,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceFile>()
.DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderFile>())
,Component.For<MessageOfTheDayController>().LifestyleTransient()
.DependsOn(Dependency.OnComponent<IMessageOfTheDayService, MessageOfTheDayServiceFile>())
);
Full info about this approach is here. In the source code provided with that post I show two other ways of achieving the same result.
If you want to do it at runtime, This can be acheived through IHandlerSelector. Write a class that implements IHandlerSelector. It provides a method SelectHandler which will let you define the condition for binding conditionally at runtime. A Handler in this case is a component in Windsor that participates in instance construction. Refer here for more details.
My answer maybe not the best one, you can use naming method to resolve multi implementation:
container.Register(Component.For(typeof(ILogger))
.ImplementedBy(typeof(FileLogger))
.Named("FileLoggerIoC")
.LifestylePerWebRequest() ,
Component.For(typeof(ILogger))
.ImplementedBy(typeof(DatabaseLogger))
.Named("DatabaseLoggerIoC")
.LifestylePerWebRequest());
In your calling functions, you need to resolve it by name :-
var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));
Mine method maybe not the best one as people don't like service locator to get the components, you can use this as temporary solution.
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.