Войдите в непрерывный файл CSV с Enterprise Library - PullRequest
4 голосов
/ 07 мая 2010

Мне нужно войти в систему:

  1. Скользящий файл, чтобы избежать 1 большого файла журнала.
  2. CSV формат для более удобного поиска.

Я вижу, что EntLib (5.0) имеет Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener для входа в файл скользящего журнала.

Чтобы записи в журнале выглядели как строка CSV, я могу изменить Formatters.TextFormatter.Template, чтобы заключить двойные кавычки в значения, а также изменить нижний колонтитул и верхний колонтитул слушателя на ничего, чтобы они не выводились.

При нормальных обстоятельствах это дало бы мне правильно сформированный файл CSV. Однако, если значение токена в Template содержит двойную кавычку, оно не будет экранировано. Следовательно, файл журнала становится недействительным файлом CSV.

Есть ли способ решить эту проблему?

Есть ли альтернативные решения этой проблемы?

Ответы [ 4 ]

2 голосов
/ 19 июля 2010

См. http://msdn.microsoft.com/en-us/library/ff650608.aspx. Оказалось, что добавить собственный форматер не так сложно, я добавил CSVTextFormattter, чтобы заботиться только о массе сообщения и расширенных свойствах, что мне подходит. Обратите внимание, что я использую встроенный 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 голосов
/ 01 августа 2014

У меня нормально работает следующий код:

[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 голосов
/ 16 февраля 2011

Я перевел код на 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;
    }
}

Не стесняйтесь использовать и улучшать. Это очень простое решение, может быть, было бы лучше получить новый Formatter из TextFormatter, а не оборачивать его, но это прекрасно работает для меня («работает» == Excel открывает его без каких-либо известных проблем).

0 голосов
/ 10 мая 2010

Я не думаю, что есть какое-то решение "серебряной пули", кроме написания вашего собственного форматера.

Вам нужно беспокоиться о двойных кавычках и новых строках. Любой из них скинет форматирование.

Я думаю, что единственными свойствами, за которые вам нужно беспокоиться об этих символах, являются Сообщение, Заголовок и любые Расширенные свойства, которые вы используете. Я рекомендую написать тонкую оболочку или фасад вокруг метода Write, где вы экранируете эти свойства, чтобы убедиться, что у вас есть правильно отформатированный файл. т.е. избегать двойных кавычек и заменять новые строки пробелом.

...