5

I've got to the point in my design, where I am seriously considering a singleton.

As we all know, the "common" argument is "Never do it! It's terrible!", as if we'd littered our code with a bunch of goto statements.

ServiceStack is a wonderful framework. Myself and my team are sold on it, and we have a complicated web-service based infrastructure to implement. I have been encouraging an asynchronous design, and where possible - using SendAsync on the service-stack clients.

Given we have all these different systems doing different things, it occurred to me I'd like to have a common logger, (A web service in itself actually, with a fall-back to a local text file if the web service is not available - e.g. some demons are stalking the building). Whilst I am a big fan of Dependency Injection, it doesn't seem clean (at least, to me) to be passing a reference to a "use this logger client" to every single asynchronous request.

Given that ServiceStack's failure signature is a Func<TRESPONSE, Exception> (and I have no fault with this), I am not even sure that if the enclosing method that made the call in the first place would have a valid handle.

However, if we had a singleton logger at this point, it doesn't matter where we are in the world, what thread we are on, and what part of a myriad of anonymous functions we are in.

Is this an accepted valid case, or is it a non-argument - down with singletons?

Community
  • 1
  • 1
Moo-Juice
  • 38,257
  • 10
  • 78
  • 128
  • "As we all know, the "common" argument is "Never do it! It's terrible!"" common where? – Filip Apr 24 '13 at 20:46
  • I pretty much never use static classes, and I avoid singleton, but I don't see them as terrible, and I don't want my code to depend on them. – eandersson Apr 24 '13 at 20:47
  • 1
    @Filip, you won't have to do much searching on Google or StackOverflow to find the "wrath of the Anti-Singleton" Brigade. Whilst there are some who believe it will always have its place in some niche scenarios, there are also some who will down-vote an answer if it used a singleton as an *example*. So, I figured it best to ask the question, in the interests of best-practice. And that's what this site is for :) – Moo-Juice Apr 24 '13 at 20:49
  • 1
    It’s just that singleton’s aren’t necessary. With a good usage of dependency injection you won’t even get to the point where you could create an object which *should only exist once*. It’s just given to you by the DI container, and the container alone manages its lifetime, ensuring it is only created once. – poke Apr 24 '13 at 21:07
  • 1
    @poke I find it ironic that unless you have some way to pass the container around, the container itself is typically a singleton (StructureMap.ObjectFactory anyone?). – Andy Apr 25 '13 at 19:39

4 Answers4

3

There's only one problem with classic implementation of a singleton - it is easily accessible, and provokes direct use, which leads to strong coupling, god objects, etc.

under classic implementation I mean this:

class Singleton
{
   public static readonly Singleton Instance = new Singleton();
   private Singleton(){}
   public void Foo(){}
   public void Bar(){}
}

If you use singleton only in terms of an object lifecycle strategy, and let IoC framework manage this for you, maintaining loose coupling - there is nothing wrong with having 'just one' instance of a class for entire lifetime of application, as long as you make sure it is thread-safe.

Alexander
  • 4,153
  • 1
  • 24
  • 37
  • And this is where my mind is at, at the time of writing. It would have a single method. `Log(LogEntry entry)`. The rest is more of a mindset-thing. We *don't* need to log everything we do. We need to log when something happened that needs attention, or we want some kind of a metric on performance (there may be other cases...). I don't see the logger "interface" changing at all over time. It's a "hey, log this stuff somewhere, please" and then move on. – Moo-Juice Apr 24 '13 at 21:15
  • 1
    So what's your concern? You don't want to inject single ILogger interface into every request for performance reasons? Memory overhead is not a problem, since logger would be a singleton. As for changing logger interface - you can always extend it behind the counter, maintaining interface. Implementation may log performance metrics, write to files, db, send emails etc. Also don't forget about testing, as @Steven mentioned in his answer. If you are using direct reference to logger - you would have to initialize or somehow mock it in your tests. – Alexander Apr 24 '13 at 21:18
3

Logging is one of the areas which makes sense to be a singleton, it should never have any side-effects to your code and you will almost always want the same logger to be used globally. The primary thing you should be concerned with when using Singletons is ThreadSafety, which in the case of most Loggers, they're ThreadSafe by default.

ServiceStack's Logging API allows you to both provide a substitutable Logging implementation by configuring it globally on App_Start with:

LogManager.LogFactory = new Log4NetFactory(configureLog4Net:true);

After this point every class now has access to Log4Net's logger defined in the Factory above:

class Any
{
    static ILog log = LogManager.GetLogger(typeof(Any));
}

In all Test projects I prefer everything to be logged to the Console, so I just need to set it once with:

LogManager.LogFactory = new ConsoleLogFactory();

By default ServiceStack.Logging, logs to a benign NullLogger which ignores each log entry.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • This is kind of how my mind was working. I do not see an issue with using a singleton here (and believe me, I try and avoid them). For the ease of simplicity, and the things you outline above, well - why not? – Moo-Juice Apr 24 '13 at 21:27
0

If you are placing that common logging behind a static facade that application code calls, ask yourself how you would actually unit test that code. This is a problem that Dependency Injection tries to solve, but you are reintroducing it by letting application logic depend on a static class.

There are two other problems you might be having. To question I have for you is: Are you sure you don't log too much, and are you sure you aren't violating the SOLID principles.

I've written an SO answer a year back that discusses those two questions. I advice you to read it.

Community
  • 1
  • 1
Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thanks for your answer, Steven. The existing infrastructure *does* log too much, but that's not my concern really - it's the "moving forward" infrastructure. I'm a big fan of logging *when necessary*. There are many internal and 3rd party systems in our business, and whilst my predecessors may have been over-zealous in the "Log it!" department (obviously with different implementations.. wtf?), I'd like a cleaner "Log when you need to" strategy. – Moo-Juice Apr 24 '13 at 21:13
0

As always, I prefer to have a factory. This way I can change the implementation in future and maintain the client contract.

You could say that singleton's implmenentation could also change but factories are just more general. For example, the factory could implement arbitrary lifetime policy and change this policy over time or according to your needs. On the other hand, while this is technically possible to implement different lifetime policies for a singleton, what you get then should probably not be considered a "singleton" but rather a "singleton with specific lifetime policy". And this is probably just as bad as it sounds.

Whenever I am to use a singleton, I first consider a factory and most of the times, the factory just wins over singleton. If you really don't like factories, create a static class - a stateless class with static methods only. Chances are, you just don't need an object, just a set of methods.

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106