Design principles to consider when wrapping a 3rd party logger like log4net - c#

I'm creating a logger for a company that has several types of .NET projects (Windows Services, ASP.NET, etc), and I was considering using log4net as my logger, but I don't want to be married to log4net, so I was thinking of wrapping it in my own assembly. I realize some developers don't recommend wrapping log4net because that would be an anti-pattern, but assuming I was going that route anyway, I had some questions:
I am planning to use the design principles mentioned in this article to design my wrapper (using factory method, interfaces, and reflection, I can simply decide which logger I want to use (whether log4net, elmah or something else) by specifying in the config file:
https://www.simple-talk.com/dotnet/.net-framework/designing-c-software-with-interfaces/
Question is:
Should I create this logger project in a separate Visual Studio solution and just use the dll in my client applications? If so, where would the configuration details for log4net go? Would that be supplied by the client application's config file? If so, is that good design? For instance, if I decided to switch away from log4net to a different logging framework, I would not only have to change the config setting to specify the new concrete logger's assembly/class name, but would also have to remove the log4net config entries (and perhaps add the new logger's config entries). Is this considered as an acceptable design approach?

Oh my goodness your timing is awesome. And that article is very relevant to me so thanks! I am doing this very same thing right now. I realized that log4net is a decent logger, but a terrible library for making a logger.
I agree with the article, in that you should not directly expose to log4net. Unless this is a small app it would be too difficult to switch later. And log4net is showing age so that may happen. I like the interface approach overall.
But, wrapping log4net it is a pain in the butt. So in doing my prototype wrapper I feel like I rewrote 50% of log4net, and discarded 25%. Some issues I found:
log4net will grab the "caller information" for you. Normally that is great. But if you wrap log4net, the caller information will point to your logger. So you will have to explicitly grab it yourself. But log4net doesn't provide a way for you to override the caller information. So now you will have to create your own fields for the caller's file, line number, class, and package. Thus, not only do you not gain the benefit here, but it is actually more work than just doing it yourself.
Log4net uses the old pre C#-4.0 method of grabbing the caller information which is slow.
Your will be unable to completely wrap log4net without wrapping the configuration. The caller has to configure the loggers either in code or in their app.config. If they do it in their app.config then they are putting log4net specific stuff in their app, so you failed to hide it with your wrapper. But if you have your wrapper code automatically perform the configuration, you just lost the flexibility of log4net. The third option is to make your own configuration, but then what good did log4net do for you? You just rewrote another piece of it.
You are stuck with the log levels that come with log4net. In our app, we want "categories" instead of "levels" which I then have to map to the log4net "levels" under the hood. Now, all the predefined log4net filters are of no use to me.
Anyone using your wrapper still has to reference log4net in their project anyway.
If your wrapper needs a way to handle errors, or pass them back to the caller, you will have trouble. log4net has its own internal error handling and you will need to hook into that and provide your own. Otherwise, errors (like a misconfigured appender) will just go out to the console. If it was designed as a library for making loggers, it would just throw the exception back up or provide a simple event.
One thing we wanted to get out of log4net is the ability to write to different outputs without us us having to write that code ourselves. Ex: I've never written to the event log, and I think log4net can do that. But it might be easier for me to rip out the Event logging code, rather than to try and wrap that. Same thing with filters.
There are some other problems I had with log4net that aren't directly related to trying to wrap it necessarily.
The code is old. The interfaces don't use generics where they should. Lots of object.
They use the old System.Collections collections. Similar to #1.
It has ifdefs for .NET 1 versus 2, and ifdefs for the obsolete compact framework. ugh.
It is designed to log strings, not structured objects. I made my own code to do so, and so did these people: http://stephenjamescode.blogspot.com/2014/01/logging-custom-objects-and-fields-with.html and http://element533.blogspot.com/2010/05/mapping-message-object-properties-to.html but this feels like basic functionality.
It doesn't support CSV and it is cumbersome to add. http://element533.blogspot.com/2010/05/writing-to-csv-using-log4net.html
It doesn't have any kind of logging "service"
It doesn't provide a way to read or parse the log.
I found it was more effort to configure the appenders than to write your own. Ex: I mapped a bunch of fields to the AdoNetAppender, but it would have taken me less time to just rewrite AdoNetAppender. Debugging a database field mapping in XML is harder than trying to debug the equivalent C# + ADO.NET code. The XML is more verbose and less type safe. This might be specific to me because I had a very structured set of data and a lot of fields.
Sorry for the really long post, I have lots and lots of thoughts on this topic. I don't really dislike log4net, I just think it is out of date and if you are wrapping it, you might be better off writing your own.

Related

C# how to log file inside a DLL?

I'm developing a DLL and I want to log some data it generates.
I wanted to use "Log4Net", but I found the problem that in a DLL I don't have an "App.config" file where I can write the XML code, so I don't know how to implement this (I'm new in this matters).
I read about "Singleton" but I saw it's better to avoid it since it has it's issues (i.e hide some visibility of the code, problems with unit tests, ...).
So my question is: How and what is the best way to create a log file for the data generated by my DLL?
A DLL - a class library - should never be logging by itself. Even the ones that are there for output - like the one containing Console or even logger code - should never decide to write their own logfile. Logging work - all output work - that is not controllable or even fully controlled by the programmer using your DLL, is just going to be vexing behavior. And you should never write something with Vexing behavior.
Logging is the job of the person using your code, not of your code. If you are writing a Library or really anything else that usually has no output (like a Windows Service), it is customary to have a wrapper project for debugging and testing.
If it is important enough it warants an Exception. If it is not important enough for a Exception - it is propably not important enough at all. It is a daunting challenge to write good Exception handling, nevermind good Exception throwing code. But there are two articles on the mater that I link very often. And I really think would help you get you on the right paths:
https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
They really helped me get a handle on it. And thus far they helped countless others. And their ideas are not even tied to .NET only.
The config file will be connected in running module.
It will be in exe file if it's a console application,
or in web.config in case of web application.
To log the application flow in the DLL,
Just create a Class that create and access the log text file.
In that class, declare the object LoggingClass loggingObject; and then use this instance to access the log file.
In creating object for it, you can use,
public static LoggingClass createOrGetObject()
{
return (loggingObject == null)? new LoggingClass() : loggingObject;
}
Now, just you can call this method to get the same instance that access the log file to write the log.
In this example, Log4Net is not used but works fine for logging.
You don't say who you expect to use your dll.
If it will be used by lots of other people and if the logging is useful to them, then may not want to be forced to use log4net or this may cause problems if they want to use a different version of log4net than you are using.
I have seen several dlls which use Common.Logging to avoid this issue which allows the consumers to use whichever logging package they want.
Having said that, see Configure log4net logging in dll for another possible solution.

Optional logging dependency for C# library?

I've been working on a library in C# and would like to offer capability for it automatically log all exceptions. Ideally, I'd like it to use log4net, thus allowing the client to configure the log however they like (i.e. they can redirect it to the EventLog or a database or what have you).
However, I would like to avoid having the logging dependency if they chose not to use the logging feature. Is this doable?
In other words, is there a way I can optionally have a log4net dependency, depending on what the client sets in config file?
The obvious answer is to use the System.Diagnostics.Trace subsystem with a custom TraceSource. That way you can set up, in the configuration file, any TraceListener you'd like, including log4net, EventLog, text files, Console, or XML files.
If you added a log4net TraceListener then the dependency would be loaded at runtime, and not compiled in. The Trace subsystem has become quite powerful since its inception, and I recommend you look into it.
You can use the tracing sub-system that is already built into .NET - it is configuration controlled.
I wouldn't add a dependency myself - simply emit messages through the tracing API - the client can decide how to log them if they so wish.
Personally I prefer the Log4Net API to that provided by System.Diagnostics.Trace.
So I have my own abstraction layer that uses a provider-model design pattern, and exposes an API similar to log4net. I can then plug in a log4net provider, but am free to replace it with a different provider (e.g. System.Diagnostics.Trace; EntLib) in the future.
The implementation is very similar to this one.
By doing this, applications have no direct dependency on the underlying logging provider, and you can easily switch to a different provider if you (or your customers) have other preferences.

Remove dependency to logging code

I have more like desing question as I'm refactoring quite big piece of code that I took over.
It's not modular, basically it's pseudo-object-oriented code. It contains hard coded dependencies, no interfaces, multiple responsibilities etc. Just mayhem.
Among others it contains a great deal of internal calls to class called Audit, that contains methods like Log, Info, LogError etc... That class has to be configured in application config in order to work, otherwise it's crash. And that's the main pain for me. And please, let's focus on that issue in responses, namely making client code independent of logging classes/solutions/frameworks.
And now, I would like those classes, that have that Audit class dependency hardcoded, refactored in order to obtain several benefits:
First is to extract them nicely to different assemblies, as I will need some functionality available in other applications (for instance generating attachments code - let's call it AttachmentsGenerator class, that until now was specyfic to one application, but now that code could be used in many places)
Remove internal dependencies so that other application that will take advantage of my AttachmentsGenerator class without the need to add reference to other
Do a magic trick in order to allow AttachmentsGenerator class to report some audit info, traces etc. But I don't want it to have hardcoded implementation. As a matter of fact, I don't want it to be mandatory, so it would be possible to use AttachmentsGenerator without that internal logging configured and without the necessity for the client code to add reference to another assemblies in order to use logging. Bottom line: if client code wants to use AttachmentsGenerator, it adds reference to assembly that contains that class, then it uses new operator and that's all.
What kind approach can I use in terms of design patterns etc to achieve it? I would appreciate some links to articles that address that issue - as it can be timeconsuming to elaborate ideas in answer. Or if you can suggest simple interface/class/assembly sketch.
Thanks a lot,
Paweł
Edit 1: As my question is not quite clear, I'll rephrase it once again: This is my plan, are there other interesting ways to do this?
Seems like the easiest way to do this would be to use dependency injection.
Create a generic ILogger interface with methods for logging.
Create a concrete implementation of ILogger that just does nothing for all the methods (e.g. NullLogger)
Create another concrete implementation that actually does logging via whatever framework you choose (e.g. log4net)
Use a DI tool (spring, structure map, etc.) to inject the appropriate implementation depending on whether or not you want logging enabled.
Implement logging (and any other cross-cutting concerns) as a Decorator. That's way more SOLID than having to inject some ILogger interface into each and every service (which would violate both the Single Responsibility Principle and DRY).

Best practice for using application wide TraceSource

So I have an application where I will be using tracing for logging application activity/errors etc. Most information will go in a log file whereas some errors will go in event viewer too. This application will have lots of classes.
What is the best way of using TraceSource in this case? Should I create a singleton wrapping TestSource class or is there any better way of doing it?
I agree to some extent with #Valdis - log4net and NLog are two examples of very powerful logging frameworks that offer a great deal of flexibility and relatively easy to use configuration (NLog is probably easier to configure than log4net). However, I don't think that is necessary to completely avoid TraceSource. TraceSource is built in so you avoid an extra dependency.
If you are using TraceSource I don't think that a single application wide static TraceSource object is the way to go. I would recommend using more TraceSources, possibly even one per class (that might be overkill, but is a common pattern when using log4net and NLog).
I have put more details in an answer to your other question about when to use more than one TraceSource in an application.
are you strict to use built-in .net eventing and logging infrastructure? I would encourage to use some of the 3rd parties (like logging from Enterprise Library). those are more flexible, more configurable and you do not have a headackes like these - you just write:
Logger.Write(...)

How to conditionally enable actions in C# ASP.NET website

Using a configuration file I want to enable myself to turn on and off things like (third party) logging and using a cache in a C# website. The solution should not be restricted to logging and caching in particular but more general, so I can use it for other things as well.
I have a configuration xml file in which I can assert that logging and caching should be turned on or off (it could also be in the Web.Config, that's not the point right now) which will result in for example a bool logging and a bool caching that are true or false.
The question is about this part:
What I can do is prepend every logging/caching related statement with if (logging) and if (caching).
What is better way of programming this? Is there also a programming term for this kind of problem? Maybe attributes are also a way to go?
Why not just use the web.config and the System.Configuration functionality that already exists?
Your web app is going to parse web.config on every page load anyway, so the overhead involved in having yet another XML config file seems overkill when you can just define your own section on the existing configuration.
I'm curious what kind of logging/caching statements you have? If you have some class that is doing WriteLog or StoreCahce or whatever... why not just put the if(logging) in the WriteLog method. It seems like if you put all of your logging caching related methods into once class and that class knew whether logging/caching was on, then you could save your self a bunch of If statements at each instance.
You could check out the Microsoft Enterprise Library. It features stuff like logging and caching. The logging is made easy by the fact you always include the logging code but the actual logging beneath it is controlled by the settings.
http://msdn.microsoft.com/en-us/library/cc467894.aspx
You can find other cool stuff in the patterns and practices group.
Consult http://msdn.microsoft.com/en-us/library/ms178606.aspx for specifics regarding configuring cache.
I agree with foxxtrot, you want to use the web.config and add in a appsetting or two to hold the values.
Then for the implementation on checking, yes, simply use an if to see if you need to do the action. I highly recommend centralizing your logging classes to prevent duplication of code.
You could use a dependency injection container and have it load different logging and caching objects based on configuration. If you wanted to enable logging, you would specify an active Logging object/provider in config; if you wanted to then disable it, you could have the DI inject a "dummy" logging provider that did not log anything but returned right away.
I would lean toward a simpler design such as the one proposed by #foxxtrot, but runtime swapping out of utility components is one of the things that DI can do for you that is kind of nice.

Categories

Resources