0

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?

Jeric Cantos
  • 278
  • 1
  • 7

2 Answers2

1

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

  1. Centralizing logging output options for the entire application. If you change your standards, you don't have to update many class files.
  2. 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.

Community
  • 1
  • 1
ErnieL
  • 5,773
  • 1
  • 23
  • 27
  • We made use of log4net too but what we wanted was something a bit friendlier for junior devs to use. The issue with log4net is that it has bulky configuration sections, which is great for experienced devs who want to do lots of customizations, but are tiresome to implement if you are just starting out. Our idea was something that you can immediately use just by referencing a dll, with very simple config files. Junior devs without much knowledge of logging or WCF error handling can just reference this dll, and poof, they have a rudimentary error logging and handling system. – Jeric Cantos Feb 20 '13 at 07:17
0

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.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks for your response! So in this scenario, where does the developer get to specify the logging options for the original logger that we created? Would it be in the GetLogger method as well? Also, in essence, will it be one LoggerFactory for every type of logger, i.e. the developer would need to code his own logger factory if ever he wants to use a different logger from the one that's originally provided by the library? – Jeric Cantos Feb 19 '13 at 07:41