[EntLib]微软企业库6 日志 通过CustomTraceListener实现自动分类存放日志文件

对比log4net,EntLib 的可视化配置以及完善的文档实在是占了挺大的优势,但两者在文件日志方面都存在着相同的问题,就是不能根据Category(log4net里面是logger name)自动分类存放(所有的日志都记在一个日志文件里,当查看日志时会相对比较麻烦),如果想实现分类存放,那就需要在config文件里写上一大堆的配置,作为懒人,这肯定是不能接受的,当然是写的越少越好:P

在code之前先描述下设计思路:
首先说下前提,EntLib 的Logging Application Block支持SpecialSources,它包含三部分AllEvents、LoggingErrorsAndWarnings以及Unprocessed,这里面的Unprocessed对应的就是Unprocessed Category,所有未定义过的Categories都会被分配到此部分,如果Unprocessed没设置TraceListener,那就会产生一个异常,由LoggingErrorsAndWarnings来处理这个异常
其次是思路,说白了很简单,就是根据Category动态创建新的RollingFlatFileTraceListener,然后在记录日志时,由Category对应的RollingFlatFileTraceListener来处理相应的日志,如果无法识别的日志,则由默认的RollingFlatFileTraceListener来处理

PS:微软的企业库配置小软件无法通过加载dll识别该类,估计是因为AutoCategoryRollingFlatFileTraceListenerData直接继承自TraceListenerData,而不是继承自CustomTraceListenerData,导致小工具无法识别,不过可以将配置手写到config文件中,此时是能够正确加载的

