IOC for Attributes / Decorators in .NET - c#

Our team has created a library that automates the implementation of exception handling and logging for WCF Services. Using this library, developers need only to decorate a service with a custom attribute, setup some simple configuration file entries, and they can already take advantage of a generic exception handlin​g and logging mechanism.
Here's an example of how the library is used:
[ErrorHandlingBehavior (LogWriterOption.EmailLogWriter, LogWriterOption.SQLLogWriter)]
public class SampleService : ISampleService
{
public string GetData(int value)
{
throw new DivideByZeroException();
//return string.Format("You entered: {0}", value);
}
}
The ErrorHandlingBehavior class makes use of a Logger object that takes in the LogWriterOption enums in the parameters to figure out where to log.
Our original intention was to allow the developer to specify his own logging mechanism and supply it to the ErrorHandlingBehavior, so as to remove the dependency of the solution on the Logger class (instead it takes in any class that implements ILogger). However, specifying attributes in the manner below produces an error:
[ErrorHandlingBehavior (new Logger (new HashSet<LogWriterOptions>
{LogWriterOption.EmailLogWriter, LogWriterOption.SQLLogWriter}))]
It seems we cannot instantiate anything when specifying attributes, and therefore we are now unable to let users specify their own logging mechanism.
Would anyone know of a way around this? How can we feed an instance of a class implementing ILogger to our attribute instead of hard-wiring the depenendency?

