.net 使用企业库登录到滚动CSV文件

.net 使用企业库登录到滚动CSV文件,.net,logging,enterprise-library,.net,Logging,Enterprise Library,我需要登录到: 滚动文件,以避免1个大日志文件 CSV格式,便于查找 我可以看到EntLib(5.0)有Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener来记录到滚动日志文件 为了使日志条目看起来像CSV行,我可以更改Formatters.TextFormatter.Template以在值周围加上双引号,还可以将侦听器的页脚和页眉更改为“无”,这样它们就不会被输出 在正

我需要登录到:

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

    为了使日志条目看起来像CSV行,我可以更改
    Formatters.TextFormatter.Template
    以在值周围加上双引号,还可以将侦听器的页脚和页眉更改为“无”,这样它们就不会被输出

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

    有没有办法解决这个问题


    这个问题有其他解决方案吗?

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

    您需要担心双引号和新行。任何一个都会丢掉格式

    我认为您需要担心这些字符的唯一属性是消息、标题和您正在使用的任何ExtendedProperties。我建议围绕Write方法编写一个薄包装或外观,在其中转义这些属性以确保文件格式正确。i、 e.转义任何双引号,并用空格替换新行。

    请参阅。 事实证明,添加自定义格式化程序并不是那么难,我添加了一个CSVTextFormatter,只负责消息和扩展属性的处理,这对我来说很有用。 请注意,我使用TextFormatter中的bult来完成所有繁重的工作

    示例配置:

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

    我已将代码翻译为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打开它时没有任何已知问题)。

    以下代码对我来说很好:

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

    是的,我最终不得不在Logger(已经是facade!!!)周围编写一个包装器,因为我无法编写自己的格式化程序来支持模板和配置元素中的所有标记,我发现Textformatter非常有用。我现在接受这个答案。谢谢。RollingFlatFileTraceListener会添加不需要的页眉和页脚,如“------------------------------------------------”。您是如何消除页眉和页脚的?
    [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;
        }
    }