Log4Net logging from custom interface - c#

I would like to know how to print the right class name in the %logger pattern using log4net (in my example).
In my app I am using a logging class implementing a logging interface (following SOLID). Other classes use the logging interface abstraction to perform the actual logging. I want to switch to Log4Net, but I was thinking of keeping the logging abstraction. The custom logging class methods take objects as arguments and create logs basing on their states.
So in the below example the %logger pattern will log "MyLogger", which is expected, but I would like to log the calling class name (in this case ObjectManipulator).
using System.Reflection;
using log4net;
namespace LoggingTestur
{
class Program
{
class AnObject
{
public string State { get; set; }
}
interface IMyLogger
{
void LogObjectStateChenge(AnObject anObject);
}
class MyLogger : IMyLogger
{
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void LogObjectStateChenge(AnObject anObject)
{
Log.InfoFormat("AnObject's state is: {0}", anObject.State);
}
}
class ObjectManipulator
{
private readonly IMyLogger _logger;
public ObjectManipulator(IMyLogger logger)
{
_logger = logger;
}
public void Manipulate()
{
var anObject = new AnObject { State = "New" };
_logger.LogObjectStateChenge(anObject);
}
}
static void Main(string[] args)
{
var logger = new MyLogger();
var manipulator = new ObjectManipulator(logger);
manipulator.Manipulate();
}
}
}

You have a few options to get the class name:
Pass it as a parameter, i.e. LogObjectStateChange(AnObject anObject, Type type = null);
If you're targeting the 4.5 runtime you can use the CallerFilePath attribute to get the the file name, and extract the class name from that (assuming you have one file per class and the file name is the same as the class name), i.e. LogObjectStateChange(AnObject anObject, [CallerFilePath] string fileName = "")
Use reflection to get the calling class from the CallStack and StackFrame classes. Note though that in Release builds the compiler may inline methods or otherwise rearrange things, resulting in incorrect information in the stack trace.

Related

Having some parameters Injected with DI and some assigned manually

In a .NET Core 3.1 console application I want a Class that would have some parameters in constructor injected but some that I could assign manually. For example something like that but with IConfiguration Injected:
static void Main() {
var myObj1 = new MyClass(1);
var myObj2 = new MyClass(2);
}
public class MyClass {
public MyClass(IConfiguraiton config, int myVal)
{
}
}
I tried this with Ninject:
static void Main()
{
kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
kernel.Get<MyClass>();
}
public class MyClass
{
public MyClass(IConfiguraiton config)
{
}
}
public class Bindings : NinjectModule
{
public override void Load()
{
var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.json").Build();
Bind<IConfiguration>().ToMethod(ctx => SetupConfiguration()).InSingletonScope();
Bind<MyClass>().ToSelf().InTransientScope();
}
}
I managed to make simple dependency injection, but haven't had any success making injection with parameters.
I've read a lot of people suggesting that it's better to pass parameters into methods of the class rather than constructor, however in my situation this isn't an option in addition I'm a software engineering student, and would like to learn how to do this, since it might be useful in some situations.
This is a situation where the Ninject.Extensions.Factory is useful, as it is made exactly for this situation. It does pull in the Factory dependency in addition to Castle.Core, as it uses DynamicProxy under the hood (as a SE student, playing with this library is a good idea for using the interceptor pattern).
To use it, you define a Factory interface like so:
public interface IMyClassFactory
{
MyClass Create(int myVal);
}
Note that the Create method returns MyClass, and the argument(s) to the Create method match exactly in type and name to the arguments you wish to provide. The argument type(s) you want injected must be registered with the kernel. Unfortunately, it is easy to make a mistake here - if you specify a parameter that does not exist in the factory interface it is ignored, but if you forget one it will throw an exception when called.
Next, register IMyClassFactory like this: Bind<IMyClassFactory>().ToFactory(); and remove your binding for MyClass. Then wherever you need to create an instance, inject IMyClassFactory and call Create: kernel.Get<IMyClassFactory>().Create(2)
You can achieve the same result without using Ninject.Extensions.Factory by writing and registering your own implementation of IMyClassFactory, essentially doing the same thing that the code the Factory extension ends up emitting. A full sample is below using both methods based on commenting in/out the registration (note the output if you add .InSingletonScope() to the registration of IConfiguraiton - both approaches respect the binding scopes of Ninject).
internal class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Bind<IConfiguraiton>().To<Configuraiton>();
kernel.Bind<IMyClassFactory>().ToFactory();
//kernel.Bind<IMyClassFactory>().To<NinjectMyClassFactory>().InSingletonScope();
var factory = kernel.Get<IMyClassFactory>();
var one = factory.Create(1);
var two = factory.Create(2);
}
}
public interface IMyClassFactory
{
MyClass Create(int myVal);
}
public class NinjectMyClassFactory : IMyClassFactory
{
public NinjectMyClassFactory(IResolutionRoot resolutionRoot)
{
ResolutionRoot = resolutionRoot;
}
private IResolutionRoot ResolutionRoot { get; }
public MyClass Create(int myVal)
{
return ResolutionRoot.Get<MyClass>(new ConstructorArgument("myVal", myVal));
}
}
public class MyClass
{
public MyClass(IConfiguraiton config, int myVal)
{
Console.Out.WriteLine("Created MyClass({0},{1})", config.MyNum, myVal);
}
}
public interface IConfiguraiton { int MyNum { get; } }
public class Configuraiton : IConfiguraiton
{
static int CreateCount;
public Configuraiton()
{
MyNum = Interlocked.Increment(ref CreateCount);
}
public int MyNum { get; }
}

