2010-05-07 5 views
4

Je vous connecter à besoin:Connectez-vous à rouler fichier CSV avec Enterprise Library

  1. fichier roulant, pour éviter 1 grand fichier journal.
  2. Format CSV pour faciliter la recherche.

Je peux voir le EntLib (5.0) a le Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener pour se connecter à un fichier journal roulant. Pour que les entrées du journal ressemblent à une ligne CSV, je peux modifier le Formatters.TextFormatter.Template pour mettre des guillemets autour des valeurs, et aussi changer le pied de page et l'en-tête de l'auditeur pour qu'ils ne soient pas sortis.

Dans des circonstances normales, cela me donnerait un fichier CSV bien formé. Toutefois, si une valeur de jeton dans le Template contient un guillemet double, cela ne sera pas échappé. Par conséquent, le fichier journal devient un fichier CSV non valide.

Y at-il un moyen de résoudre ce problème?

Existe-t-il des solutions alternatives à ce problème?

+0

RollingFlatFileTraceListener ajoute-tête et pieds de page indésirables comme « ------------------------ ---------------- ". Comment avez-vous éliminé l'en-tête et le pied de page? –

Répondre

2

Voir http://msdn.microsoft.com/en-us/library/ff650608.aspx. Il n'est pas si difficile d'ajouter un formateur personnalisé, j'ai ajouté un CSVTextFormattter pour prendre soin seulement de masser le message et les propriétés étendues, ce qui fonctionne pour moi. Notez que j'utilise le TextFormatter de bult-in pour faire tous les gros travaux.

Exemple de configuration:

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

La classe est quelque chose comme ceci:

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

Je ne pense pas qu'il existe une solution «miracle» pour écrire votre propre formateur.

Vous devrez vous soucier des guillemets et des nouvelles lignes. N'importe lequel d'entre eux va jeter le formatage. Je pense que les seules propriétés pour lesquelles vous devez vous inquiéter de ces caractères sont le message, le titre et toutes les propriétés étendues que vous utilisez. Je recommande d'écrire un wrapper ou une façade mince autour de la méthode Write où vous échappez à ces propriétés pour vous assurer que vous avez un fichier correctement formaté. c'est-à-dire échapper les guillemets doubles et remplacer les nouvelles lignes par un espace.

+0

Ouais, j'ai finalement dû écrire une enveloppe autour de l'enregistreur (déjà une façade !!!), puisque je ne peux pas écrire mon propre formateur capable de supporter tous ces jetons dans le template et l'élément de configuration, ce que je trouve très utile dans Textformatter. Je vais accepter cela comme une réponse pour l'instant. Merci. – Tinminator

0

Je traduis le code C# et fixe un bogue dans qualificateur échapper. J'ai aussi ajouté un point-virgule comme séparateur, depuis Excel par défaut suppose CSVs séparées par des points-virgules ..

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

Ne hésitez pas à utiliser et d'améliorer. C'est une solution très simple, peut-être serait-il plus agréable de dériver le nouveau Formatter de TextFormatter au lieu de l'encapsuler, mais cela fonctionne très bien pour moi ('works' == Excel l'ouvre sans aucun problème connu).

0

Le code suivant fonctionne bien pour moi:

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