Log in to CSV file with Enterprise Library

I need to log in:

  • Move the file to avoid 1 large log file.
  • CSV format for easy searching.

I see that EntLib (5.0) has Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener to enter the sliding log file.

To make the log entries look like a CSV string, I can change the Formatters.TextFormatter.Template to place a double quote around the values, as well as change the listener footer and header to nothing, so they will not be displayed.

Under normal circumstances, this will give me a well-formed CSV file. However, if the token value in Template contains a double quote, this will not be escaped. Therefore, the log file becomes an invalid CSV file.

Is there any way to resolve this?

Are there any alternative solutions to this problem?

+4
source share
4 answers

See http://msdn.microsoft.com/en-us/library/ff650608.aspx . Turns out adding custom formatting isn't that hard, I added a CSVTextFormattter to just take care of massaging the message and advanced properties, which works for me. Notice that I'm using bult-in TextFormatter to do all the heavy lifting.

Configuration Example:

 <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 looks 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 
+2
source

I don’t think there is any solution to the “silver bullet” without writing my own formatter.

You will need to worry about double quotes and newlines. Any of them will reset the formatting.

I think the only properties you should worry about with these characters are Message, Title, and any ExtendedProperties that you use. I recommend writing a thin wrapper or façade around the Write method, where you avoid these properties to make sure you have a properly formatted file. those. avoid any double quotes and replace newlines with a space.

0
source

I translated the code into C # and fixed the error when the qualifier retreated. I also added a semicolon as a separator, since Excel accepts CSV semicolon separated by default.

 public class CsvLogFormatter: ILogFormatter { private TextFormatter _formatter; public CsvLogFormatter(string template) { // property Template allows 'set', but formatter still uses original template.. Must recreate formatter when template changes! _formatter = new TextFormatter(template); } public string Template { get { return _formatter.Template; } } public string Format(LogEntry log) { try { var logEntry = (LogEntry)log.Clone(); logEntry.Message = NormalizeToCsvToken(logEntry.Message); var normalizableKeys = logEntry.ExtendedProperties.Where(l => l.Value == null || l.Value is string).ToList(); foreach (var pair in normalizableKeys) { logEntry.ExtendedProperties[pair.Key] = NormalizeToCsvToken((string)pair.Value); } return _formatter.Format(logEntry); } catch { // this redundant catch is useful for debugging exceptions in this methods (EnterpriseLibrary swallows exceptions :-/) throw; } } private static string NormalizeToCsvToken(string text) { var wrapLogText = false; const string qualifier = "\""; if (text.Contains(qualifier)) { text = text.Replace(qualifier, qualifier + qualifier); wrapLogText = true; } var delimiters = new[] { ";", ",", "\n", "\r", "\r\n" }; foreach (var delimiter in delimiters) { if (text.Contains(delimiter)) wrapLogText = true; } if (wrapLogText) text = string.Format("\"{0}\"", text); return text; } } 

Feel free to use and improve. This is a very simple solution, it might be nice to get a new Formatter from TextFormatter instead of wrapping it, but it works fine for me ("works" == Excel opens it without any known problems).

0
source

The following code works great for me:

 [ConfigurationElementType(typeof(CustomFormatterData))] public class CsvLogFormatter : ILogFormatter { private TextFormatter _formatter; private string template = "template"; public CsvLogFormatter(NameValueCollection collection) { // property Template allows 'set', but formatter still uses original template.. Must recreate formatter when template changes! _formatter = new TextFormatter(collection[template]); } public string Template { get { return _formatter.Template; } } public string Format(LogEntry log) { try { var logEntry = (LogEntry)log.Clone(); logEntry.Message = NormalizeToCsvToken(logEntry.Message); var normalizableKeys = logEntry.ExtendedProperties.Where(l => l.Value == null || l.Value is string).ToList(); foreach (var pair in normalizableKeys) { logEntry.ExtendedProperties[pair.Key] = NormalizeToCsvToken((string)pair.Value); } return _formatter.Format(logEntry); } catch { // this redundant catch is useful for debugging exceptions in this methods (EnterpriseLibrary swallows exceptions :-/) throw; } } private static string NormalizeToCsvToken(string text) { var wrapLogText = false; const string qualifier = "\""; if (text.Contains(qualifier)) { text = text.Replace(qualifier, qualifier + qualifier); wrapLogText = true; } var delimiters = new[] { ";", ",", "\n", "\r", "\r\n" }; foreach (var delimiter in delimiters) { if (text.Contains(delimiter)) wrapLogText = true; } if (wrapLogText) text = string.Format("\"{0}\"", text); return text; } } 
0
source

Source: https://habr.com/ru/post/1309065/


All Articles