Is there a way to implement an adapter or interface without changing a class that uses it in this situation?

I'm trying to learn and understand interfaces, adapters, and dependency injection and how they can be used to achieve abstraction.
TLDR: How to change the way function\class uses the interface without changing said class\function's code?
I would also like it if you could point out errors in my thinking.
The Example:
Let's say I need to develop a logging mechanism (implement an ILogger interface) for some class (MoneyAdder) that I'm not allowed to modify. I also need to implement usage (please give me a correct term for this) of some other loggers via an adapter so we could choose the best way.
class MoneyAdder
{
private ILogger logger;
private Customer customer;
public MoneyAdder(ILogger logger, Customer customer)
{
this.logger = logger;
}
public void AddMoney(int amount)
{
logger.log("Doing work!");
customer.balance+=amount;
logger.log("I'm done!");
}
}
This is what I did at first:
public interface ILogger
{
void log(string str);
}
public class MyLogger : ILogger
{
public void log(string str)
{
Console.WriteLine($"LOG - {str}");
}
}
In this case, I have complete control over the interface definition, the class that implements it, and is able to use the log string that is given any way I want.
We know that OtherLogger requires a string and it's length to write a log. To use it I wrote an adapter like this:
public class OtherLogger
{
//We know it needs a string and its length for some reason.
public void log2(string str, int i);
}
public class OtherLoggerAdapter : ILogger
{
private OtherLogger ol = new OtherLogger();
public void log(string str)
{
ol.log2(str, str.Length);
}
}
This way I adapt the other dll to my interface. When the adapter is called I, in turn, call the different method name. I'm also able to provide an int it needs because it's computable from the input.
Now I need to add another logger that needs a log string and an assembly from which it was referenced. I'm able to provide it as well. It is not computable from the log string, but I'm able to give it any common information, like the environment variables or the program name because it is common for the program, MoneyAdder class, and StrangeLoggerAdapter.
public class StrangeLogger
{
public void logS(string str, System.Reflection.Assembly assembly);
}
public class StrangeLoggerAdapter : ILogger
{
private StrangeLogger sl = new StrangeLogger();
public void log(string str)
{
sl.logS(str, System.Reflection.Assembly.GetExecutingAssembly());
}
}
The final logger is an old mechanism that was used before. It was directly referenced and called inside MoneyAdder's other methods. (Tightly coupled, is this the correct term?). The logging method definition is as follows:
public class OldLogger
{
public void log(string str, string customerName);
}
It needs a customer name, but it is neither computable from the log string, nor it can be accessed from any adapter I can write. The only way to use it is to "inject" a customer name into a logger like this:
public interface ILogger
{
void log(string str);
void injectCustomer(string customerName);
}
And then to implement it in an adapter like this:
public class OldLoggerAdapter
{
private string customerName;
private OldLogger ol = new OldLogger();
public void injectCustomer(string customerName)
{
this.customerName = customerName;
}
public void log(string str)
{
ol.log(str, this.customerName);
}
}
However, to use the implementation I still need to modify (which I'm not allowed to do) either the MoneyAdder class or AddMoney method to use the injection function.
class MoneyAdder
{
private ILogger logger;
private Customer customer;
public MoneyAdder(ILogger logger, Customer customer)
{
this.logger = logger;
this.logger.injectCustomer(customer.Name);
}
public void AddMoney(int amount)
{
logger.log("Doing work!");
customer.balance+=amount;
logger.log("I'm done!");
}
}
The question:
To implement a logging function I can only use the information, that is either provided to the function via a method or can be computed from that information, or is global to the entire program.
There is no way to directly provide or inject information without modifying the calling function.
Or is there?
Is there a way, using some System\Reflexion\DI\IOC magic, to write something like:
Hey, MoneyAdder, It's an OldLogger that is currently used as ILogger. Whenever you call log(string str) you should actually call my method log(string str, string customerName). I know for sure you have a private property Customer that has a Customer.Name, use that in my log method as a "customerName".

How to pass CallingMemberName to custom logging provider

Using ASP.NET Core and implementing my own console logging provider with ILogging and ILoggingProvider as I want to pass the name of the calling function to the logger as part of the log record as well as date/time stamp.
The best way to retrieve the name of the calling function is to use [CallerMemberName] attribute in the function parameters, however as I'm trying to keep to the standard logging pattern and inheriting the ILogger interface I can't work out how to overload any of the Log method calls to use an additional function parameter to add the CallerMemberName attribute.
Here is the code in Main:
public class Program
{
static ILogger Logger { get; } = ApplicationLogging.CreateLogger<Program>();
public static void Main(string[] args)
{
ApplicationLogging.Logger.AddMyLogger();
Program.Logger.LogInformation("Test log");
...
}
}
Here is my custom logging provider
// Setup logging for all classes to use
public static class ApplicationLogging
{
public static ILoggerFactory Logger { get; } = new LoggerFactory();
public static ILogger CreateLogger<T>() =>
Logger.CreateLogger<T>();
}
public static class MyLoggerProviderExtensions
{
public static ILoggerFactory AddMyLogger(this ILoggerFactory loggerFactory)
{
loggerFactory.AddProvider(new MyLoggerProvider());
return loggerFactory;
}
}
public class MyLoggerProvider : ILoggerProvider
{
public MyLoggerProvider()
{
}
public ILogger CreateLogger(string categoryName)
{
return new MyLogger();
}
public void Dispose()
{
}
void IDisposable.Dispose()
{
throw new NotImplementedException();
}
}
public class MyLogger : ILogger
{
public MyLogger()
{
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Console.WriteLine("{0,-12} {1,-20} {2}", DateTime.Now.ToString("HH:mm:ss.fff"), " [NEED_CALLING_METHOD_NAME_HERE]", state);
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public IDisposable BeginScope<TState>(TState state)
{
return new NoopDisposable();
}
private class NoopDisposable : IDisposable
{
public void Dispose()
{
}
}
}
If I add CallerMemberName to the Log method call then I'm not implementing as per the interface and it won't compile (as expected). I tried adding a class scoped variable and to set up the calling member name when instantiating the logger but that won't capture correct calling member.
I'm fairly new to C# so I might be missing the best way to do this - the way I see it I have to overload the Log function to add the [CallerMemberName] attribute but the standard logging call semantics (eg. LogCritical) won't use my overloaded function.
Of course I could just ditch my custom logging provider and write my own logging function which would be simpler but won't leverage the logging infrastructure provided by Microsoft. I know I can use Reflection but I'd prefer not to take the extra CPU hit as this software will run on very low end hardware.
In the Log function:
public void Log()
{
var stackTrace = new System.Diagnostics.StackTrace(1); // skip one frame as this is the Log function frame
var name = stackTrace.GetFrame(0).GetMethod().Name;
}
EDIT:
If you want to avoid reflection maybe:
using (_logger.BeginScope("name of method"))
{
// log the stuff
}
Not sure about .Net Core but in normal .Net you could add this by using Aspect Oriented Programming to multiple methods fairly easy.

wrapping a static class/method in order to unit test it?

I've got a static class that I am using for logging:
public static class myLogger
{
public static ErrorLogging(string input)
{
//dostuff
}
}
The way I am using it is:
public class myClassthatDoesStuff
{
...
myLogger.ErrorLogging("some error ocurred");
...
}
How can I moq the myLogger class in order to be able to unit test it and ensure that the ErrorLogging method was executed? Is it possible to do this without setting any parameters in the constructor (constructor injection)? myClassthatDoesStuff requires that there are no parameters in the constructor.
This blog post describes the exact same scenario - you have an old static logging method and want to use it in testable code.
Wrap the static class in a non-static class - not just for testing, but for general use.
Extract the methods of your new non-static class into an interface.
Wherever you would have depended on the static class, depend on the interface instead. For example, if class DoesSomething requires the function in your static class, do this:
public interface ILogger
{
void ErrorLogging(string input);
}
public class MyClassthatDoesStuff
{
private readonly ILogger _logger;
public MyClassthatDoesStuff(ILogger logger)
{
_logger = logger;
}
}
This gives you two benefits:
You can unit test your old static class (assuming that it has no state and doesn't depend on anything that has any state) (although if that's the case I suppose you could unit test it anyway.)
You can unit test code that will use that static class (by removing the direct dependency on that static class.) You can replace ILogger with a mocked class, like one that adds your error messages to a list.
class StringLogger : List<string>, ILogger
{
public void ErrorLogging(string input)
{
Add(input);
}
}
var testSubject = new MyClassthatDoesStuff(new StringLogger());
A simpler option doesn't require creating an interface and an adapter class. You can create a delegate which is like an interface for just a method.
In the case of the logger, it would be
delegate void Logging ErrorLoggingMethod(string input);
Using it looks similar to using an interface:
public class MyClassthatDoesStuff
{
private readonly ErrorLoggingMethod _logger;
public MyClassthatDoesStuff(ILogger logger)
{
_logger = logger;
}
public void DoSomethingThatLogs()
{
// _logger is a method
_logger("Log something");
}
}
This is even easier to mock and test
string loggedMessage = null;
ErrorLoggingMethod fakeLogger = (input) => loggedMessage = input;
You can inject the fake logger into the class you're testing. If that class calls the logger, the method assigns whatever was logged to the variable. Then you can assert whatever was logged or just that anything was logged.
If your app uses a dependency injection/IoC container, you can register the delegate just like you would an interface. Here's an example.
If you can not change it from a static to a non-static class, wrap it with a non-static class...
void Test()
{
string testString = "Added log";
var logStore = new List<string>();
ILogger logger = new MyTestableLogger(logStore);
logger.ErrorLogging(testString);
Assert.That(logStore.Any(log => log==testString));
}
public interface ILogger
{
void ErrorLogging(string input);
}
public class MyTestableLogger : ILogger
{
public MyTestableLogger(ICollection<string> logStore)
{
this.logStore = logStore;
}
private ICollection<string> logStore;
public void ErrorLogging(string input)
{
logStore.Add(input);
MyLogger.ErrorLogging(input);
}
}
public static class MyLogger
{
public static void ErrorLogging(string input)
{
// Persist input string somewhere
}
}
You can do it using Microsoft's Shims
Assuming that Your Project is called ConsoleApplication1.
First of all go to your unit test project references, right click on assembly that contains myClassthatDoesStuff class and chose 'Add Fakes Assembly'.
Unit test with shims will look like:
[TestClass()]
public class MyClassthatDoesStuffTests
{
[TestMethod()]
public void ImportansStuffTest()
{
using (ShimsContext.Create())
{
bool logCalled = false;
ConsoleApplication1.Fakes.ShimmyLogger.ErrorLoggingString =
(message) => logCalled = true;
new myClassthatDoesStuff().ImportansStuff();
Assert.IsTrue(logCalled);
}
}
}
You can do it with Typemock Isolator.
It allows you to avoid all this amount of wrappers and interfaces and to do it that simple:
[TestMethod]
public void TestLogging()
{
//Arrange
Isolate.WhenCalled(() => myLogger.ErrorLogging("")).CallOriginal();
//Act
var foo = new myClassthatDoesStuff();
foo.DoStuff();
//Assert
Isolate.Verify.WasCalledWithAnyArguments(() => myLogger.ErrorLogging(""));
}

How to use Property Injection with AutoFac?

In a Console application, I'm using Log4Net and in the Main method I'm getting the logger object. Now, I'd like to make this log object available in all my classes by letting all the classes inherit from a BaseClass which has a ILog property and is supposed to be set by Property Injection rather than Constructor Injection.
I'm using AutoFac IoC container, how to inject my log Object to the Log property of my every class?
What's the best/easiest way to achieve this?
Is there any way to automatically resolve types?
Below is my test application:
namespace ConsoleApplication1
{
class Program
{
static ILog Log;
static IContainer Container;
static void Main(string[] args)
{
InitializeLogger();
InitializeAutoFac();
// the below works but could it be done automatically (without specifying the name of each class)?
Product.Log = Container.Resolve<ILog>();
// tried below but didn't inject ILog object into the Product
Container.Resolve<Product>();
RunTest();
Console.ReadLine();
}
private static void RunTest()
{
var product = new Product();
product.Do();
}
private static void InitializeAutoFac()
{
var builder = new ContainerBuilder();
builder.Register(c => Log).As<ILog>();
builder.RegisterType<Product>().PropertiesAutowired();
Container = builder.Build();
}
private static void InitializeLogger()
{
log4net.Config.XmlConfigurator.Configure();
Log = LogManager.GetLogger("LoggerName");
}
}
public class Product
{
public static ILog Log { get; set; }
public void Do()
{
// this throws exception because Log is not set
Log.Debug("some Debug");
}
}
}
In my opinion the solution Ninject created is much nicer than the propertyinjection in Autofac. Therefore I created a a custom attribute which is a postsharp aspect which automatically injects my classes:
[AutofacResolve]
public IStorageManager StorageManager { get; set; }
My aspect:
[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AutofacResolveAttribute : LocationInterceptionAspect
{
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (!args.Location.LocationType.IsInterface) return;
if ( args.Value != null )
{
args.Value = DependencyResolver.Current.GetService(args.Location.LocationType);
args.ProceedSetValue();
}
}
}
I know the answer on the question is already given but I thought this was a really neat way of solving automatic property injection in Autofac. Maybe it'll be useful to somebody in the future.
Use Property Injection:
builder.Register(c => LogManager.GetLogger("LoggerName"))
.As<ILog>();
builder.RegisterType<CustomClass>()
.PropertiesAutowired();
Property injection works for Properties and not for Fields. In your class, Log is a field and not a property and hence it will never get resolved by the Autofac.
I didn't want to use postsharp so I made a quick solution, but it doesn't auto inject. I am new to Autofac, and it should be possible to build on to this solution.
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class AutofacResolveAttribute : Attribute
{
}
public class AutofactResolver
{
/// <summary>
/// Injecting objects into properties marked with "AutofacResolve"
/// </summary>
/// <param name="obj">Source object</param>
public static void InjectProperties(object obj)
{
var propertiesToInject = obj.GetType().GetProperties()
.Where(x => x.CustomAttributes.Any(y => y.AttributeType.Name == nameof(AutofacResolveAttribute))).ToList();
foreach (var property in propertiesToInject)
{
var objectToInject = Autofact.SharedContainer.Resolve(property.PropertyType);
property.SetValue(obj, objectToInject, null);
}
}
}
Use it with this call:
AutofactResolver.InjectProperties(sourceObject);
Use Property Injection (In addition to #cuongle answer).
Option 1:
builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();
builder.RegisterType<Product>()
.WithProperty("Log", LogManager.GetLogger("LoggerName"));
Option 2:
Or you can add a SetLog method to the Product class:
public class Product
{
public static ILog Log { get; set; }
public SetLog(Log log)
{
this.Log = log;
}
}
This way you won't have to call LogManager.GetLogger("LoggerName") twice but to use the context of the builder in order to resolve the Log.
builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();
builder.Register(c =>
var product = new Product();
product.SetLog(c.Resolve<Log>());
return product;
);
Option 3:
Use the OnActvated:
The OnActivated event is raised once a component is fully constructed.
Here you can perform application-level tasks that depend on the
component being fully constructed - these should be rare.
builder.RegisterType<Product>()
.OnActivated((IActivatedEventArgs<Log> e) =>
{
var product = e.Context.Resolve<Parent>();
e.Instance.SetParent(product);
});
These options gives more control, and you will not have to worry about #steven comment:
The scary thing with PropertiesAutowired however is that it does
implicit property injection, which means that any unresolvable
dependencies will be skipped. This makes it easy to miss configuration
errors and can result in application that fails at runtime
There is an interface IPropertySelector that you can implement and pass the implementaiton via .PropertiesAutowired(new MyPropertySelector()). This will allow you to implement any logic you want.

Categories

Resources