Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexible Logging interface design in C#

Tags:

c#

logging

I want to write my own Logging classes (in C#) which implement a standard interface, which I can call from any part of the code.

My idea is to have multiple Log classes implement the Logger interface, each for its specific log destination, for example, a FileLogger will implement logging to a file, a TextBox logger will implement logging into a Multi Line TextBox in a Form, a DBLogger will implement logging to a database table, etc.

Further, each logger class can have a nested logger or chained logger classes, so that a single call to Log() method from the application code can log the message in multiple destinations; example log to a file and a textbox on Form in a single call.

The difficulty I am facing is this:

Usually I log to a running log file (which will contain all log messages required for debugging), a review log file (which will contain only log messages to be reviewed by the user, or which require user action), a Multi Line textbox on screen (which will replicate all log messages to give a progress indication to the user), and another Multi Line textbox (which will log only messages required for user to review).

When I call logger.Log(message), some messages may not apply to a particular log destination. For example, some message may be intended to be logged only in a running log file or progress textbox, but not in the user review textbox, and vice versa.

Since the loggers will be chained so that a single function call can log into all required destinations, how can a particular logger identify that the log message is not intended for it and hence ignore the log message?

My sample log interface is:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public void AddLogger(Logger chainedLogger);
    public void RemoveLogger(Logger chainedLogger);
}

public class FileLogger : Logger
{
      //implement methods
}

public class TextBoxLogger : Logger
{
      //implement methods
}

public class DBLogger : Logger
{
      //implement methods
}

EDIT 1:

To be more precise, there could be 4 loggers: 2 file loggers and 2 textbox loggers. A particular message is suppose meant for 1 of the textbox loggers, and 1 of the file loggers; how should my design handle this?

EDIT 2: Please do not suggest existing logging frameworks. I just want to write it on my own !

EDIT 3: Ok. I have a design. Please give your feedback and probably fill the gaps.

The revised interface:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void Log(int loggerIds, string msg);
    public void Log(int loggerIds, string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public int getLoggerId();
}

public enum LoggerType
{
    File,
    TextBox
};

public class LoggerFactory
{
    public Logger getLogger(LoggerType loggerType)
    {

    }
}

The LoggerFactory class will be the sole way to instantiate a logger. This class will assign a unique id to each instance of a logger. This unique id will be a power of 2. Example, 1st logger will get id 1, 2nd will get id 2, 3rd will get 4, and 4th will get 8, and so on.

The returned logger object can be typecast to specific class, and further values like filePath, textbox, etc. can be set by the caller, or else I can have multiple methods in LoggerFactory: one for each type of logger, which will accept specific parameters.

So, suppose we have 4 loggers with ids 1,2,4,8. A particular message which has to be processed by the 1st and 3rd logger (i.e. logger ids 1 and 4) has to be logged using the function:

    public void Log(int loggerIds, string msg);

The value to be passed to loggerIds should be "0101". Each logger will check whether its logger id bit is ON. If yes, only then it will log the message.

Now in the function signatures, I have mentioned int type, but which is the specific optimised type for performing bit manipuations and comparisons?

In this approach, there can probably be a limit on the max no. of loggers, but that is fine with me. Please give your feedback.

Note: Currently I am still on .NET 2.0. If possible, suggest solution within .NET 2.0, else fine, I can move to higher versions.

CONS of this design: Each class which needs to log, needs to know about all the available loggers instantiated by the application, and accordingly set up the bit pattern. Any ideas how to have a loosely coupled design?

like image 472
AllSolutions Avatar asked Dec 15 '22 15:12

AllSolutions


2 Answers

Why don't you look at (or indeed use) an existing logging framework such as log4net or NLog.

They have the concept of a log level (e.g. trace, info, error etc) as well as being able to filter by the name of the log (which is normally the fully qualified type name which invoked the logging call). You can then map these to one or more 'targets'.

like image 137
devdigital Avatar answered Dec 26 '22 16:12

devdigital


"Please do not suggest existing logging frameworks. I just want to write it on my own !"

Accepted answer: Don't reinvent the wheel! Use this existing logging framework!

facepalm

The best answer, which is my answer, goes something like this. Use interfaces if you want plug and play functionality. You can make it easily configurable. Here's the high level run down.

  1. Use a config file to indicate what type of logger you want which implements your logging interface

  2. Use reflection to instantiate the type you pull from your config file AT RUNTIME.

  3. Pass in your logger interface you just made via constructor injection inside your classes.

You're not reinventing the wheel by designing by interface. If you make your interface general enough, it is implementation non specific (ideally). This means if log4net goes to crap, or is no longer supported, you don't have to RIP OUT AND MODIFY ALL YOUR CALLING CODE. This is like hard-wiring a lamp directly into your house to turn it on. For the love of god, please don't do that. The interface defines the contract by which the components interact, not the implementation.

The only thing I can think of is, look at existing logging frameworks, find the common elements, and write your interface as the intersection of the common features. Obviously there are going to be features that you miss. It depends on how much flexibility you want. You can use Log4net, or the Microsoft Event Viewer logger, or both! No implementation details are re implemented. And it is a MUCH less coupled system than having everything in your code tied to one technology / framework.

like image 38
Nathvi Avatar answered Dec 26 '22 18:12

Nathvi