HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Extensible logging

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
extensibleloggingstackoverflow

Problem

Whenever I need logging functionality in .net, I use a logging framework, such as NLog. Obviously there's no logging framework for vba, at least none that I know of.

As much as I love using NLog, the way it works, with targets, rules and loggers, was going to be way too heavy and complex for my needs.

So I kept the idea of a logger, and centralized log message formatting in a LogManager static class - this class provides the entire client API; client code doesn't directly deal with ILogger implementations, rather calls the LogManager.Log method, which "dispatches" the log message to all relevant loggers.


LogManager class module

```
Option Explicit

Public Enum LogLevel
TraceLevel = 0
DebugLevel
InfoLevel
WarnLevel
ErrorLevel
FatalLevel
End Enum

Private Type TLogManager
Formatter As ILogMessageFormatter
Loggers As New Dictionary
End Type

Private this As TLogManager

Public Property Get Formatter() As ILogMessageFormatter
Set Formatter = this.Formatter
End Property

Public Property Set Formatter(ByVal value As ILogMessageFormatter)
Set this.Formatter = value
End Property

Public Sub Register(ByVal logger As ILogger)
If Not this.Loggers.Exists(logger.Name) Then
this.Loggers.Add logger.Name, logger
Else
Err.Raise vbObjectError + 1098, "LogManager.Register", "There is already a logger registered with name '" & logger.Name & "'."
End If
End Sub

Public Function IsEnabled(ByVal level As LogLevel) As Boolean

Dim logger As ILogger
Dim item As Variant
For Each item In this.Loggers.Items
Set logger = item
If level >= logger.MinLevel Then
IsEnabled = True
Exit Function
End If
Next

End Function

Public Sub Log(ByVal level As LogLevel, ByVal message As String, Optional ByVal loggerName As String)

Dim logger As ILogger
If loggerName = vbNullString Then

Dim item As Variant
For Each item In this.Loggers.Items

Solution

-
It's really nit-picky, but there's no reason to declare the starting point for an Enum unless you don't want to start at zero.

Public Enum LogLevel
  TraceLevel = 0
  DebugLevel
  '.....


You're also repeating yourself a lot here. It's almost hungarian. They're all LogLevels. I think you'd be fine to shorten these up to Trace,Debug, Info etc. Of course, vba can be a bit strange about when you can and can't call enum members with a full reference like this LogLevel.Trace, so you may or may not want to actually do that. I'm on the fence about it personally. But you can't, because Debug is a reserved keyword....

-
I really recommend an Enum for your custom error numbers. It's much easier to maintain if they're all in one place.

Err.Raise vbObjectError + 1098, "LogManager.Register", "There is already a logger registered with name '" & logger.Name & "'."
'.......
Err.Raise vbObjectError + 1099, "LogManager.Log", "There is no registered logger named '" & loggerName & "'."


These become much simpler with an Enum that looks something like this.

Public Enum LogMangerError
    SomeError = vbObjectError + 1098
    SomeOtherError 
End Enum

'......

Err.Raise SomeError, "LogManager.Register", "There is already a logger registered with name '" & logger.Name & "'."


-
This is an absolute pleasure to read. Great use of white space.

Select Case level

    Case LogLevel.DebugLevel
        FormatLogLevel = "DEBUG"

    Case LogLevel.ErrorLevel
        FormatLogLevel = "ERROR"

    Case LogLevel.FatalLevel
        FormatLogLevel = "FATAL"

    Case LogLevel.InfoLevel
        FormatLogLevel = "INFO"

    Case LogLevel.TraceLevel
        FormatLogLevel = "TRACE"

    Case LogLevel.WarnLevel
        FormatLogLevel = "WARNING"

End Select


Maybe I'm tired, or maybe it's just good. I can't tell for sure at the moment, but I think you have some pretty solid code here. Good naming and formatting, as well as a very nice abstraction and use of interfaces.

Code Snippets

Public Enum LogLevel
  TraceLevel = 0
  DebugLevel
  '.....
Err.Raise vbObjectError + 1098, "LogManager.Register", "There is already a logger registered with name '" & logger.Name & "'."
'.......
Err.Raise vbObjectError + 1099, "LogManager.Log", "There is no registered logger named '" & loggerName & "'."
Public Enum LogMangerError
    SomeError = vbObjectError + 1098
    SomeOtherError 
End Enum

'......

Err.Raise SomeError, "LogManager.Register", "There is already a logger registered with name '" & logger.Name & "'."
Select Case level

    Case LogLevel.DebugLevel
        FormatLogLevel = "DEBUG"

    Case LogLevel.ErrorLevel
        FormatLogLevel = "ERROR"

    Case LogLevel.FatalLevel
        FormatLogLevel = "FATAL"

    Case LogLevel.InfoLevel
        FormatLogLevel = "INFO"

    Case LogLevel.TraceLevel
        FormatLogLevel = "TRACE"

    Case LogLevel.WarnLevel
        FormatLogLevel = "WARNING"

End Select

Context

StackExchange Code Review Q#64109, answer score: 8

Revisions (0)

No revisions yet.