Good practice is that the logger as a singleton?

I had the habit of passing the registrar to the constructor, for example:

public class OrderService : IOrderService { public OrderService(ILogger logger) { } } 

But this is pretty annoying, so I used this property for some time:

 private ILogger logger = NullLogger.Instance; public ILogger Logger { get { return logger; } set { logger = value; } } 

It's annoying too - it's not dry, I need to repeat this in every class. I could use the base class, but then again - I am using the Form class, so I need FormBase, etc. Therefore, I think it would be a drawback of a singleton with an exposed ILogger, so someone would know where to get the recorder:

  Infrastructure.Logger.Info("blabla"); 

UPDATE: As Merlin correctly noted, I should mention that in the first and second examples I use DI.

+69
c # dependency-injection logging singleton
Dec 12 '11 at 10:11
source share
9 answers

It's annoying too - it's not DRY

It's true. But there is only so much that you can do for the end-to-end care that permeates every type that you have. You must use the logger everywhere, so you must have a property on these types.

So, let's see what we can do about it.

Singleton

The singletones are terrible <flame-suit-on> .

I recommend sticking to the injection properties, as you did with the second example. This is the best factoring you can do without resorting to magic. It is better to have an explicit dependency than to hide it with a singleton.

But if singles save you considerable time, including all the refactoring you will ever have to do (crystal ball time!), I suppose you could live with them. If ever there has been use of Singleton, perhaps it is. Keep in mind that the cost, if you ever want to change your mind, will be about as high as it is.

If you do, check out the answers of other people using the Registry template (see description), and those who register (reset) a singleton factory, rather than a single instance of the log.

There are other alternatives that may work just as well, without any compromise, so you should check them out first.

Visual Studio Code Snippets

You can use Visual Studio code snippets to speed up the entry of this repeating code. You can enter something like a logger tab , and the code will magically display for you.

Using AOP for DRY off

You can eliminate some of this property injection code by using an AOP-oriented programming (AOP) framework such as PostSharp to automatically generate some of it.

It might look something like this when you are done:

 [InjectedLogger] public ILogger Logger { get; set; } 

You can also use your sample method tracking code to automatically track the input and output of the method code, which may eliminate the need to add some of the logger properties together. You can apply the attribute at the class level or in the namespace:

 [Trace] public class MyClass { // ... } // or #if DEBUG [assembly: Trace( AttributeTargetTypes = "MyNamespace.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public )] #endif 
+32
Dec 13 '11 at 7:18
source share

I put a log instance in a dependency injection container, which then injects the logger into the classes that need it.

+35
Dec 12 '11 at 10:23
source share
Good question. I believe that in most projects, logger is singleton.

Some ideas just come to my mind:

  • Use a ServiceLocator (or another injection dependency container if you are already using one) that allows you to share a logger through services / classes, so you can create an instance of a registrar or even several different registrars and share it through ServiceLocator, which will obviously be singleton, a kind of Inversion of Control . This approach gives you great flexibility with respect to the process of creating a log instance and the initialization process.
  • If you need a logger almost everywhere, implement extension methods for the Object type so that each class can call log methods such as LogInfo() , LogDebug() , LogError()
+22
Dec 12 '11 at 10:19
source share

Syntax is a good idea. An even better idea is to use the Registry template, which gives you a bit more control over instantiation. In my opinion, a singleton pattern is too close to global variables. When creating or reusing a registry processing object, there is room for future changes to the rules for creating instances.

The registry itself can be a static class to give simple syntax for accessing the log:

 Registry.Logger.Info("blabla"); 
+13
Dec 12 '11 at 10:14
source share

A simple singleton is not a good idea. This makes it difficult to replace the registrar. I use filters for my registrars (some noisy classes can only log warnings / errors).

I use a singleton pattern in conjunction with a proxy pattern for the factory log:

 public class LogFactory { private static LogFactory _instance; public static void Assign(LogFactory instance) { _instance = instance; } public static LogFactory Instance { get { _instance ?? (_instance = new LogFactory()); } } public virtual ILogger GetLogger<T>() { return new SystemDebugLogger(); } } 

This allows me to create a FilteringLogFactory or just a SimpleFileLogFactory without changing the code (and therefore following the Open / Closed principle).

Sample extension

 public class FilteredLogFactory : LogFactory { public override ILogger GetLogger<T>() { if (typeof(ITextParser).IsAssignableFrom(typeof(T))) return new FilteredLogger(typeof(T)); return new FileLogger(@"C:\Logs\MyApp.log"); } } 

And use the new factory

 // and to use the new log factory (somewhere early in the application): LogFactory.Assign(new FilteredLogFactory()); 

In your class to register:

 public class MyUserService : IUserService { ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>(); public void SomeMethod() { _logger.Debug("Welcome world!"); } } 
+9
Dec 13 '11 at 8:11
source share

In .NET there is a book, "Injecting Dependencies." Based on what you need, you should use interception.

This book has a chart to help you decide whether to use constructor injection, property injection, method injection, environment context, interception.

The way one of the reasons uses this diagram:

  • Do you have an addiction or is it needed? - I need her
  • Is this a cross-cutting issue? - Yes
  • Do you need an answer? - No

Use interception

+3
Dec 13 '11 at 19:18
source share

If you want to look at a good logging solution, I suggest you look at the Google engine with python, where logging is as simple as import logging , and then you can simply logging.debug("my message") or logging.info("my message") , which really saves it as simple as it should be.

Java did not have a good registration solution, i.e. log4j should be avoided because it practically forces you to use singletones that are said to be β€œterrible” and I have had terrible experience trying to make the log output the same log expression only once when I suspect that the reason is double logging was that I have one Singleton logging object in two class loaders in the same virtual machine (!)

I apologize for the fact that you are not so specific to C #, but from what I saw, the solutions with C # look like Java, where we had log4j, and we also have to make it single-handed.

That's why I really liked the solution with GAE / python , it is as simple as it can be, and you don’t need to worry about class loaders, getting a double-log report or any design template in general.

I hope that some of this information may be relevant to you, and I hope that you want to look at my registration solution, which I recommend, and not that I am intimidating the question of how strongly Singleton is suspected of for the impossibility of having a real singleton when it should run in several class loaders.

0
Dec 13 '11 at 17:57
source share

Another solution that I personally consider the easiest is to use a static Logger class. You can call it from any class method without having to change the class, for example. add property attachment etc. Its quite simple and easy to use.

 Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed 
0
Dec 28 '15 at 12:44
source share

Given that today I use a common logger (ILogger for type T) - the answer is no;)

-four
Dec 12 '11 at 10:14
source share



All Articles