2010-05-07 49 views
4

我需要登录到:日志滚动CSV文件与企业库

  1. 滚动文件,以避免1个大的日志文件。
  2. CSV格式,便于查找。

我可以看到EntLib(5.0)有Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener登录到滚动日志文件。

为了使日志条目看起来像一个CSV行,我可以更改Formatters.TextFormatter.Template以将值放在双引号内,并且也将Listener的页脚和标题更改为无,因此它们不会输出。

在正常情况下,这会给我一个很好形成的CSV文件。但是,如果Template中的令牌值包含双引号,则不会转义。因此日志文件成为无效的CSV文件。

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

是否有这个问题的任何其他解决方案?

+0

RollingFlatFileTraceListener增加了不必要的页眉和页脚,如“------------------------ ----------------”。你是如何消除页眉和页脚的? – 2015-09-03 15:58:26

回答

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 
0

我不认为有任何的“银弹”解决短期编写自己的格式化。

你需要担心双引号和新的生产线。其中任何一种都会导致格式化。

我认为你不必担心这些字符是消息,标题的唯一属性,并且您使用任何ExtendedProperties。我建议在写入方法的周围写一个薄的包装或外观,以避免这些属性,以确保您的文件格式正确。即转义任何双引号并用空格替换新行。

+0

是的,我最终不得不围绕记录器写一个包装(已经是一个门面了!),因为我无法编写自己的格式化程序,它可以在模板和配置元素中支持所有这些标记,我发现它对Textformatter非常有用。 我现在接受这个答案。 谢谢。 – Tinminator 2010-05-10 23:36:35

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在没有任何已知问题的情况下打开它。

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