I'm refactoring a large application and have the following problem :
The application consists of minimally 6 and maximally 40 VB DLLs (and
OCXes). This is because the is a main application which dynamically loads
subsystems (country-specific payment modules). If only one subsystem is
installed, the application will load only that subsystem. If more are
installed on the PC, other subsystems will get loaded as well. This
explains the strange DLL numbers.

All subsystems, as well as the main application, share references to 6
common DLLs. There are six because of practical problems (less would mean a
+4Mb DLL, more would mean more possible update problems). Now all
subsystems and the main application, as well as shared services inside the
system DLL, need to log once in a while. Since this is a huge application
logging tends to be a reliable means to 'debug' problems detected on client
installations.

The question I have is what you think of my approach for logging, which I'll
explain below.
Since there are many ways to log, I decided to capture a logservice
functionality inside an interface (this is VB, but the problem is in fact
language -independent):
interface iTBXLogService
Public Enum eLogType
eltFirstLogType = 1
eltFatalError = 1
eltError = 2
eltWarning = 3
eltHint = 4
eltLastLogType = 4
End Enum

Public Sub LogString(ByVal eType As eLogType, _
ByVal strModuleName As String, _
ByVal strMessage As String)
End Sub

Now for this there a some basic implementations : FileLogger, NTLogLogger,
MsgBoxLogger, FilteredLogger, TreeViewLogger, TextBoxLogger...
There is one logger (NoLogger) that implements the NullObject design pattern
(do nothing but fully implement the interface) and another (MultiLogger),
which implements a Composite for Loggers (Log to multiple loggers at once).

Now this is tedious to use :
CreateFileLogger ("mylog.txt").LogString eltWarning,
"WvddCore.Sorting.clsSortableCollection.Get Item", "Index out of bounds"

Since a logger is used in many, many places, it makes sense to define a
Singleton to access THE logger. The main application sets up THE logger,
and all clients use it.
The client code becomes slightly easier :
LogServiceSingleton.LogString eltWarning,
"WvddCore.Sorting.clsSortableCollection.Get Item", "Index out of bounds"
There are now two global methods (in a Global multi use in VB, public class
methods in most other languages)
public function LogServiceSingleton as iTBXLogservice
public sub setLogServiceSingleton (byval objNew as iTBXLogservice)

There is one catch : if you want to log before the global logger is
installed (at startup) then there is a possibility of an uninitialized
singleton. We solve this by returning a MsgBoxLogger if there is no logger
installed. That way, at least the message doesn't disappear and the user
can say something when he reports a problem.

Now since a logservice is called a lot, I decided to put a facade on top of
it.
I defined the following global routines :
Sub LogFatalError (ByVal strModuleName As String, ByVal strMessage As
String)
...
Sub LogHint(ByVal strModuleName As String, ByVal strMessage As String)

They all work on the singleton and make client logging easy :
LogWarning, "WvddCore.Sorting.clsSortableCollection.Get Item", "Index out of
bounds"

Now what is your opinion on this. What else do I need for a LogService ?
Is the design sound ? How did YOU do it ?


Thanks in advance
--
Van den Driessche Willy