views:

529

answers:

17

Here is a problem I've struggled with ever since I first started learning object-oriented programming: how should one implement a logger in "proper" OOP code?

By this, I mean an object that has a method that we want every other object in the code to be able to access; this method would output to console/file/whatever, which we would use for logging--hence, this object would be the logger object.

We don't want to establish the logger object as a global variable, because global variables are bad, right? But we also don't want to have the pass the logger object in the parameters of every single method we call in every single object.

In college, when I brought this up to the professor, he couldn't actually give me an answer. I realize that there are actually packages (for say, Java) that might implement this functionality. What I am ultimately looking for, though, is the knowledge of how to properly and in the OOP way implement this myself.

A: 

You could look at the Singleton pattern.

Per Hornshøj-Schierbeck
A: 

Create the logger as a singleton class and then access it using a static method.

Hortitude
+1  A: 

I've always used the Singleton pattern to implement a logging object.

awhite
Whoever voted this down: why?
awhite
+1  A: 

I think you should use AOP (aspect-oriented programming) for this, rather than OOP.

Paul Reiners
+12  A: 

You do want to establish the logger as a global variable, because global variables are not bad. At least, they aren't inherently bad. A logger is a great example of the proper use of a globally accessible object. Read about the Singleton design pattern if you want more information.

benzado
Couldn't have said it better myself.
Outlaw Programmer
A: 

In practice a singleton / global method works fine, in my opinion. Preferably the global thing is just a framework to which you can connect different listeners (observer pattern), e.g. one for console output, one for database output, one for Windows EventLog output, etc.

Beware for overdesign though, I find that in practice a single class with just global methods can work quite nicely.

Or you could use the infrastructure the particular framework you work in offers.

Tobi
A: 

The Enterprise Library Logging Application Block that comes from Microsoft's Pattern & Practices group is a great example of implementing a logging framework in an OOP environment. They have some great documentation on how they have implemented their logging application block and all the source code is available for your own review or modification.

There are other similar implementations: log4net, log4j, log4cxx

They way they have implemented the Enterprise Library Logging Application Block is to have a static Logger class with a number of different methods that actually perform the log operation. If you were looking at patterns this would probably be one of the better uses of the Singleton pattern.

spoon16
+1  A: 

The best way to learn this might be to simply have a look at the source code of log4net and log4j. Anyhow, I think a usual idea is to have a logger singleton.

hangy
+2  A: 

There are some very well thought out solutions. Some involve bypassing OO and using another mechanism (AOP).

Logging doesn't really lend itself too well to OO (which is okay, not everything does). If you have to implement it yourself, I suggest just instantiating "Log" at the top of each class:

private final log=new Log(this);

and all your logging calls are then trivial: log.print("Hey");

Which makes it much easier to use than a singleton.

Have your logger figure out what class you are passing in and use that to annotate the log. Since you then have an instance of log, you can then do things like:

log.addTag("Bill");

And log can add the tag bill to each entry so that you can implement better filtering for your display.

log4j and chainsaw are a perfect out of the box solution though--if you aren't just being academic, use those.

Bill K
A: 

I am all for AOP together with log4*. This really helped us. Google gave me this article for instance. You can try to search more on that subject.

Petr Macek
A: 

(IMHO) how 'logging' happens isn't part of your solution design, it's more part of whatever environment you happen to be running in - like System and Calendar in Java.

Your 'good' solution is one that is as loosely coupled to any particular logging implementation as possible so think interfaces. I'd check out the trail here for an example of how Sun tackled it as they probably came up with a pretty good design and laid it all out for you to learn from!

Brabster
A: 

use a static class, it has the least overhead and is accessible from all project types within a simple assembly reference

note that a Singleton is equivalent, but involves unnecessary allocation

if you are using multiple app domains, beware that you may need a proxy object to access the static class from domains other than the main one

also if you have multiple threads you may need to lock around the logging functions to avoid interlacing the output

IMHO logging alone is insufficient, that's why I wrote CALM

good luck!

Steven A. Lowe
A: 

Maybe inserting Logging in a transparent way would rather belong in the Aspect Oriented Programming idiom. But we're talking OO design here...

The Singleton pattern may be the most useful, in my opinion: you can access the Logging service from any context through a public, static method of a LoggingService class.

Though this may seem a lot like a global variable, it is not: it's properly encapsulated within the singleton class, and not everyone has access to it. This enables you to change the way logging is handled even at runtime, but protects the working of the logging from 'vilain' code.

In the system I work on, we create a number of Logging 'singletons', in order to be able to distinguish messages from different subsystems. These can be switched on/off at runtime, filters can be defined, writing to file is possible... you name it.

xtofl
A: 

I've solved this in the past by adding an instance of a logging class to the base class(es) (or interface, if the language supports that) for the classes that need to access logging. When you log something, the logger looks at the current call stack and determines the invoking code from that, setting the proper metadata about the logging statement (source method, line of code if available, class that logged, etc.) This way a minimal number of classes have loggers, and the loggers don't need to be specifically configured with the metadata that can be determined automatically.

This does add considerable overhead, so it is not necessarily a wise choice for production logging, but aspects of the logger can be disabled conditionally if you design it in such a way.

Realistically, I use commons-logging most of the time (I do a lot of work in java), but there are aspects of the design I described above that I find beneficial. The benefits of having a robust logging system that someone else has already spent significant time debugging has outweighed the need for what could be considered a cleaner design (that's obviously subjective, especially given the lack of detail in this post).

I have had issues with static loggers causing permgen memory issues (at least, I think that's what the problem is), so I'll probably be revisiting loggers soon.

rcreswick
+1  A: 

A globally accessible logger is a pain for testing. If you need a "centralized" logging facility create it on program startup and inject it into the classes/methods that need logging. How do you test methods that use something like this:

public class MyLogger 
{
    public static void Log(String Message) {}
}

How do you replace it with a mock?

Better:

public interface ILog 
{
    void Log(String message);
}

public class MyLog : ILog 
{
    public void Log(String message) {}
}
EricSchaefer
A: 

One other possible solution is to have a Log class which encapsulates the logging/storage procedure. That way you can just instantiate a new Log(); whenever you need it without having to use a singleton.

This is my preferred solution, because the only dependency you need to inject is the Database if you're logging via database. If you're using files potentially you don't need to inject any dependencies. You can also entirely avoid a global or static logging class/function.

Lotus Notes
A: 

To avoid global variables, I propose to create a global REGISTRY and register your globals there.

For logging, I prefer to provide a singleton class or a class which provides some static methods for logging.

Actually, I'd use one of the existing logging frameworks.

Stefan Pantke