Logger dependency injection, getting calling class name and source file path - c#

In a WinForms app, there is Logger class that is a form designed for logging, so that any class can call it.
There is a static Configuration class, inside which a Logger lives.
Previous implementation
Various classes would call the logger like so:
public class ImportController
{
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
Configuration.logger.log("Something failed");
Configuration.logger.log(ex);
}
}
}
Current implementation
The logger implements the following interface, which was extracted from it as part of refactoring to enable unit testing calling classes through dependency injection:
public interface ILogger
{
void (string message, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
void (Exception ex, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
}
As can be seen, the idea is to have it automatically log the calling class name and source file path.
The following is an example of an attempt to inject a logger into all classes that use it, in this instance the ImportController from above:
public class ImportControllerLogger
{
public void log(string message, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(string message, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
public void log(Exception exception, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(exception, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
}
public class ImportController
{
ILogger _logger;
public ImportController(ILogger logger)
{
this._logger = logger;
}
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
_logger.log("Something failed");
_logger.log(ex);
}
}
}
Questions
Is this the correct approach to decouple the logger from all classes that use it?
It seems it might be better to create a single "LoggerHelper" class, that abstracts away the logger so that any class can make a call to it, instead of creating such a class for every calling class. How can the name of the calling class and source file path for the calling class be logged, in a proper way, without resorting to manually specifying it for each class? It worked in the previous implementation with the attributes.

I also had to implement something like that.
The code is simplified.
ILogger
public interface ILogger
{
event EventHandler<LogEventArgs> OnLogAdded;
Type Type { get; }
void Log(string message);
}
Logger
public class Logger : ILogger
{
public Type Type { get; }
public Logger(Type type)
{
Type = type;
}
public event EventHandler<LogEventArgs> OnLogAdded;
public void Log(string message)
{
EventHandler<LogEventArgs> handler = OnLogAdded;
handler?.Invoke(this, new LogEventArgs(message));
}
}
LogProvider
public static class LogProvider
{
private static List<ILogger> loggers = new List<ILogger>();
public static ILogger CreateLogger<T>()
{
if (loggers.Select(x => x.Type.Equals(typeof(T))).Count() > 0)
{
throw new Exception($"There is allready a logger for the type {typeof(T)}");
}
ILogger logger = new Logger(typeof(T));
logger.OnLogAdded += OnLogAdded;
loggers.Add(logger);
return logger;
}
private static void OnLogAdded(object sender, LogEventArgs e)
{
//add log to your config
}
}
And you can use it like this:
public class SampleView
{
private ILogger logger = LogProvider.CreateLogger<SampleView>();
public SampleView()
{
logger.Log("TestLog");
}
}
I don't know if this is the best implementation, but it works like a charm.

Related

Log4net get correct class and method name in logfile using Ninject

I'm converting an application to use Ninject as IoC and one of the things I need to convert is the existing Log4net implementation. The problem that I'm facing is that in the logfile (I use the XmlLayoutSchemaLog4j pattern) the class and method name seems to be of the calling parent instead of the actual caller.
I checked the types that are given to the new Log4NetLogger() and they seem to be of the exact same type as you specify using the LogManager.GetLogger(Methodbase.GetCurrentMethod.DeclaringType);
I made a small program that uses the old and the new implementation to check the differences but I can't seem to find them.
the outcome of the program is this:
Every level is a specific log entry in the code and the first entry of that level is done via Ninject and the second is via de LogManager.
As you can see the logger is the same, but the class and method differs.
the code from the project is:
internal class Program
{
private static IDoSomething _something;
static void Main()
{
log4net.Config.XmlConfigurator.Configure();
Init();
_something.StartSomething();
}
private static void Init()
{
var kernel = new StandardKernel(new NinjectSettings { LoadExtensions = false });
kernel.Load(Assembly.GetExecutingAssembly());
_something = kernel.Get<IDoSomething>();
}
}
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<ILogger>().ToMethod(x => new Log4NetLogger(x.Request.Target.Member.DeclaringType)).InTransientScope();
Bind<IDoSomething>().To<DoSomething>();
Bind<IDoSomethingElse>().To<DoSomethingElse>();
}
}
the dosomething:
public interface IDoSomething
{
void StartSomething();
}
public class DoSomething : IDoSomething
{
[Inject]
public ILogger Logger { get; set; }
public static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
[Inject]
public IDoSomethingElse DoSomethingElse { get; set; }
public void StartSomething()
{
Logger.Debug("Start StartSomething");
Log.Debug("Start StartSomething");
DoSomethingElse.StartSomethingElse();
Logger.Fatal("End StartSomething");
Log.Fatal("End StartSomething");
}
}
And the DoSomethingElse
public interface IDoSomethingElse
{
void StartSomethingElse();
}
public class DoSomethingElse : IDoSomethingElse
{
[Inject]
public ILogger Logger { get; set; }
public static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void StartSomethingElse()
{
Logger.Info("Start Do Something Else");
Log.Info("Start Do Something Else");
StartSomethingLocal();
Logger.Error("End Do Something Else");
Log.Error("End Do Something Else");
}
private void StartSomethingLocal()
{
Logger.Warn("Start Do Something Local");
Log.Warn("Start Do Something Local");
Logger.Warn("End Do Something Local");
Log.Warn("End Do Something Local");
}
}
I tried several solutions for the type resolving in the new Log4NetLogger in the Load method but no luck.

Add prefix for logs lines log4net

I have class where write logs, i used log4net:
class MyClass
{
private readonly ILog _logger = LogManager.GetLogger(typeof(MyClass));
public void Work(int i)
{
_logger.Info($"{i} start");
_logger.Info($"{i} work");
_logger.Info($"{i} finished");
}
}
logs worked. But how can changing this code, that prefix "{i}" set before of writing block of logs and do not need to add each time when write to log? Something likes this:
using(_logs.Startprefix("{i}"))
{
_logger.Info("start");
_logger.Info("work");
_logger.Info("finished");
}
as result in file will be write in each line {i}
Make your own logger that incapsulates ILog and implement Prefix member there.
class MyLogger
{
private readonly ILog _logger;
public int? Prefix;
public MyLogger(Type t)
{
_logger = NLog.LogManager.GetLogger(t.Name);
}
public void Info(string message)
{
_logger.Info($"{Prefix?.ToString() ?? ""} {message}");
}
}
Then you may use using statement:
using (myLogger.Prefix = i)
{
myLogger.Info("start");
myLogger.Info("work");
myLogger.Info("finished");
}
What you want to do is not a built in feature of log4net. You would need to write a wrapper around the log4net logic to add this functionality. Something like this, but this is a rough draft, you would need to fix it up and complete it.
class MyLogger
{
private readonly string _prefix;
private readonly ILog _logger = LogManager.GetLogger(typeof(MyClass));
public MyLogger(string prefix)
{
_prefix = prefix;
}
public void Info(string info)
{
_logger.Info($"{_prefix} {info}");
}
}
The most easiest part would be to use a new method like this and a property for your prefix:
class MyClass
{
private readonly ILog _logger = LogManager.GetLogger(typeof(MyClass));
public int Prefix {get;set;}
public void Work()
{
PrintLine("start");
PrintLine("work");
PrintLine("finished");
}
private void PrintLine(string txt) {
_logger.Info(Prefix + " " + txt);
}
}
I found standart for this case. LogicalThreadContext for log4net, and my problem is solved like this:
using (LogicalThreadContext.Stacks["NDC"].Push($"{i}"))
{
_logger.Info("start");
_logger.Info("work");
_logger.Info("finished");
}

How to create a exception handling and logging class in C#

I am working on a small project, I am trying to create a layer that will handle exception and logging.
This layer will sit in between User interface and DAL, mostly like BAL which will have some generic method that will then initiate further call to Data access layer.
Some thing like this
Public Class ExceptionHandler
{
//which should take a method name,parameters and return a object.
Public T InitiateDatabaseCall(//method name as input,parameters)
{
try
{
//then make the call to the method using the input parameter and pass the parameters
}
catch(Exception e)
{
// do logging
}
}
This layer will act as center repository to handle and log exception. I am not able to create the method that i described can experts provide some snippet that will show case this scenario.
Edited: With code added
static void Main(string[] args)
{
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if(!string.IsNullOrEmpty(screen))
{
ExceptionHandler.InitiateCall(() =>GetList(screen) ));
}
}
}
public static class ExceptionHandler
{
public T InitiateCall<T>(Func<T>method,object[] parms) where T : object
{
try
{
return method.Invoke();
}
catch(Exception ex)
{
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
It gives error GetList() does not exist in current context.
For these kind of things AOP (Aspect Oriented Programming, see https://en.wikipedia.org/wiki/Aspect-oriented_programming) is a really good fit.
These are cross-cutting concerns that are cluttering code if not done properly.
See for an example AOP framework PostSharp. Even with the free version that is simple to code. There are also (might be paid) build-in aspects for that, like http://doc.postsharp.net/exception-tracing.
A simple alternative is using a Func or Action (try it out in a Console App):
static void Main(string[] args)
{
ExceptionHandler.InitiateDatabaseCall(() => CallDb("Dummy"));
ExceptionHandler.InitiateDatabaseCall<int>(() => { throw new InvalidOperationException(); });
}
int CallDb(string justToShowExampleWithParameters)
{
return 5;
}
public static class ExceptionHandler
{
public static T InitiateDatabaseCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception e)
{
// do logging
Console.WriteLine(e.Message);
return default(T); // or `throw` to pass the exception to the caller
}
}
}
Edit:
based on your added code in the question you can solve the error about GetList() by some minor modifications:
static void Main(string[] args) {
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if (!string.IsNullOrEmpty(screen))
{
return ExceptionHandler.InitiateCall(() => new DAL().GetList(screen)); // Slight modification of your code here
}
else
{
return null; // or whatever fits your needs
}
}
}
public class ExceptionHandler
{
public static T InitiateCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception ex)
{
//log
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
You don't need the object[] parms parameter in InitiateCall given the provided code. Any paramaters you need for the method call are given in the Func<T>
Personally, I think loging should be done in two ways:
Step logging (when you log some step in your code)
Scope logging (when you log begin/end of some code or it's time)
So, I always choose to create ILogger class with these two ways:
public sealed class Logger : ILogger
{
private readonly Serilog.ILogger _seriLogger;
public Logger(Serilog.ILogger seriLogger)
{
_seriLogger = seriLogger;
}
public void Debug(string format, params object[] args)
{
_seriLogger.Debug(format, args);
}
public void Info(string format, params object[] args)
{
_seriLogger.Information(format, args);
}
public void Warn(string format, params object[] args)
{
_seriLogger.Warning(format, args);
}
public void Error(Exception e, string format, params object[] args)
{
_seriLogger.Error(e, format, args);
}
public void Fatal(Exception e, string format, params object[] args)
{
_seriLogger.Fatal(e, format, args);
}
public IDisposable GetScope(string name, long timeout = 0)
{
return new LoggerScope(this, name, timeout);
}
}
internal class LoggerScope : IDisposable
{
private readonly ILogger _logger;
private readonly string _name;
private readonly long _timeout;
private readonly Stopwatch _sw;
private bool ExceedScope
{
get { return _timeout > 0; }
}
public LoggerScope(ILogger logger, string name, long timeout)
{
_logger = logger;
_name = name;
_timeout = timeout;
if (!ExceedScope)
{
_logger.Debug("Start execution of {0}.", name);
}
_sw = Stopwatch.StartNew();
}
public void Dispose()
{
_sw.Stop();
if (ExceedScope)
{
if (_sw.ElapsedMilliseconds >= (long)_timeout)
{
_logger.Debug("Exceeded execution of {0}. Expected: {1}ms; Actual: {2}ms.", _name, _timeout.ToString("N"), _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
else
{
_logger.Debug("Finish execution of {0}. Elapsed: {1}ms", _name, _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
}
Then, If I want to log something, I use it like this, without AOP:
using(_log.GetScope("Some describable name"))
{
//Some code here
}

How to use the type being resolved to resolve a dependency

I have several classes that take a dependency of type ILogger. The implementation of ILogger needs to know the type for which it is the logger, i.e. the ILogger for Foo will be new Logger(typeof(Foo)), for Bar it will be new Logger(typeof(Bar)), etc.
I would like the proper logger to be injected automatically by Unity; in other words, when I call container.Resolve<Foo>(), I want a new Logger(typeof(Foo)) to be injected into the Foo instance.
How can I set this up in Unity? Is there a way to pass the type being resolved to the dependency?
(In my real code, I actually have a ILoggerFactory with a Create method, which also takes a type as a parameter. So I could just pass the factory to my classes, and they would call Create themselves to get the appropriate logger, but it's not as elegant as what I'd like to achieve)
Some code to make things clearer:
interface ILogger
{
...
}
class Logger : ILogger
{
private readonly Type _type;
public Logger(Type type)
{
_type = type;
}
...
}
class Foo
{
private readonly ILogger _logger;
public Foo(ILogger logger) // here I want a Logger with its type set to Foo
{
_logger = logger;
}
}
This related question shows exactly what I'm trying to do, and the accepted answer is exactly the kind of thing I'm looking for... but it's for NInject, not Unity.
Here's a container extension that will set the Type parameter of the Logger constructor to the Type that the ILogger is being injected into.
The transient IBuilderContext.Policies is used to store the type that the ILogger is being injected into.
Maybe it's more complicated than it needs to be but this seems to work
public class LoggerExtension : UnityContainerExtension
{
public static NamedTypeBuildKey LoggerBuildKey = new NamedTypeBuildKey<Logger>();
protected override void Initialize()
{
Context.Strategies.Add(new LoggerTrackingPolicy(), UnityBuildStage.TypeMapping);
Context.Strategies.Add(new LoggerBuildUpStrategy(), UnityBuildStage.PreCreation);
}
}
public class LoggerTrackingPolicy : BuilderStrategy
{
public LoggerTrackingPolicy()
{
}
public override void PreBuildUp(IBuilderContext context)
{
if (context.BuildKey.Type != typeof(Logger))
{
var loggerPolicy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
if (loggerPolicy == null)
{
loggerPolicy = new LoggerPolicy();
context.Policies.Set<ILoggerPolicy>(loggerPolicy, LoggerExtension.LoggerBuildKey);
}
loggerPolicy.Push(context.BuildKey.Type);
}
}
}
public class LoggerBuildUpStrategy : BuilderStrategy
{
public LoggerBuildUpStrategy()
{
}
public override void PreBuildUp(IBuilderContext context)
{
if (context.BuildKey.Type == typeof(Logger))
{
var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
Type type = policy.Peek();
if (type != null)
{
context.AddResolverOverrides(new ParameterOverride("type", new InjectionParameter(typeof(Type), type)));
}
}
}
public override void PostBuildUp(IBuilderContext context)
{
if (context.BuildKey.Type != typeof(Logger))
{
var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
policy.Pop();
}
}
}
public interface ILoggerPolicy : IBuilderPolicy
{
void Push(Type type);
Type Pop();
Type Peek();
}
public class LoggerPolicy : ILoggerPolicy
{
private Stack<Type> types = new Stack<Type>();
public void Push(Type type)
{
types.Push(type);
}
public Type Peek()
{
if (types.Count > 0)
{
return types.Peek();
}
return null;
}
public Type Pop()
{
if (types.Count > 0)
{
return types.Pop();
}
return null;
}
}
How it works is: when a type that is not a Logger is trying to be resolved then, during the TypeMapping stage (before any creation), the type is pushed on a stack. Later, before creation, if the type is a Logger then the type it is being injected into is peeked from the stack and that type used as a resolver override. After creation, if the type is not a Logger, it is popped from the stack.
And some code to make sure it's working (I added a Type property to the logger just to verify it's set properly):
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.AddNewExtension<LoggerExtension>();
var a = container.Resolve<A>();
var b = container.Resolve<B>();
var c = container.Resolve<C>();
var d = container.Resolve<D>();
var x = container.Resolve<X>();
}
}
public interface ILogger
{
Type Type { get; }
}
public class Logger : ILogger
{
private readonly Type _type;
public Logger(Type type)
{
_type = type;
}
public Type Type { get { return _type; } }
}
public class A
{
public A(ILogger logger)
{
System.Diagnostics.Debug.Assert(logger.Type == typeof(A));
}
}
public class B
{
public B(ILogger logger)
{
System.Diagnostics.Debug.Assert(logger.Type == typeof(B));
}
}
public class C
{
public C(A a, D d, B b, ILogger logger)
{
System.Diagnostics.Debug.Assert(logger.Type == typeof(C));
}
}
public class D
{
public D()
{
}
}
public class X
{
public X(Y y)
{
}
}
public class Y
{
public Y(ILogger logger)
{
System.Diagnostics.Debug.Assert(logger.Type == typeof(Y));
}
}
As #spotco suggested, I would also recommend generics. The unity registration would be done using "open generics" as shown here. This single registration gives you the ability to resolve any type in the generics parameter of Logger.
unityContainer.RegisterType(typeof(ILogger<>), typeof(Logger<>));
Using that registration, you can resolve the instance using constructor injection like this.
public class MyClass
{
public MyClass(ILogger<MyClass> logger) { ... }
}
More info here.

How do I configure Unity to create a class that takes two different items of same type?

I am still getting started with Unity, and have what seems to be a simple question.
I have a class that has a dependency on two different instances of the same interface. How do I configure and resolve this class?
I.E. Given:
public interface ILogger
{
void Write(string message);
}
public class ConsoleLogger : ILogger
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
public class AnotherLogger : ILogger
{
public void Write(string message)
{
Console.WriteLine(DateTime.Now.ToString() + " " + message);
}
}
public class CombinedLogger : ILogger
{
IList<ILogger> _loggers;
public CombinedLogger(params ILogger[] loggers)
{
_loggers = new List<ILogger>(loggers);
}
public void Write(string message)
{
foreach(var logger in _loggers) logger.Write(message);
}
}
I know how to configure for ConsoleLogger, and AnotherLogger. I also know how to access them in the actual code. What I seem to be blocking on is figuring out how to configure and use CombinedLogger, passing in the instances of ConsoleLogger and AnotherLogger.
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, ConsoleLogger>();
container.RegisterType<ILogger, AnotherLogger>("another");
container.RegisterType<ILogger, CombinedLogger>("combined");
var instances = container.ResolveAll<ILogger>();
Read the documentation on configuration support for arrays.
You use a named registration.
myContainer.RegisterType("ConsoleLogger");
myContainer.RegisterType("AnotherLogger");
myContainer.RegisterType("CombinedLogger");
Then when you resolve the type, you used the name to get the specific one
public class CombinedLogger : ILogger{
IList<ILogger> _loggers;
public CombinedLogger(params ILogger[] loggers)
{
_loggers = new List<ILogger>();
_loggers.Add(myContainer.Resolve(Of ILogger)("ConsoleLogger")
_loggers.Add(myContainer.Resolve(Of ILogger)("AnotherLogger")
}
public void Write(string message)
{
foreach(var logger in _loggers) logger.Write(message);
}
}

Categories

Resources