以下是具体code

    using System.IO;
    using System.Collections.Concurrent;
    using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
    using System.Diagnostics;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
    using Microsoft.Practices.EnterpriseLibrary.Common.Utility;
    using System.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Design;

    #region AutoCategoryRollingFlatFileTraceListener
    /// <summary>
    /// 根据Category自动分类保存日志
    /// </summary>
    [ConfigurationElementType(typeof(AutoCategoryRollingFlatFileTraceListenerData))]
    public class AutoCategoryRollingFlatFileTraceListener : CustomTraceListener
    {
        private ConcurrentDictionary<string, RollingFlatFileTraceListener> _dic = new ConcurrentDictionary<string, RollingFlatFileTraceListener>();
        private RollingFlatFileTraceListener _defaultListener;

        private readonly string _directory;
        private readonly string _extension;
        private readonly string _header;
        private readonly string _footer;
        private readonly int _rollSizeKB;
        private readonly string _timeStampPattern;
        private readonly RollFileExistsBehavior _rollFileExistsBehavior;
        private readonly RollInterval _rollInterval;
        private readonly int _maxArchivedFiles;

        /// <summary>
        /// Initializes a new instance of the <see cref="RollingFlatFileTraceListener"/> class.
        /// </summary>
        /// <param name="fileName">The filename where the entries will be logged.</param>
        /// <param name="header">The header to add before logging an entry.</param>
        /// <param name="footer">The footer to add after logging an entry.</param>
        /// <param name="formatter">The formatter.</param>
        /// <param name="rollSizeKB">The maxium file size (KB) before rolling.</param>
        /// <param name="timeStampPattern">The date format that will be appended to the new roll file.</param>
        /// <param name="rollFileExistsBehavior">Expected behavior that will be used when the roll file has to be created.</param>
        /// <param name="rollInterval">The time interval that makes the file rolles.</param>
        /// <param name="maxArchivedFiles">The maximum number of archived files to keep.</param>
        public AutoCategoryRollingFlatFileTraceListener(string fileName,
                                            string header = RollingFlatFileTraceListener.DefaultSeparator,
                                            string footer = RollingFlatFileTraceListener.DefaultSeparator,
                                            ILogFormatter formatter = null,
                                            int rollSizeKB = 0,
                                            string timeStampPattern = "yyyy-MM-dd",
                                            RollFileExistsBehavior rollFileExistsBehavior = RollFileExistsBehavior.Overwrite,
                                            RollInterval rollInterval = RollInterval.None,
                                            int maxArchivedFiles = 0)
        {
            Guard.ArgumentNotNullOrEmpty(fileName, "fileName");
            this._directory = Path.GetDirectoryName(fileName);
            this._extension = Path.GetExtension(fileName);
            this._header = header;
            this._footer = footer;
            this._rollSizeKB = rollSizeKB;
            this._timeStampPattern = timeStampPattern;
            this._rollFileExistsBehavior = rollFileExistsBehavior;
            this._rollInterval = rollInterval;
            this._maxArchivedFiles = maxArchivedFiles;

            this.Formatter = formatter;

            this._defaultListener = new RollingFlatFileTraceListener(fileName, this._header, this._footer,
                            this.Formatter, this._rollSizeKB, this._timeStampPattern,
                            this._rollFileExistsBehavior, this._rollInterval, this._maxArchivedFiles);
        }
        public override void Write(string message)
        {
            this._defaultListener.Write(message);
        }

        public override void WriteLine(string message)
        {
            this._defaultListener.WriteLine(message);
        }

        public override void Flush()
        {
            this._defaultListener.Flush();
        }

        /// <summary>
        /// Delivers the trace data to the underlying file.
        /// </summary>
        /// <param name="eventCache">The context information provided by <see cref="System.Diagnostics"/>.</param>
        /// <param name="source">The name of the trace source that delivered the trace data.</param>
        /// <param name="eventType">The type of event.</param>
        /// <param name="id">The id of the event.</param>
        /// <param name="data">The data to trace.</param>
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            if (this.Filter == null || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
            {
                var listener = this._defaultListener;
                if (data is LogEntry)
                {
                    ((LogEntry)data).Categories.ForEach((category) =>
                    {
                        var tmpListener = this.GetTraceListener(category);
                        tmpListener.TraceData(eventCache, source, eventType, id, data);
                        tmpListener.Flush();
                    });
                    return;
                }

                listener.TraceData(eventCache, source, eventType, id, data);
                listener.Flush();
            }
        }

        private RollingFlatFileTraceListener GetTraceListener(string category)
        {
            RollingFlatFileTraceListener listener = this._defaultListener;
            if (!string.IsNullOrWhiteSpace(category))
            {
                category = category.ToLower().Trim();
                if (this._dic.ContainsKey(category))
                {
                    return this._dic[category];
                }
                else
                {
                    try
                    {
                        string fileName = Path.Combine(this._directory, category, string.Format("{0}{1}", category, this._extension));
                        var tmpListener = new RollingFlatFileTraceListener(fileName, this._header, this._footer,
                            this.Formatter, this._rollSizeKB, this._timeStampPattern,
                            this._rollFileExistsBehavior, this._rollInterval, this._maxArchivedFiles);
                        this._dic.TryAdd(category, tmpListener);
                        listener = tmpListener;
                    }
                    catch
                    {
                    }
                }
            }
            return listener;
        }
    }
    #endregion

    #region AutoCategoryRollingFlatFileTraceListenerData
    public class AutoCategoryRollingFlatFileTraceListenerData : TraceListenerData
    {
        private const string FileNamePropertyName = "fileName";
        private const string footerProperty = "footer";
        private const string formatterNameProperty = "formatter";
        private const string headerProperty = "header";
        private const string RollFileExistsBehaviorPropertyName = "rollFileExistsBehavior";
        private const string RollIntervalPropertyName = "rollInterval";
        private const string RollSizeKBPropertyName = "rollSizeKB";
        private const string TimeStampPatternPropertyName = "timeStampPattern";
        private const string MaxArchivedFilesPropertyName = "maxArchivedFiles";

        public AutoCategoryRollingFlatFileTraceListenerData()
            : base(typeof(AutoCategoryRollingFlatFileTraceListener))
        {
            ListenerDataType = typeof(AutoCategoryRollingFlatFileTraceListenerData);
        }

        public AutoCategoryRollingFlatFileTraceListenerData(string name,
                                                string fileName,
                                                string header,
                                                string footer,
                                                int rollSizeKB,
                                                string timeStampPattern,
                                                RollFileExistsBehavior rollFileExistsBehavior,
                                                RollInterval rollInterval,
                                                TraceOptions traceOutputOptions,
                                                string formatter)
            : base(name, typeof(AutoCategoryRollingFlatFileTraceListener), traceOutputOptions)
        {
            FileName = fileName;
            Header = header;
            Footer = footer;
            RollSizeKB = rollSizeKB;
            RollFileExistsBehavior = rollFileExistsBehavior;
            RollInterval = rollInterval;
            TimeStampPattern = timeStampPattern;
            Formatter = formatter;
        }

        public AutoCategoryRollingFlatFileTraceListenerData(string name,
                                                string fileName,
                                                string header,
                                                string footer,
                                                int rollSizeKB,
                                                string timeStampPattern,
                                                RollFileExistsBehavior rollFileExistsBehavior,
                                                RollInterval rollInterval,
                                                TraceOptions traceOutputOptions,
                                                string formatter,
                                                SourceLevels filter)
            : base(name, typeof(AutoCategoryRollingFlatFileTraceListener), traceOutputOptions, filter)
        {
            FileName = fileName;
            Header = header;
            Footer = footer;
            RollSizeKB = rollSizeKB;
            RollFileExistsBehavior = rollFileExistsBehavior;
            RollInterval = rollInterval;
            TimeStampPattern = timeStampPattern;
            Formatter = formatter;
        }

        /// <summary>
        /// FileName
        /// </summary>
        [ConfigurationProperty(FileNamePropertyName, DefaultValue = "auto.log")]
        [System.ComponentModel.Editor(CommonDesignTime.EditorTypes.FilteredFilePath, CommonDesignTime.EditorTypes.UITypeEditor)]
        public string FileName
        {
            get { return (string)this[FileNamePropertyName]; }
            set { this[FileNamePropertyName] = value; }
        }

        /// <summary>
        /// Gets and sets the footer.
        /// </summary>
        [ConfigurationProperty(footerProperty, IsRequired = false, DefaultValue = "----------------------------------------")]
        public string Footer
        {
            get { return (string)base[footerProperty]; }
            set { base[footerProperty] = value; }
        }

        /// <summary>
        /// Gets and sets the formatter name.
        /// </summary>
        [ConfigurationProperty(formatterNameProperty, IsRequired = false)]
        [Reference(typeof(NameTypeConfigurationElementCollection<FormatterData, CustomFormatterData>), typeof(FormatterData))]
        public string Formatter
        {
            get { return (string)base[formatterNameProperty]; }
            set { base[formatterNameProperty] = value; }
        }

        /// <summary>
        /// Gets and sets the header.
        /// </summary>
        [ConfigurationProperty(headerProperty, IsRequired = false, DefaultValue = "----------------------------------------")]
        public string Header
        {
            get { return (string)base[headerProperty]; }
            set { base[headerProperty] = value; }
        }

        /// <summary>
        /// Exists Behavior
        /// </summary>
        [ConfigurationProperty(RollFileExistsBehaviorPropertyName)]
        public RollFileExistsBehavior RollFileExistsBehavior
        {
            get { return (RollFileExistsBehavior)this[RollFileExistsBehaviorPropertyName]; }
            set { this[RollFileExistsBehaviorPropertyName] = value; }
        }

        /// <summary>
        /// Roll Intervall
        /// </summary>
        [ConfigurationProperty(RollIntervalPropertyName)]
        public RollInterval RollInterval
        {
            get { return (RollInterval)this[RollIntervalPropertyName]; }
            set { this[RollIntervalPropertyName] = value; }
        }

        /// <summary>
        /// Roll Size KB 
        /// </summary>
        [ConfigurationProperty(RollSizeKBPropertyName)]
        public int RollSizeKB
        {
            get { return (int)this[RollSizeKBPropertyName]; }
            set { this[RollSizeKBPropertyName] = value; }
        }

        /// <summary>
        /// Time stamp
        /// </summary>
        [ConfigurationProperty(TimeStampPatternPropertyName, DefaultValue = "yyyy-MM-dd")]
        public string TimeStampPattern
        {
            get { return (string)this[TimeStampPatternPropertyName]; }
            set { this[TimeStampPatternPropertyName] = value; }
        }

        /// <summary>
        /// Max rolled files
        /// </summary>
        [ConfigurationProperty(MaxArchivedFilesPropertyName)]
        public int MaxArchivedFiles
        {
            get { return (int)this[MaxArchivedFilesPropertyName]; }
            set { this[MaxArchivedFilesPropertyName] = value; }
        }

        protected override TraceListener CoreBuildTraceListener(LoggingSettings settings)
        {
            var formatter = this.BuildFormatterSafe(settings, this.Formatter);

            return new AutoCategoryRollingFlatFileTraceListener(
                        this.FileName,
                        this.Header,
                        this.Footer,
                        formatter,
                        this.RollSizeKB,
                        this.TimeStampPattern,
                        this.RollFileExistsBehavior,
                        this.RollInterval,
                        this.MaxArchivedFiles);
        }
    }
    #endregion