I also wrote some logging exception handling behaviors. In situations like this I always asked myself:
What would log4net do?
Your LogWriterOptions appear to translate to log4net appenders. Appenders are generally best done through xml configuration because their requirements change by environment. (Its the logging equivalent of don't put your WCF client binding in code.) In other words, when developing locally: don’t send email and just output to a local text file. When running in QA: output to the DB but don’t send email with a tester breaks something on purpose. In production: do something else completely different. Log4net appenders support all these types of post compile changes (and more).
Back to your question:
In your approach, I would pass the ErrorHandlingBehavior a behavior name as a string like “StandardLogging” which would look up a configurable behavior that results in EmailLogWriter and SQLLogWritter being used.
An alternate approach which is common in logging frameworks is to pass the type of the class being logged. If that type is not explicitly configured, it gets the default appenders.
Note that this configuration approach has the added benefit of
Centralizing logging output options for the entire application. If you change your standards, you don't have to update many class files.
Standardizing what log writer options different pieces of code are using. In the code review meeting you simply ask “Are you using standard logging output?” Check.
Update in response to your "keep it simple" comment:
If keep it simple is the goal, I would say pass nothing to your behavior's constructor. Instead put all the log4net config information in its own log4net.config file and store that as a part of the common logging libraries in source control. Then new projects (or junior devs) are just required to add
<configuration>
<log4net configSource="log4net.config" />
</configuration>
to app.config. The bonus to this approach is that as a part of our build process we defined different log4net.config files for deployment to different environments.

You could use the factory pattern. Have the developer specify a type that will be used to provide an ILogger instance:
[ErrorHandlingBehavior(LoggerFactoryType = "FooBar.MyLoggerFactory")]
This type could implement an interface of yours:
public interface ILoggerFactory
{
ILogger GetLogger();
}
and then inside your custom attribute you could first get the factory type using the Type.GetType method, check if it implements the ILoggerFactory interface, instantiate the factory using the Activator.CreateInstance method and finally call the GetLogger method on that instance.

Related

Custom contract on a LogReceiverService Nlog

I'm having at the moment a big solution with different projects sending logs with Nlog to LogReceiverService target.
I would like to use an interface so I have only 1 installation of Nlog in 1 project of my solution.
So I created a new interface to call instead of NLog.LogReceiverService.ILogReceiverClient (inherited from it) but when I initialize my logger, it's still looking for the contract NLog.LogReceiverService.ILogReceiverClient
Is this a normal operation. Is there a way to change this contract?

PostSharp - How to customize log output of generic list input parameters?

I'm using the [Log] attribute on several methods in my code. This works great to log the entry and exit of each method as well as print the details of the input parameters.
However when the input parameter is a generic list, then the log details are not very useful.
For example:
[Log]
public List<InventoryResponse> GetInventory(List<InventoryRequest> request)
{
...
This will output the following to the log file:
Entering: Inventory.GetInventory(this = {CC.Viero.Inventory.Service.Inventory}, {System.Collections.Generic.List`1[CC.Viero.Inventory.Service.InventoryRequest]})
I would like to output the contents of the list parameter instead of just printing the list object name. Is there a way to customize this?
This might be bit of over engineering but when I use Postsharp I usually create my own logging aspects by inheriting from the "OnMethodBoundaryAspect" this gives me control as to what is logged, how, and where.
I have an example of implementing a custom logging aspect which can be found here https://github.com/vnvizitiu/AOP/blob/master/PostSharpTutorial/LoggerAspect/LoggingAspect.cs, though this is a more generic approach you can also see this link
http://www.postsharp.net/blog/post/Day-4-OnMethodBoundaryAspect
or this one http://www.agile-code.com/blog/aop-method-interception-in-postsharp-with-onmethodboundaryaspect/
The current version of the PostSharp logging library uses only the standard string.Format() facility of .NET, so you can customize the output only by overriding ToString() method on your own classes.
Support for custom formatters is planned for one of the future versions of the PostSharp logging library. Until then the workaround is to implement a custom logging aspect. You can find a getting started example on GitHub: https://github.com/postsharp/PostSharp.Samples/tree/master/PostSharp.Samples.CustomLogging

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

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.

Most standard way to set configuration settings in a Class Library

I'm developing a Class Library / API and I need to store some global parameters that will be used by some classes. I thought about two main ways to do so (ignoring configuration files which I'd prefer not using in this case):
1) Specifying the parameters in a static class, like this:
// Stores and validates settings
ApiConfiguration.SetConfiguration("some values or class here");
var methods1 = new MyFirstApiMethods();
methods1.DoStuff(); // Internally uses static ApiConfiguration.
var methods2 = new MySecondApiMethods();
methods2.DoOtherStuff(); // Internally uses static ApiConfiguration.
2) Creating an instance of the configuration class and pass it to the classes, like this:
// Create an instance of the configuration class
var config = new ApiConfiguration();
config.ServerName = "some-server-name";
var methods1 = new MyFirstApiMethods(config);
methods1.DoStuff(); // Uses the supplied ApiConfiguration instance.
var methods2 = new MySecondApiMethods(config);
methods2.DoOtherStuff(); // Uses the supplied ApiConfiguration instance.
The fist option feels more natural for me, but I can think of some possible downsides (if the config is set in two places with different values, for example).
I want to know the possible downsides of each implementation and what is the most common way to do this in known projects of this nature.
I'd say #1 is the most common. I know you said that you didn't want to use configuration files, but if you look at how .NET uses app.config I think you will see that a similar approach to #1 is taken. You don't see instances of app.config settings being passed around to every method/function that needs to read a setting. I normally do VB.NET, for which there is a static My.Settings class that basically achieves the same thing as your #1.
The biggest disadvantage I see to #2 (and probably why it is less common) is that the config class can get passed around a lot. If only a small number of methods actually need to read the config it might be ok, but if many methods need to read the config it starts to become a headache. In my opinion it also clutters up the method signatures. Imagine a class deep in the library that needs to read the config; you may have to pass the config through several higher level classes just to pass it through to the class that needs it.
I'd recommend at least considering using app.config or web.config as either one of these already have built in functionality for this type of thing.
EDIT
I was waiting for Brannon to respond with an example, but since he hasn't I'll go ahead and chime in. IOC containers are great tools to help with dependency injection, but I wouldn't dream of introducing one just for a settings class. If you were already using one, that might be a different story. Lets suppose that you were already using an IOC container and wanted to use it for your config class. That means you still have method signatures that look like:
Function Add (FirstNumber, SecondNumber, Config)
Admittedly that example is a stretch, but you get the idea. The IOC container will resolve your Config dependency (it will create the config class for you), but you still have the config as a parameter to each method/constructor that needs it.
To be honest some of it comes down to personal preference. Keep in mind that VS/.NET uses #1 out of the box when you use app.config. I know that static classes are frequently frowned upon and rightfully so in many cases, but I think that settings/config classes are exceptions to the rule.

log4Net dilemma

I have a C# solution containing multiple C# projects. I am planning to add logging in it. This logging should be available in all the projects and preferably use log4Net with rolling file logs.
With the above said premise, I could think of two ways to do that.
Initialize logger in entry point (Program class) of the solution & Get the logger instance & use it as a member variable for every class that needs logging.
Add another project, Utilities & define a Logging class with static logging methods. This class should be initialized in entry point (Program class) of the solution.
What could be the best possible solution?
I have a similar situation. What we've done is use 1 app config for all the projects and use links to reference it.
In the app.config for your app you set the log4net config Section
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
And later set the Appender:
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
...
And in each class you want to log you put a line similar to this:
private static readonly ILog log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
Then each class grabs the same logger (singleton).
Would that work for you?
In my projects, I wrote a wrapper around LOG4NET to (theoretically) exchange it with other logging frameworks like NLOG without breaking my code.
My own logging class is accessible through a static singleton. It does initialization in this singleton.
(BTW: I published the source code over at the Code Project some time ago, maybe it gives you some inspiration)
If you're thinking about initializing something at the entry point every single time, then you're writing yourself a case for dependency injection. Using a DI library (like Unity, Castle, or whatever flavor you personally like), you could do either constructor injection or property injection to get your logger class (or Log4Net) initialized automatically.
Additionally, you can use the DI kernel to make it a singleton, so that you only have one instance of it active.
Building off of Queso's answer, we had .dll's that were imported with reflection at runtime. In order to get them to use Log4Net we created a log4net.config file with all the appropriate config sections. We also used the same line of code to intialize the log in each class that Queso references. The seperate config allowed us to use it throughout the entire app domain.
EDIT we also had to make a modifcation to the appsettings file to allow for this.
http://haacked.com/archive/2005/03/07/ConfiguringLog4NetForWebApplications.aspx
Option 1 is the way to go.
If you use static methods you loose the ability to use hierarchical loggers. This feature allows you to configure your log output differently for individual classes or even for entire sub systems e.g. YourNameSpace.Security. This tutorial elaborates on this topic (also the other chapters are a good read).
Building a wrapper is certainly not a bad idea, but it is not strictly necessary. It does allow for a few things though: change log framework, use DI, add additional log levels (e.g. log.Verbose()) or different overloads for logging...
Btw. I would initialize the loggers like this:
ILog log = LogManager.GetLogger(typeof(YourClass));

Categories

Resources