Add prefix for logs lines log4net - c#

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");
}

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.

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

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.

LightInject and automatic property instantiation

Some time ago I worked on a project that I THINK used LightInject. I no longer have access, so I can't just go look for myself. It seemed like once the ServiceContainer was instantiated, something triggered reflection across all assemblies, and any properties of a certain interface type were automatically instantiated. Something like this:
A C# class library that contains a logger class; the logger is what should be injected.
namespace Common {
public interface ILogger { void Log(string msg); }
public class Logger : ILogger {
public Logger() { }
public void Log(string msg) { Console.WriteLine(msg); }
}
}
A C# console app that references the class library. Some things that didn't seem to help are commented out.
namespace TestLightInject {
class Program {
private static ServiceContainer container;
static void Main(string[] args) {
container = new ServiceContainer();
//container.EnableAnnotatedPropertyInjection();
container.Register<ILogger, Logger>();
//container.RegisterPropertyDependency<ILogger>((factory, propertyInfo) => new Logger());
var worker = new Worker();
worker.DoSomething();
}
}
public class Worker {
//[Inject]
ILogger logger { get; set; } = null; // THIS IS THE PROPERTY THAT NEEDS TO BE SET
public Worker() { }
public void DoSomething() { logger.Log("It works!"); }
}
}
I guess I could allow public access to the service container, and change the Worker ctor to something like
public Worker() { logger = Program.container.GetInstance<ILogger>(); }
but it was simpler when any ILogger property was automatically instantiated.
Is there a way to do this with LightInject, or was it some other DI framework that did it? Or am I just imagining it all?

pass in the class object itself in constructor

class LogUtil : ILogUtility
{
object _classtype;
log4net.ILog log;
public LogUtil(object classtype)
{
_classtype = classtype;
log = log4net.LogManager.GetLogger(_classtype.GetType().FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , _classtype.GetType().FullName, message);
}
}
From the client class code I call the above LogUtil class as follows:
public class TestCode
{
public void test()
{
LogUtil logutil = new LogUtil(this);
}
}
In the LogUtil constructor, I want to AVOID passing in object classtype.
I want to pass in the client class object that I can pass to the GetLogger Method. GetLogger method needs to know which class instantiated the LogUtil class.
The way I have it now, I can pass in integer variable class type and it would work. I want to avoid it.
How do I do that? How can I utilize generics method in this case?
Is this what you want?
class LogUtil<T> : ILogUtility
{
log4net.ILog log;
public LogUtil()
{
log = log4net.LogManager.GetLogger(typeof(T).FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1}", typeof(T).FullName, message);
}
}
You could then use it like this:
LogUtil<TestCode> logutil = new LogUtil<TestCode>();
If you use method level generics, rather than class level generics (or rather, in addition to in this case), you can let type inference make things easier on you.
class LogUtil<T> : ILogUtility
{
log4net.ILog log;
public LogUtil<T>()
{
log = log4net.LogManager.GetLogger(typeof(T));
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , typeof(T).FullName, message);
}
public static LogUtil<NewType> Create<NewType>(NewType instance)
{
return new LogUtil<NewType>();
}
}
public class TestCode
{
public void test()
{
var logutil = LogUtil.Create(this);
}
}
I'd change it to this:
class LogUtility : ILogUtility
{
private Type classType;
public LogUtil(object o)
: this(o.GetType())
{
}
public LogUtil(Type t)
{
this.classType = t;
}
public static LogUtil Create<T>()
{
return new LogUtil(typeof(T));
}
public void Log(string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , this.classType, message);
}
}
Please modify the LogUtil class as below.
class LogUtil : ILogUtility
{
Type _classtype;
log4net.ILog log;
public LogUtil(Type classtype)
{
_classtype = classtype;
log = log4net.LogManager.GetLogger(_classtype.FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , _classtype.FullName, message);
}
}
The client code should be as below,
public class TestCode
{
public void test()
{
LogUtil logutil = new LogUtil(GetType());
}
}

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