AutoCategoryRollingFlatFileTraceListenerData就是抄了一遍RollingFlatFileTraceListenerData,然后改了下Type部分,这些细节就不要在意了

注册代码如下

1、代码注册

Logger.Writer.Configure(config =>
            {
                AutoCategoryRollingFlatFileTraceListener acListener = new AutoCategoryRollingFlatFileTraceListener("../applog/auto/auto.log", rollFileExistsBehavior: RollFileExistsBehavior.Increment,
                    rollInterval: RollInterval.Minute, timeStampPattern: "yyyyMMddHHmm");
                config.SpecialSources.Unprocessed.AddAsynchronousTraceListener(acListener);//异步执行
            });

2、配置方式

<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
    <listeners>
      <add name="AutoCategoryRollingFlatFileTraceListener" type="WebAPIDemo.AutoCategoryRollingFlatFileTraceListener, WebAPIDemo"
        listenerDataType="WebAPIDemo.AutoCategoryRollingFlatFileTraceListenerData, WebAPIDemo"
        fileName="../applog/auto/auto.log" formatter="Text Formatter"
        rollFileExistsBehavior="Increment" rollInterval="Minute" rollSizeKB="1"
        timeStampPattern="yyyyMMddHHmm" asynchronous="true" asynchronousDisposeTimeout="infinite" />
    </listeners>
    <formatters>
      <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging"
        template="Timestamp: {timestamp(local)}{newline}
Message: {message}{newline}
Category: {category}{newline}
Priority: {priority}{newline}
EventId: {eventid}{newline}
Severity: {severity}{newline}
Title:{title}{newline}
Extended Properties: {dictionary({key} - {value}{newline})}"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category">
        <listeners>
          <add name="AutoCategoryRollingFlatFileTraceListener" />
        </listeners>
      </notProcessed>
      <errors switchValue="All" name="Logging Errors & Warnings" />
    </specialSources>
</loggingConfiguration>

实际使用的与正常日志无区别

Logger.Write(string.Format("Get Single Product By ID:{0}", id), "Product");//Product在config中有配置
Logger.Write(string.Format("Get Single Product By ID:{0}", id), "ProductU");//ProductU在config中未配置
Logger.Write(string.Format("Get Single Product By ID:{0}", id), new List<string>() { "ProductX", "ProductXX" });//ProductX、ProductXX在config中未配置

 
 

你可能感兴趣的:(C#,logging,library,enterprise,微软企业库,EntLib)