使用Enterprise Library登录滚动CSV文件

时间:2010-05-07 01:39:48

标签: .net logging enterprise-library

我需要登录:

  1. 滚动文件,以避免1个大日志文件。
  2. CSV格式,以便于查找。
  3. 我可以看到EntLib(5.0)有Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener来登录滚动日志文件。

    为了使日志条目看起来像一个CSV行,我可以更改Formatters.TextFormatter.Template以在值周围加上双引号,并且还将监听器的页脚和标题更改为空,这样就不会输出它们。

    在正常情况下,这会给我一个格式良好的CSV文件。但是,如果Template中的标记值包含双引号,则不会对其进行转义。因此,日志文件将成为无效的CSV文件。

    有什么方法可以解决这个问题吗?

    这个问题还有其他解决办法吗?

4 个答案:

答案 0 :(得分:2)

http://msdn.microsoft.com/en-us/library/ff650608.aspx。 结果添加一个自定义格式化器并不难,我添加了一个CSVTextFormattter来处理按摩消息和扩展属性,这对我有用。 请注意,我使用bult-in TextFormatter来完成所有繁重的工作。

示例配置:

<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>

课程是这样的:

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

答案 1 :(得分:0)

我认为除了编写自己的格式化程序之外,没有任何“银弹”解决方案。

您需要担心双引号和新行。其中任何一个都会抛弃格式化。

我认为您必须担心这些字符的唯一属性是Message,Title和您正在使用的任何ExtendedProperties。我建议在Write方法周围编写一个瘦的包装器或外观,在那里您可以转义这些属性,以确保您拥有格式正确的文件。即转义任何双引号并用空格替换新行。

答案 2 :(得分:0)

我已将代码翻译成c#并修复了限定符转义中的错误。我还添加了分号作为分隔符,因为Excel默认采用分号分隔的CSV格式。

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;
    }
}

随意使用和增强。这是一个非常简单的解决方案,从TextFormatter派生新的Formatter而不是包装它可能会更好,但这对我来说很好('works'== Excel打开它没有任何已知问题)。

答案 3 :(得分:0)

以下代码适用于我:

[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;
    }
}