Reputation: 821
So I've done a lot of research on this and haven't found any answers where I said, "yes, THAT". I'm hoping the ever-erudite StackOverflow crowd can help me out.
I've run into this issue in a couple of different scenarios. Say I have a C# app, and there are important things that I want to log.
public class MyClass
{
...
public void ImportantMethod()
{
DoInterestingThing();
var result = SomethingElseImportant();
if (result == null)
{
logger.Log("I wasn't expecting that. No biggie.");
return;
}
MoreInterestingStuff();
}
The thing I'm interested in is where do I get logger
from.
As I see it I have a few options.
None of these seem like great options. #3 looks to be impossible since I'm logging right in the middle of my business logic and not just doing a simple trace of my method calls, input parameters and/or exceptions thrown. #2, though simple seems like it would be really difficult to unit test. Of course, I would want to unit test everything. #1, though it would work fine, clutters up all my business logic with logging objects that have nothing to do with the business objects themselves.
Any alternative ideas, or thoughts on one of the options above? Much thanks!
EDIT: just to be clear, I already know how to do DI (I use Unity), and I already know a good logging framework (I use log4net). Just wondering a how to use logging in an architecture sense across an application in the smartest way.
* EDIT *
I marked Mark Seeman's answer as the solution. I went through my application and found that most of my logging calls were doing the same thing a decorator could do. Namely, log the entry to the method, any exceptions thrown, and exit return values.
Some cases I still needed to log directly inside a method. An example would be where I want to fail fast in a method which doesn't return anything but not throw an Exception. In those cases I have a singleton which holds a reference a LogProvider
which will in turn retrieve a named log instance. The code looks similar to this:
private ILog logger = LogProviderFactory.Instance.GetLogger(typeof(Foo));
LogProviderFactory has a method SetProvider
which allows you to swap out the singleton. So in unit testing I can do:
// LogProviderFactory.Instance now is our mock
LogProviderFactory.SetProvider(MockLogProvider);
The logging decorator uses the same LogProvider as the singleton (which it obtains through injection), so logging is unified throughout the system.
So really the end solution was mostly option #3, and a hybrid #2 (where it's the service locator pattern but the service is 'injected' into the locator).
AOP
As far as "aspect oriented programming" goes, I was a bit disappointed in the limitations of the language. Hoping AOP will be treated as a first-class citizen in future releases.
Upvotes: 6
Views: 3667
Reputation: 61795
In the Using the request context in a Factory or Factory Method to do contextual binding section of the Ninject Contetual Binding docs I have an example of leveraging your container to inject an appropriate logger for your class by doing (in Ninjectese):
Bind<ILog>().ToMethod( context => LogFactory.CreateLog( context.Request.Target.Type ) );
For tracing type stuff, Mark's interception article describes the best approach.
And can I ask again that you read @Mark Seemann's cited articles in depth before just discarding them without an upvote.
Upvotes: 1
Reputation: 10215
Two bits:
(1) - A pre-built logging framework.
Some people like Log4Net but I'm a EntLibs fan. This does the heavy lifting in terms of actual logging. Tools like EntLibs will let you log to different types of logging repositories (database, message queue, rolling text file, etc). They'll also let you log to different instances based on categories and so forth. They are usually highly configurable.
(2) - Custom class(es) that wrap the logging framework.
So "logger" is something you write, and it calls the logging framework to do the actual logging.
I like this approach for several reasons:
Here's an example of a Informational logging class in a project I've worked on. It has a bunch of easy to call public methods, and one private method that calls the framework (ConcreteLogInformation).
public static void LogInformation(string title, string message)
public static void LogInformation(string title, Dictionary<string, object> extendedProperties)
public static void LogInformation(string title, int eventId, Dictionary<string, object> extendedProperties)
public static void LogInformation(string title, string message, Dictionary<string, object> extendedProperties)
public static void LogInformation(string title, string message, int eventId)
public static void LogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties)
public static void LogInformation(string title, string message, int eventId, string category)
public static void LogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties, string category)
private static void ConcreteLogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties, string category)
Upvotes: 2