【问题标题】:C# equivalent to Python's logging libraryC#相当于Python的日志库
【发布时间】:2010-12-17 20:49:21
【问题描述】:

使用 python,我可以使用 logging 库,非常方便。

什么是 C# 的日志库?

【问题讨论】:

    标签: c# python logging


    【解决方案1】:

    您可以使用log4netTraceListener

    【讨论】:

    • 是的,我也喜欢它,但问题是与问题中链接的库等效。绝对 log4net 与此非常相似,我也会在答案中 highlighted :D +1 我也是
    【解决方案2】:

    您还应该考虑使用 NLog (http://nlog-project.org),因为 log4net 自 2006 年 4 月以来就没有更新过。这是关于 .NET 日志记录的另一个讨论 - log4net vs. Nlog

    【讨论】:

      【解决方案3】:

      log4net 并在EntLib 中记录应用程序块,除了我听说 log4net 中有一些并发问题。

      【讨论】:

      • Enterprise Library 是非常糟糕的库,恕我直言。 log4net 在我所有的项目中都非常好用。
      【解决方案4】:

      我发现 log4net 非常方便:http://logging.apache.org/log4net/index.html

      或者,如果您想记录到事件日志,请使用跟踪侦听器。很多例子可以通过http://www.google.se/search?sourceid=chrome&ie=UTF-8&q=c%23+tracelistener找到

      【讨论】:

        【解决方案5】:

        以下是来自 Python 的 logging 模块的许多概念和特性的 C# 近似:

        public class Logger {
            private static readonly System.Collections.Generic.Dictionary<string, Logger> Loggers = new System.Collections.Generic.Dictionary<string, Logger>();
        
            private readonly string _name;
            private LoggingLevel _level;
            private readonly System.Collections.Generic.List<Filter> _filters;
            private readonly System.Collections.Generic.List<Handler> _handlers;
        
            public static Logger GetLogger(string name = "root") {
                Logger value;
                if (Logger.Loggers.TryGetValue(name, out value)) {
                    return value;
                }
                value = new Logger(name);
                Logger.Loggers.Add(name, value);
                return value;
            }
        
            private Logger(string name) {
                this._name = name;
                this._level = LoggingLevel.Warning;
                this._filters = new System.Collections.Generic.List<Filter>();
                this._handlers = new System.Collections.Generic.List<Handler>();
            }
        
            public void SetLevel(LoggingLevel level) {
                this._level = level;
            }
        
            public void AddHandler(Handler handler) {
                this._handlers.Add(handler);
            }
        
            public void RemoveHandler(Handler handler) {
                this._handlers.Remove(handler);
            }
        
            public void AddFilter(Filter filter) {
                this._filters.Add(filter);
            }
        
            public void RemoveFilter(Filter filter) {
                this._filters.Remove(filter);
            }
        
            public void Debug(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Debug, message, null, filePath, lineNumber, memberName);
            }
        
            public void Info(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Info, message, null, filePath, lineNumber, memberName);
            }
        
            public void Warning(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Warning, message, null, filePath, lineNumber, memberName);
            }
        
            public void Error(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Error, message, null, filePath, lineNumber, memberName);
            }
        
            public void Critical(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Critical, message, null, filePath, lineNumber, memberName);
            }
        
            public void Exception(Exception error, string message = "", [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                this.Log(LoggingLevel.Error, message, error, filePath, lineNumber, memberName);
            }
        
            public void Log(LoggingLevel level, string message, Exception error = null, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
                if (this.IsEnabledFor(level)) {
                    var record = new LogRecord(this._name, level, filePath, lineNumber, memberName, message, error);
                    if (this._filters.All(x => x.Check(record))) {
                        this._handlers.ForEach(x => x.Process(record));
                    }
                }
            }
        
            public bool IsEnabledFor(LoggingLevel level) {
                return level >= this._level;
            }
        }
        
        public abstract class Handler {
            public static ExceptionSetting RaiseExceptions = ExceptionSetting.Write;
            private static readonly Formatter DefaultFormatter = new Formatter();
        
            private LoggingLevel _level;
            private readonly System.Collections.Generic.List<Filter> _filters;
            private Formatter _formatter;
        
            protected Handler() {
                this._level = LoggingLevel.Warning;
                this._filters = new System.Collections.Generic.List<Filter>();
                this._formatter = null;
            }
        
            public void SetLevel(LoggingLevel level) {
                this._level = level;
            }
        
            public void SetFormatter(Formatter formatter) {
                this._formatter = formatter;
            }
        
            public void AddFilter(Filter filter) {
                this._filters.Add(filter);
            }
        
            public void RemoveFilter(Filter filter) {
                this._filters.Remove(filter);
            }
        
            public void Process(LogRecord record) {
                if (this.IsEnabledFor(record.Level) && this._filters.All(x => x.Check(record))) {
                    lock (this) {
                        this.Emit(record);
                    }
                }
            }
        
            protected abstract void Emit(LogRecord record);
        
            public bool IsEnabledFor(LoggingLevel level) {
                return level >= this._level;
            }
        
            private Formatter SafeFormatter => this._formatter ?? Handler.DefaultFormatter;
        
            protected string Format(LogRecord record) {
                return this.SafeFormatter.Format(record);
            }
        
            protected void HandleError(LogRecord record, Exception error) {
                if (Handler.RaiseExceptions != ExceptionSetting.Pass) {
                    Console.Error.WriteLine("--- Logging error ---");
                    Console.Error.WriteLine(error);
                    Console.Error.WriteLine($"Logged from file {record.FilePath}, line {record.LineNumber}");
                    Console.Error.WriteLine($"Message: {record.Message}");
                    Console.Error.WriteLine($"Created: {this.SafeFormatter.FormatTime(record)}");
                    if (Handler.RaiseExceptions == ExceptionSetting.Throw) {
                        throw error;
                    }
                }
            }
        }
        
        public class StreamHandler : Handler {
            public static readonly string Terminator = Environment.NewLine;
        
            protected System.IO.TextWriter Stream;
        
            public StreamHandler(System.IO.TextWriter stream = null) {
                this.Stream = stream ?? Console.Error;
            }
        
            public void Flush() {
                this.Stream.Flush();
            }
        
            protected override void Emit(LogRecord record) {
                try {
                    var message = this.Format(record);
                    this.Stream.Write(message);
                    this.Stream.Write(StreamHandler.Terminator);
                    this.Flush();
                } catch (Exception error) {
                    this.HandleError(record, error);
                }
            }
        }
        
        public class FileHandler : StreamHandler {
            public static readonly System.Text.Encoding DefaultEncoding = System.Text.Encoding.GetEncoding("iso-8859-1");
        
            protected readonly string FileName;
            private readonly System.IO.FileMode _mode;
            private readonly System.Text.Encoding _codec;
        
            public FileHandler(string fileName, System.IO.FileMode mode, System.Text.Encoding codec = null) : base(
                new System.IO.StreamWriter(new System.IO.FileStream(fileName, mode), codec ?? FileHandler.DefaultEncoding)) {
                this.FileName = fileName;
                this._mode = mode;
                this._codec = codec;
            }
        
            public void Close() {
                this.Flush();
                this.Stream.Close();
            }
        
            protected System.IO.StreamWriter Open() {
                return new System.IO.StreamWriter(new System.IO.FileStream(this.FileName, this._mode), this._codec ?? FileHandler.DefaultEncoding);
            }
        }
        
        public abstract class BaseRotatingHandler : FileHandler {
            protected Func<string, string> Namer;
            protected Action<string, string> Rotator;
        
            protected BaseRotatingHandler(string fileName, System.IO.FileMode mode, System.Text.Encoding codec = null) : base(
                fileName, mode, codec) {
                this.Namer = null;
                this.Rotator = null;
            }
        
            protected override void Emit(LogRecord record) {
                try {
                    if (this.ShouldRollover(record)) {
                        this.DoRollover();
                    }
                    base.Emit(record);
                } catch (Exception error) {
                    this.HandleError(record, error);
                }
        
            }
        
            protected abstract bool ShouldRollover(LogRecord record);
        
            protected abstract void DoRollover();
        
            protected string GetRotationFilename(string defaultName) {
                return this.Namer == null ? defaultName : this.Namer(defaultName);
            }
        
            protected void Rotate(string source, string destination) {
                if (this.Rotator == null) {
                    System.IO.File.Move(source, destination);
                } else if (System.IO.File.Exists(source)) {
                    this.Rotator(source, destination);
                }
            }
        }
        
        public class RotatingFileHandler : BaseRotatingHandler {
            private readonly int _maxBytes;
            private readonly int _backupCount;
        
            public RotatingFileHandler(string fileName, System.IO.FileMode mode, int maxBytes = 0, int backupCount = 0,
                System.Text.Encoding codec = null) : base(fileName, mode, codec) {
                this._maxBytes = maxBytes;
                this._backupCount = backupCount;
            }
        
            protected override bool ShouldRollover(LogRecord record) {
                if (this._maxBytes <= 0) return false;
                var message = $"{this.Format(record)}{StreamHandler.Terminator}";
                return ((System.IO.StreamWriter) this.Stream).BaseStream.Position + message.Length >= this._maxBytes;
            }
        
            protected override void DoRollover() {
                this.Close();
                if (this._backupCount > 0) {
                    string destination;
                    foreach (var i in Enumerable.Range(1, this._backupCount - 1).Reverse()) {
                        var source = this.GetRotationFilename($"{this.FileName}.{i}");
                        destination = this.GetRotationFilename($"{this.FileName}.{i + 1}");
                        if (System.IO.File.Exists(source)) {
                            if (System.IO.File.Exists(destination)) {
                                System.IO.File.Delete(destination);
                            }
                            System.IO.File.Move(source, destination);
                        }
                    }
                    destination = this.GetRotationFilename($"{this.FileName}.1");
                    if (System.IO.File.Exists(destination)) {
                        System.IO.File.Delete(destination);
                    }
                    this.Rotate(this.FileName, destination);
                }
                this.Stream = this.Open();
            }
        }
        
        public class SmtpHandler : Handler {
            private readonly System.Net.DnsEndPoint _mailServer;
            private readonly string _fromAddress;
            private readonly System.Collections.Generic.List<string> _toAddress;
            private readonly string _subject;
            private readonly int _timeout;
        
            public SmtpHandler(System.Net.DnsEndPoint mailServer, string fromAddress, System.Collections.Generic.List<string> toAddress, string subject, int timeout = 1000) {
                this._mailServer = mailServer;
                this._fromAddress = fromAddress;
                this._toAddress = toAddress;
                this._subject = subject;
                this._timeout = timeout;
            }
        
            protected string GetSubject(LogRecord record) {
                return this._subject;
            }
        
            protected override void Emit(LogRecord record) {
                try {
                    var message = new System.Net.Mail.MailMessage { From = new System.Net.Mail.MailAddress(this._fromAddress), Subject = this.GetSubject(record), Body = this.Format(record) };
                    this._toAddress.ForEach(x => message.To.Add(new System.Net.Mail.MailAddress(x)));
                    using (var client = new System.Net.Mail.SmtpClient(this._mailServer.Host, this._mailServer.Port)) {
                        client.Timeout = this._timeout;
                        client.Send(message);
                    }
                } catch (Exception error) {
                    this.HandleError(record, error);
                }
            }
        }
        
        public class Filter {
            private readonly string _name;
        
            public Filter(string name = null) {
                this._name = name;
            }
        
            public bool Check(LogRecord record) {
                return this._name == null || this._name == record.Name;
            }
        }
        
        public class StyleSettings {
            public string DefaultFormat { get; }
            public string TimeToken { get; }
            public string RegexSearch { get; }
            public string PreferredFormat { get; }
        
            public StyleSettings(string defaultFormat, string timeToken, string regexSearch, string preferredFormat) {
                this.DefaultFormat = defaultFormat;
                this.TimeToken = timeToken;
                this.RegexSearch = regexSearch;
                this.PreferredFormat = preferredFormat;
            }
        }
        
        public class Style {
            public static readonly StyleSettings Percent = new StyleSettings("%(Message)", "%(Created)", @"%\((\w+)\)", "%(Level):%(Name):%(Message)");
            public static readonly StyleSettings StringFormat = new StyleSettings("{Message}", "{Created}", @"\{(\w+)\}", "{Level}:{Name}:{Message}");
            public static readonly StyleSettings StringTemplate = new StyleSettings("${Message}", "${Created}", @"\$\{(\w+)\}", "${Level}:${Name}:${Message}");
        
            private readonly StyleSettings _settings;
            private readonly string _format;
        
            public Style(StyleSettings settings, string format = null) {
                this._settings = settings;
                this._format = format ?? settings.DefaultFormat;
            }
        
            public bool UsesTime => this._format.IndexOf(this._settings.TimeToken, StringComparison.Ordinal) >= 0;
        
            public string Format(LogRecord record, string dateFormat = null) {
                var regex = new System.Text.RegularExpressions.Regex(this._settings.RegexSearch);
                return regex.Replace(this._format, x => Style.Replace(x, record, dateFormat));
            }
        
            private static string Replace(System.Text.RegularExpressions.Match match, LogRecord record, string dateFormat) {
                System.Diagnostics.Debug.Assert(match.Groups.Count == 2, "one item should be captured");
                var value = Functions.GetProperty<LogRecord, object>(record, match.Groups[1].Value);
                return value is DateTime && dateFormat != null ? ((DateTime) value).ToString(dateFormat) : value.ToString();
            }
        }
        
        public class Formatter {
            private readonly Style _style;
            private readonly string _dateFormat;
        
            public Formatter(string format = null, string dateFormat = null, StyleSettings settings = null) {
                var preferredSettings = settings ?? Style.StringFormat;
                this._style = new Style(preferredSettings, format ?? preferredSettings.PreferredFormat);
                this._dateFormat = dateFormat ?? Scan.DateTimeFormat;
            }
        
            public string FormatTime(LogRecord record, string dateFormat = null) {
                return record.Created.ToString(dateFormat ?? this._dateFormat);
            }
        
            public string FormatException(LogRecord record) {
                return record.Error.ToString();
            }
        
            public bool UsesTime => this._style.UsesTime;
        
            public string FormatMessage(LogRecord record) {
                return this._style.Format(record, this._dateFormat);
            }
        
            public string Format(LogRecord record) {
                var message = this.FormatMessage(record);
                if (record.Error != null) {
                    message += StreamHandler.Terminator + this.FormatException(record);
                }
                return message;
            }
        }
        
        public class LogRecord {
            public string Name { get; }
            public LoggingLevel Level { get; }
            public string FilePath { get; }
            public int LineNumber { get; }
            public string MemberName { get; }
            public string Message { get; }
            public Exception Error { get; }
            public DateTime Created { get; }
            public int ThreadId { get; }
            public string ThreadName { get; }
            public int ProcessId { get; }
            public string ProcessName { get; }
        
            public LogRecord(string name, LoggingLevel level, string filePath, int lineNumber, string memberName,
                string message, Exception error = null) {
                this.Name = name;
                this.Level = level;
                this.FilePath = filePath;
                this.LineNumber = lineNumber;
                this.MemberName = memberName;
                this.Message = message;
                this.Error = error;
                this.Created = DateTime.Now;
                var thread = System.Threading.Thread.CurrentThread;
                this.ThreadId = thread.ManagedThreadId;
                this.ThreadName = thread.Name;
                var process = System.Diagnostics.Process.GetCurrentProcess();
                this.ProcessId = process.Id;
                this.ProcessName = process.ProcessName;
            }
        
            public override string ToString() {
                return $"<{nameof(LogRecord)}: {this.Name}, {this.Level}, {this.FilePath}, {this.LineNumber}, {this.Message}>";
            }
        }
        
        public enum LoggingLevel {
            Debug,   // Detailed information, typically of interest only when diagnosing problems.
            Info,    // Confirmation that things are working as expected.
            Warning, // An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
            Error,   // Due to a more serious problem, the software has not been able to perform some function.
            Critical // A serious error, indicating that the program itself may be unable to continue running.
        }
        
        public enum ExceptionSetting {
            Pass,   // Do nothing about the problem.
            Write,  // Record problem on standard error.
            Throw   // Propagate the exception once written.
        }
        
        public class Scan {
            public const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ss";
        }
        
        public static class Functions {
            public static TOut GetProperty<TIn, TOut>(TIn value, string name) {
                return (TOut) Functions.NotNull(value.GetType().GetProperty(name), $"{name} is not available").GetValue(value);
            }
        
            public static T NotNull<T>(T value, string message = null) {
                if (value == null) {
                    throw new InvalidOperationException(message ?? $"{nameof(value)} may not be null");
                }
                return value;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-07
          • 1970-01-01
          • 2010-10-31
          • 2020-09-28
          • 2011-06-25
          • 2010-12-21
          • 2011-05-14
          • 1970-01-01
          相关资源
          最近更新 更多