See http://msdn.microsoft.com/en-us/library/ff650608.aspx.
Turn out adding a custom formatter is not that hard, I added a CSVTextFormattter to only take care of massaging the message and extended properties, which works for me.
Notice I use the bult-in TextFormatter to do all the heavy lifting.
Sample Config:
<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
...
<formatters>
<add type="<your namespace>.CSVTextFormatter, <your dll>"
template="{timestamp(local)},{severity},{category},{message},{property(ActivityId)},{eventid},{win32ThreadId},{threadName},{dictionary({key} - {value}{newline})}"
name="CSV Text Formatter" />
</formatters>...
</loggingConfiguration>
The class is something like this:
Public Class CSVTextFormatter
Implements ILogFormatter
Private Const csTemplateAttributeName As String = "template"
Private moTextFormatter As TextFormatter
Private Property TextFormatter() As TextFormatter
Get
Return moTextFormatter
End Get
Set(ByVal value As TextFormatter)
moTextFormatter = value
End Set
End Property
Private moConfigData As System.Collections.Specialized.NameValueCollection
Private Property ConfigData() As System.Collections.Specialized.NameValueCollection
Get
Return moConfigData
End Get
Set(ByVal value As System.Collections.Specialized.NameValueCollection)
moConfigData = value
If moConfigData.AllKeys.Contains(csTemplateAttributeName) Then
TextFormatter = New TextFormatter(moConfigData(csTemplateAttributeName))
Else
TextFormatter = New TextFormatter()
End If
End Set
End Property
Public Sub New()
TextFormatter = New TextFormatter()
End Sub
Public Sub New(ByVal configData As System.Collections.Specialized.NameValueCollection)
Me.ConfigData = configData
End Sub
Public Function Format(ByVal log As Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry) As String Implements Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.ILogFormatter.Format
Dim oLog As Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry = log.Clone()
With oLog
.Message = NormalizeToCSVValue(.Message)
For Each sKey In .ExtendedProperties.Keys
Dim sValue As String = TryCast(.ExtendedProperties(sKey), String)
If Not String.IsNullOrEmpty(sValue) Then
.ExtendedProperties(sKey) = NormalizeToCSVValue(sValue)
End If
Next
End With
Return TextFormatter.Format(oLog)
End Function
Private Shared Function NormalizeToCSVValue(ByVal text As String) As String
Dim bWrapLogText = False
Dim oQualifiers = New String() {""""}
For Each sQualifier In oQualifiers
If text.Contains(sQualifier) Then
text = text.Replace(sQualifier, String.Format("""{0}""", sQualifier))
bWrapLogText = True
End If
Next
Dim oDelimiters = New String() {",", vbLf, vbCr, vbCrLf}
If text.Contains(oDelimiters) Then
bWrapLogText = True
End If
If bWrapLogText Then
text = String.Format("""{0}""", text)
End If
Return text
End Function
End Class