【问题标题】:Who should log an error/exception谁应该记录错误/异常
【发布时间】:2010-11-19 07:09:12
【问题描述】:

我正在尝试找出登录异常时的最佳做法。

到目前为止,我每次捕获异常时都会记录。但是,当较低级别的类捕获异常(例如,来自数据库层)并将其包装在我们自己的应用程序异常中时 - 我应该也将原始异常记录在那里,还是应该让较高级别的类记录所有细节?
我的低级类由于输入参数错误而决定抛出异常的位置呢?它应该也在那里记录异常,还是让捕获代码再次记录它?

【问题讨论】:

    标签: c# exception logging exception-handling


    【解决方案1】:

    主要是您应该避免将它记录在较低级别的 catch 和较高级别的 catch 中,因为这会使日志中包含冗余信息(更不用说会占用额外的 IO 资源来写入日志)。

    如果您正在寻找有关异常处理的一般最佳实践信息,this link is handy

    【讨论】:

      【解决方案2】:

      只要您的日志记录代码 (a) 记录异常的堆栈跟踪,并且 (b) 记录整个内部异常链,您就可以在应用的最顶层只记录一次.

      Microsoft 异常处理应用程序块会为您处理这两件事。我猜其他日志框架也会这样做。

      【讨论】:

        【解决方案3】:

        在我的 winform 应用程序中,我创建了一些 Observer 用于日志记录。观察者有订阅者,他们可以在某处写日志,或者处理它。 它的样子:

            public static class LoggingObserver
            {
                /// <summary>
                /// Last getted log message
                /// </summary>
                public static string LastLog;
        
                /// <summary>
                /// Last getted exception
                /// </summary>
                public static Exception LastException;
        
                /// <summary>
                /// List of log's processors
                /// </summary>
                public static List<BaseLogging> loggings = new List<BaseLogging>();
        
                /// <summary>
                /// Get Exception and send for log's processors
                /// </summary>
                /// <param name="ex">Exception with message</param>
                public static void AddLogs(Exception ex)
                {
                    LastException = ex;
                    LastLog = string.Empty;
                    foreach (BaseLogging logs in loggings)
                    {
                        logs.AddLogs(ex);
                    }
                }
        
                /// <summary>
                /// Get message log for log's processors
                /// </summary>
                /// <param name="str">Message log</param>
                public static void AddLogs(string str)
                {
                    LastException = null;
                    LastLog = str;
                    foreach (BaseLogging logs in loggings)
                    {
                        logs.AddLogs(str);
                    }
                }
        
                /// <summary>
                /// Close all processors
                /// </summary>
                public static void Close()
                {
                    foreach (BaseLogging logs in loggings)
                    {
                        logs.Close();
                    }
                }
            }
        

        订阅者的抽象类:

        public abstract class BaseLogging
            {
                /// <summary>
                /// Culture (using for date) 
                /// </summary>
                public CultureInfo culture;
        
                /// <summary>
                /// Constructor
                /// </summary>
                /// <param name="culture">Culture</param>
                public BaseLogging(CultureInfo culture)
                {
                    this.culture = culture;
                }
        
                /// <summary>
                /// Add log in log system
                /// </summary>
                /// <param name="str">message of log</param>
                public virtual void AddLogs(string str)
                {
                    DateTime dt = DateTime.Now;
        
                    string dts = Convert.ToString(dt, culture.DateTimeFormat);
        
                    WriteLine(String.Format("{0} : {1}", dts, str));
                }
        
                /// <summary>
                /// Add log in log system
                /// </summary>
                /// <param name="ex">Exception</param>
                public virtual void AddLogs(Exception ex)
                {
                    DateTime dt = DateTime.Now;
        
                    string dts = Convert.ToString(dt, culture.DateTimeFormat);
                    WriteException(ex);
                }
        
                /// <summary>
                /// Write string on log system processor 
                /// </summary>
                /// <param name="str">logs message</param>
                protected abstract void WriteLine(string str);
        
                /// <summary>
                /// Write string on log system processor 
                /// </summary>
                /// <param name="ex">Exception</param>
                protected abstract void WriteException(Exception ex);
        
                /// <summary>
                /// Close log system (file, stream, etc...)
                /// </summary>
                public abstract void Close();
            }
        

        以及记录到文件的实现:

        /// <summary>
            /// Logger processor, which write log to some stream
            /// </summary>
            public class LoggingStream : BaseLogging
            {
                private Stream stream;
        
                /// <summary>
                /// Constructor.
                /// </summary>
                /// <param name="stream">Initialized stream</param>
                /// <param name="culture">Culture of log system</param>
                public LoggingStream (Stream stream, CultureInfo culture)
                    : base(culture)
                {
                    this.stream = stream;
                }
        
                /// <summary>
                /// Write message log to stream
                /// </summary>
                /// <param name="str">Message log</param>
                protected override void WriteLine(string str)
                {
                    try
                    {
                        byte[] bytes;
        
                        bytes = Encoding.ASCII.GetBytes(str + "\n");
                        stream.Write(bytes, 0, bytes.Length);
                        stream.Flush();
                    }
                    catch { }
                }
        
                /// <summary>
                /// Write Exception to stream
                /// </summary>
                /// <param name="ex">Log's Exception</param>
                protected override void WriteException(Exception ex)
                {
                    DateTime dt = DateTime.Now;
        
                    string dts = Convert.ToString(dt, culture.DateTimeFormat);
                    string message = String.Format("{0} : Exception : {1}", dts, ex.Message);
                    if (ex.InnerException != null)
                    {
                        message = "Error : " + AddInnerEx(ex.InnerException, message);
                    }
                    WriteLine(message);
                }
                /// <summary>
                /// Closing stream
                /// </summary>
                public override void Close()
                {
                    stream.Close();
                }
        
                private string AddInnerEx(Exception exception, string message)
                {
                    message += "\nInner Exception : " + exception.Message;
                    if (exception.InnerException != null)
                    {
                        message = AddInnerEx(exception.InnerException, message);
                    }
                    return message;
                }
            }
        

        使用:

        //initialization 
        FileStream FS = new FileStream(LogFilePath, FileMode.Create);
        LoggingObserver.loggings.Add(new LoggingStream(FS, Thread.CurrentThread.CurrentCulture));
        //write exception 
        catch (Exception ex) {
        LoggingObserver.AddLog(new Exception ("Exception message", ex));
        }
        //write log 
        LoggingObserver.AddLog("Just a log");
        

        【讨论】:

        • 这如何回答 OP 的问题?
        • 为进程日志使用不同的订阅者。这是我的最佳做法。
        【解决方案4】:

        记录你捕捉到的地方,如果你正在包装,那么你应该。如果较低的包装器没有,那么您有理由(可调试性)这样做。但是,除非您知道它是良性的或者您可以处理它,否则不要吞下异常。

        我建议

        try{
        .
        .
        .
        } catch(Exception ex){
         ... log ....
         throw;
        }
        

        如果您需要记录并传递异常。

        【讨论】:

        • “但是不要吞下异常,除非你知道它是良性的或者你可以处理它。” ——即使那样你也不应该吞下它。恕我直言,这就是日志级别的存在。
        • 我不同意 - 如果一个 api 被设计为在像 MSMQ 那样时提供异常,那么你必须接受并接受就是这样。显然,您必须确定异常类型和内容。
        猜你喜欢
        • 2020-03-08
        • 2020-04-29
        • 1970-01-01
        • 2013-03-08
        • 2010-10-07
        • 2011-11-25
        • 1970-01-01
        • 1970-01-01
        • 2011-01-07
        相关资源
        最近更新 更多