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.
Related
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".
I'm trying to write a logging class that would work like this:
Log.Info("Something happened");
Log.Error("Something else happened");
Log.Debug("Yet another thing happened!");
It should be accessible from every part of the namespace and quick to write, so I thought it'd be best to make it static. That way one can avoid having to create an object just to log a message.
At this point it is sort of like Console.WriteLine();
However, I wanted it also to be able to have two different modes: LogToConsole and LogToFile.
Thus the following syntax would be the most convenient:
LogConsole.Info("This will display in the console");
LogFile.Debug("This will be saved to a file");
LogAll.Error("This will be saved to a file AND displayed in a console");
However, I realized that there could be an large amount of "modes" multiplied by a very large amount of "logtypes".
How could I do this efficiently, in a way that I only have to write each logtype method once and depending on the derived class that calls the method, action a happens or action b happens?
Ideally I would like to define all methods once, and then create the classes that inherit them. But, since they are static methods their behavior is always the same. I can't tell them: "Find out what your superclass is and execute that class' SaveLog() method".
I realize that this would all be very very easy with abstract classes, but then I'd have to create objects.
Is there any way I could do this in C#?
Thanks!
Like Boo, would also recommend a logger like log4net.
If you do want to write it yourself, I would recommend against static methods as they would inhibit your ability to test the classes / methods that call it. Instead, inject your ILogger interface to all classes that might need logging. Then separate the "mode" from the target, so you can inject a list of targets to your logger.
public interface ILogTarget
{
void Save(string message);
}
public class LogToFile : ILogTarget
{
public void Save(string message)
{
//
}
}
public class LogToConsole : ILogTarget
{
public void Save(string message)
{
//
}
}
public interface ILogger
{
void Debug(string message);
}
public class Logger : ILogger
{
private readonly List<ILogTarget> _targets;
private static Logger _logger;
public Logger(List<ILogTarget> targets)
{
_targets = targets;
}
public void Debug(string message)
{
foreach (var target in _targets)
target.Save($"Debug: {message}");
}
}
public class TheClassThatMakesTheCall
{
private readonly ILogger _logger;
public TheClassThatMakesTheCall(ILogger logger)
{
_logger = logger;
}
public void AMethod()
{
_logger.Debug("some message");
}
}
//In your IoC, register Logger as a type of ILogger, and pass in the targets that you want
//If your target vary per situation, you'll need a ILogTarget factory that returns a different list of loggers based on the situation
You cannot inherit from static classes. But you can get away with making only the functions static. don't make the classes as static. Just make the functions as static, then you can use the "new" keyword in the derived class. It would be something like this
// IF this is your base class
public class Log
{
public static bool Info(string Message)
{
Console.WriteLine(Message + " From Log");
return true;
}
public static bool Success(string Message)
{
return true;
}
public static bool Error(string Message)
{
return true;
}
}
//Then this can be your derived class
public class LogFile : Log
{
public static new bool Info(string Message)
{
Console.WriteLine(Message + " From LogFile");
return true;
}
public static new bool Success(string Message)
{
return true;
}
public static new bool Error(string Message)
{
return true;
}
}
Hope this helps.
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(""));
}
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.
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);
}
}
public class Logger
{
ILogUtility _logutility;
public Logger(ILogUtility logutility)
{
_logutility = logutility;
}
public void Log(LogType logtype, string message)
{
_logutility.Log(logtype, message);
}
}
I need to have the functionality to be flexible and have the ability to remove the LogUtil class in the future and use some thing else.
So I write LoggerUtility wrapper class as follows:
class LoggerUtility<T>
{
Logger logger;
public LoggerUtility()
{
LogUtil<T> logutil = new LogUtil<T>();
logger = new Logger(logutil);
}
public void Log(LogType logtype, string message)
{
logger.Log(logtype, message);
}
}
My client code as follows:
public class TestCode
{
public void test()
{
new LoggerUtility<TestCode>().Log(LogType.Info, "hello world");
}
}
To get loose coupling from LogUtil, I end up writing 2 wrapper classes Logger and LoggerUtility. So in the future, if I have to add another method
in the ILogUtility, I would have to add that method to Logger class and then LoggerUtility.
What is the best way to write LoggerUtility so that I could write the client code as follows:
new LoggerUtility<TestCode>().Log(LogType.Info, "hello world");
Please let me know.
Thanks
It looks like you're adding a level of abstraction where there really doesn't need to be one.
If we start with your end result, LoggerUtility just needs to have an interface that it can use to log things based on the LogType parameter.
Your Logger class, as its currently written, is just a thin wrapper around the ILogUtility interface. So why bother adding that layer? Why can't the Logger class use an ILogUtility instance directly? You could even go one step further and define your interface as ILogUtility<T> and know that when you create a LoggerUtility<Foo> that the instance of the logger it will use will be based on the Foo class.
But honestly, I think you may just be reinventing the wheel here. Take a look at Common Logging for .NET. It will probably ease what you're trying to do and make more sense in the long run.
You don't need a second wrapper, you need either a factory or to use a dependency injection framework to construct an appropriate wrapper around log4net.
Using Ninject, and modifying your interface, you can do
kernel.Bind(typeof(ILogUtility<>)).To(typeof(Log4NetUtil<>);
and instantiate it as
var logger = kernel.Get<ILogUtility<MyClass>>();
where the logger interface/class are:
public interface ILogUtility<T> where T : class
{
void Log(LogType logtype, string message);
}
public class Log4NetUtil<T> : ILogUtility<T> where T : class
{
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);
}
}