【问题标题】:Simple way to perform error logging?执行错误记录的简单方法?
【发布时间】:2013-12-11 16:59:18
【问题描述】:

我创建了一个小型 C# winforms 应用程序,作为一项附加功能,我正在考虑向其中添加某种形式的错误日志记录。有人对解决此问题的好方法有任何建议吗?这是我从未考虑添加到以前项目中的一项功能,因此我愿意听取有更多经验的开发人员的建议。

我正在考虑将异常写入指定的文本文件,或者可能是数据库表。这是一个将使用几个月,然后在完成更大的产品时丢弃的应用程序。

【问题讨论】:

  • 如果您正在考虑 DB 日志记录,为什么不为错误创建自己的 SQL 插入函数?
  • 看看这个:logging.apache.org/log4net,顺便说一句,你不想使用系统日志吗?这是最简单的解决方案。
  • “这是一个会使用几个月,然后在完成更大的产品时丢弃的应用程序”?想打赌吗?
  • @Harry,我正在调查。这和下面Mino的建议一样吗?试图弄清楚如何设置它。 Dour,这是事实,虽然我知道通常不是这样,哈哈。
  • 同理,不知道上次有没有看到。我在生产项目中看到过 Log4Net,它看起来很简单。我必须检查文档如何配置它,但是对于基本日志记录,AFAIK 需要设置的选项很少。

标签: c# .net logging error-handling error-logging


【解决方案1】:

我不会过多地研究外部库,因为您的日志记录需求很简单。

.NET Framework 已经在命名空间 System.Diagnostics 中提供了此功能,您可以通过简单地调用 Trace 类下的方法来编写所需的所有日志记录:

Trace.TraceInformation("Your Information");
Trace.TraceError("Your Error");
Trace.TraceWarning("Your Warning");

然后在您的 app.config 文件中配置所有符合您需求的跟踪侦听器:

<configuration>
  // other config
  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener"/>
        <add name="textWriterListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="YourLogFile.txt"/>
        <add name="eventLogListener" type="System.Diagnostics.EventLogTraceListener" initializeData="YourEventLogSource" />
        <remove name="Default"/>
      </listeners>
    </trace>
  </system.diagnostics>
  // other config
</configuration>

或者,如果您愿意,也可以在应用程序中配置您的侦听器,而无需依赖配置文件:

Trace.Listeners.Add(new TextWriterTraceListener("MyTextFile.log"));

请记住将 Trace.AutoFlush 属性设置为 true,以便 Text 日志正常工作。

【讨论】:

  • 它将这个记录到哪里?文本文件?
  • 这种方法的输出依赖于你配置的监听器。也就是说,要登录到文本文件,只需配置一个 System.Diagnostics.TextWriterTraceListener。
  • 您好,这可行,但会产生异常:System.dll 中发生“System.Security.SecurityException”类型的异常,但未在用户代码中处理。因此,它写入日志但没有这样做的权限。 stackoverflow.com/questions/20389248/…
  • 你是对的!如果是 EventLogListener,则必须事先创建 Event Source 键。例如,您可以在网站上使用 simple Powershell script,但如果它是交付的软件,通常安装程序会处理此问题。
【解决方案2】:

您可以使用SimpleLog

这是一个简单但强大且功能强大的一类日志记录解决方案,易于理解、易于集成和易于使用。无需花费数天时间来设置和自定义 log4Net,使用该课程,您可以在几分钟内完成。

虽然它当前记录到文件中,但应该可以轻松定制以记录到数据库。

http://www.codeproject.com/Tips/585796/Simple-Log

【讨论】:

  • 请不要发布仅链接的答案
  • 如果是nuget pacakge就好了
  • 嗯,下载 ZIP 并将源文件添加到我的应用程序需要两秒钟。谢谢。 :)
【解决方案3】:

在我看来,最佳解决方案是使用 NLog:http://nlog-project.org/

只需从 NuGet 安装配置包:http://www.nuget.org/packages/NLog.Config/,您最终将获得库和预配置的文件记录器...

然后在你的代码中你只需要:

// A logger member field:

private readonly Logger logger = LogManager.GetCurrentClassLogger(); // creates a logger using the class name

// use it:
logger.Info(...);
logger.Error(...);

// and also:
logger.ErrorException("text", ex); // which will log the stack trace.

在你得到的配置文件中,你需要取消注释你需要的部分:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- 
        See http://nlog-project.org/wiki/Configuration_file 
        for information on customizing logging rules and outputs.
    -->
    <targets>
        <!-- add your targets here -->

        <!-- UNCOMMENT THIS!
        <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
                layout="${longdate} ${uppercase:${level}} ${message}" />
        -->
    </targets>

    <rules>
        <!-- add your logging rules here -->

        <!-- UNCOMMENT THIS!
        <logger name="*" minlevel="Trace" writeTo="f" />
        -->
    </rules>
</nlog>

nlog.config文件的属性编辑为

Copy to Output Directory: Copy always

【讨论】:

  • 如果使用error.XXXException(string,exception),则需要额外注意...您需要使用${exception} 修改目标布局以获取异常消息或${exception:format=tostring} 以获取完整的tostring 值。跨度>
  • 感谢简明扼要地展示在配置和代码中要做的事情......谢谢!
  • 声称这个第三方库是“最好的解决方案”可能是一个过于激进的说法。我认为更客观的措辞将不胜感激。
【解决方案4】:

log4net 就像一块砖一样工作。配置可能有点困难,但值得。它还允许您配置这些日志文件的文件锁定等。

http://www.codeproject.com/Articles/140911/log4net-Tutorial

【讨论】:

  • 似乎这可能是值得尝试的事情,无论如何我肯定有兴趣了解更多。你能帮我弄清楚 app.config、dll 和代码中的设置吗?您的链接似乎有点混乱。
  • 如果你愿意,我可以写一个关于如何使用 log4net 作为答案的简短描述。
  • Log4net 非常慢而且很重。自从我在我的应用程序的各个类中添加了static readonly log = LogManager.GetLogger(...) 语句后,我注意到启动时间激增,并且(特别令人讨厌)单元测试启动时间需要很长时间。
  • 启动时间可能会慢一些(我没有测试那么多),但是写日志非常快。
【解决方案5】:

创建一个名为 Log.cs 的类 我正在使用 Linq To SQl 保存到数据库

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
public static partial class Log
{
    /// <summary>
    /// Saves the exception details to ErrorLogging db with Low Priority
    /// </summary>
    /// <param name="ex">The exception.</param>
    public static void Save(this Exception ex)
    {
        Save(ex, ImpactLevel.Low, "");
    }

    /// <summary>
    /// Saves the exception details to ErrorLogging db with specified ImpactLevel
    /// </summary>
    /// <param name="ex">The exception.</param>
    /// <param name="impactLevel">The Impact level.</param>
    public static void Save(this Exception ex, ImpactLevel impactLevel)
    {
        Save(ex, impactLevel,"");
    }
    /// <summary>
    /// Saves the exception details to ErrorLogging db with specified ImpactLevel and user message
    /// </summary>
    /// <param name="ex">The exception</param>
    /// <param name="impactLevel">The impact level.</param>
    /// <param name="errorDescription">The error Description.</param>
    public static void Save(this Exception ex, ImpactLevel impactLevel, string errorDescription)
    {
        using (var db = new ErrorLoggingDataContext())
        {
            Log log = new Log();

            if (errorDescription != null && errorDescription != "")
            {
                log.ErrorShortDescription = errorDescription;
            }
            log.ExceptionType = ex.GetType().FullName;
            var stackTrace = new StackTrace(ex, true);
            var allFrames = stackTrace.GetFrames().ToList();
            foreach (var frame in allFrames)
            {
                log.FileName = frame.GetFileName();
                log.LineNumber = frame.GetFileLineNumber();
                var method = frame.GetMethod();
                log.MethodName = method.Name;
                log.ClassName = frame.GetMethod().DeclaringType.ToString();
            }

            log.ImpactLevel = impactLevel.ToString();
            try
            {
                log.ApplicationName = Assembly.GetCallingAssembly().GetName().Name;
            }
            catch
            {
                log.ApplicationName = "";
            }

            log.ErrorMessage = ex.Message;
            log.StackTrace = ex.StackTrace;
            if (ex.InnerException != null)
            {
                log.InnerException = ex.InnerException.ToString();
                log.InnerExceptionMessage = ex.InnerException.Message;
            }
            log.IpAddress = ""; //get the ip address

            if (System.Diagnostics.Debugger.IsAttached)
            {
                log.IsProduction = false;
            }

            try
            {
                db.Logs.InsertOnSubmit(log);
                db.SubmitChanges();
            }
            catch (Exception eex)
            {

            }
        }
    }
}

创建下表

USE [database Name]
GO

/****** Object:  Table [dbo].[Log]    Script Date: 9/27/2016 11:52:32 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Log](
    [LogId] [INT] IDENTITY(1,1) NOT NULL,
    [ErrorDate] [DATETIME] NOT NULL CONSTRAINT [DF_Log_Date]  DEFAULT (GETDATE()),
    [ErrorShortDescription] [VARCHAR](1000) NULL,
    [ExceptionType] [VARCHAR](255) NULL,
    [FileName] [VARCHAR](1000) NULL,
    [LineNumber] [INT] NULL,
    [MethodName] [VARCHAR](255) NULL,
    [ClassName] [VARCHAR](150) NULL,
    [ImpactLevel] [VARCHAR](50) NOT NULL,
    [ApplicationName] [VARCHAR](255) NULL,
    [ErrorMessage] [VARCHAR](4000) NULL,
    [StackTrace] [VARCHAR](MAX) NULL,
    [InnerException] [VARCHAR](2000) NULL,
    [InnerExceptionMessage] [VARCHAR](2000) NULL,
    [IpAddress] [VARCHAR](150) NULL,
    [IsProduction] [BIT] NOT NULL CONSTRAINT [DF_Log_IsProduction]  DEFAULT ((1)),
    [LastModified] [DATETIME] NOT NULL CONSTRAINT [DF_Log_LastModified]  DEFAULT (GETDATE()),
 CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED 
(
    [LogId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'This table holds all the exceptions. 
ErrorData = when error happened
,[ErrorShortDescription] == short desc about the error entered by the developers
      ,[FileName] = file where error happened full path
      ,[LineNumber] = line number where code failed
      ,[MethodName] = method name where exception happened
      ,[ClassName] = class where exception happened
      ,[ImpactLevel] = high, medium, low
      ,[ApplicationName] = name of the application where error came from
      ,[ErrorMessage] = exception error messge
      ,[StackTrace] = C# stack trace
      ,[InnerException] = inner exception of strack trace
      ,[InnerExceptionMessage] = inner message
      ,[IpAddress]
      ,[IsProduction]' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Log'
GO

影响级别基本上是枚举

 public enum ImpactLevel
    {
        High = 0,
        Medium = 1,
        Low = 2,
    }

你可以如下使用它

try
{


}
catch(Exception ex)
{
    //this will save the exception details and mark exception as low priority
    ex.Save();
}


try
{


}
catch(Exception ex)
{
    //this will save the exception details with  priority you define: High, Medium,Low
    ex.Save(ImpactLevel.Medium);
}

try
{


}
catch(Exception ex)
{
    //this will save the exception details with  priority you define: High, Medium,Low
    ex.Save(ImpactLevel.Medium, "You can enter an details you want here ");
}

【讨论】:

  • 很好的贡献。正是我几乎在做的事情。谢谢!
【解决方案6】:

在阅读了这里的建议后,我最终使用了以下内容:

private void LogSystemError(string message)
{
    EventLog.WriteEntry("YourAppName", message, EventLogEntryType.Error);
}

EventLog 类可通过 System.Diagnostics 获得。

我避免了登录文件的选项(例如“yourLogFile.txt”),以避免多线程并发记录错误、文件位置和访问安全性以及文件变得过大可能出现的问题.

【讨论】:

    【解决方案7】:

    以下是 log4net 的示例:

    1. 创建一个名为 Log4NetTest 的新控制台项目
    2. 将 log4net [1.2.13] nuget 包添加到项目中
    3. 编写以下程序:

      using System.Threading.Tasks;
      using log4net;
      using System.Text;
      using System.CollectionsGeneric;
      using System;
      namespace Log4NetTest
      {
          class Program
          {
      
              private static readonly ILog _logger = LogManager.GetLogger("testApp.LoggingExample");
      
              static void Main(string[] args)
              {
                  // Configure from App.config. This is marked as obsolete so you can also add config into separate config file
                  // and use log4net.Config.XmlConfigurator method to configure from xml file.            
                  log4net.Config.DOMConfigurator.Configure(); 
      
                  _logger.Debug("Shows only at debug");
                  _logger.Warn("Shows only at warn");
                  _logger.Error("Shows only at error");
      
                  Console.ReadKey();
              }
          }
      }
      
    4. 将您的 app.config 更改为以下内容:

      <!-- language: xml -->
      <?xml version="1.0" encoding="utf-8" ?> 
      <configuration> 
          <configSections> 
              <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> 
          </configSections> 
              <startup> 
                      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> 
              </startup> 
          <log4net debug="false"> 
              <appender name="LogFileAppender" type="log4net.Appender.FileAppender,log4net" > 
                  <param name="File" value="myLog.log" /> 
                  <param name="AppendToFile" value="true" /> 
                  <layout type="log4net.Layout.PatternLayout,log4net"> 
                      <param name="ConversionPattern" value="%date [%thread] %-5level %logger %ndc - %message%newline" /> 
                  </layout>      
              </appender> 
              <root> 
                  <priority value="ALL" /> 
                  <appender-ref ref="LogFileAppender" /> 
              </root> 
              <category name="testApp.LoggingExample"> 
                  <priority value="ALL" /> 
              </category> 
          </log4net> 
      </configuration>
      

    5.运行应用程序,您应该会在 bin\Debug 文件夹中找到以下文件:

    2013-12-13 13:27:27,252 [8] DEBUG testApp.LoggingExample (null) - Shows only at debug
    2013-12-13 13:27:27,280 [8] WARN  testApp.LoggingExample (null) - Shows only at warn
    2013-12-13 13:27:27,282 [8] ERROR testApp.LoggingExample (null) - Shows only at error
    

    【讨论】:

    • 很难插入那个 XML 配置,不能做更好的样式:(.
    【解决方案8】:

    您只需将异常错误写入文本文件即可。 Write to Text File。一个建议是将您创建的文件放在 userdata 或 appdata 目录中,这样您就不必为权限而烦恼。

    由于这只需要几个月并且将被丢弃,因此没有理由过度使用 DB。一个简单的文本文件就足够了。

    【讨论】:

    • 我就是这么想的,但想听听外界的意见。目前我正在捕获异常并将它们显示如下:catch (Exception ex) { MessageBox.Show("Source:\t" + ex.Source + "\nMessage: \t" + ex.Message + "\nData:\t" + ex.Data); }。关于在写入错误日志时如何使它更有用的任何想法? (除了添加日期/时间)。
    • 异常会将您指向代码库中要调查的位置,因此除了日期/时间之外,您真的不需要太多其他东西。也许StackTrace
    【解决方案9】:

    我没有使用外部库 log4net,而是创建了自己的简单类,高度可定制且易于使用(使用您需要的命名空间编辑 YOURNAMESPACEHERE)。

    控制台应用

    using System;
    using System.IO;
    
    namespace YOURNAMESPACEHERE
    {
        enum LogEvent
        {
            Info = 0,
            Success = 1,
            Warning = 2,
            Error = 3
        }
    
        internal static class Log
        {
            private static readonly string LogSession = DateTime.Now.ToLocalTime().ToString("ddMMyyyy_HHmmss");
            private static readonly string LogPath = AppDomain.CurrentDomain.BaseDirectory + "logs";
    
            internal static void Write(LogEvent Level, string Message, bool ShowConsole = true, bool WritelogFile = true)
            {
                string Event = string.Empty;
                ConsoleColor ColorEvent = Console.ForegroundColor;
    
                switch (Level)
                {
                    case LogEvent.Info:
                        Event = "INFO";
                        ColorEvent = ConsoleColor.White;
                        break;
                    case LogEvent.Success:
                        Event = "SUCCESS";
                        ColorEvent = ConsoleColor.Green;
                        break;
                    case LogEvent.Warning:
                        Event = "WARNING";
                        ColorEvent = ConsoleColor.Yellow;
                        break;
                    case LogEvent.Error:
                        Event = "ERROR";
                        ColorEvent = ConsoleColor.Red;
                        break;
                }
    
                if (ShowConsole)
                {
                    Console.ForegroundColor = ColorEvent;
                    Console.WriteLine(" [{0}] => {1}", DateTime.Now.ToString("HH:mm:ss"), Message);
                    Console.ResetColor();
                }
    
                if (WritelogFile)
                {
                    if (!Directory.Exists(LogPath))
                        Directory.CreateDirectory(LogPath);
    
                    File.AppendAllText(LogPath + @"\" + LogSession + ".log", string.Format("[{0}] => {1}: {2}\n", DateTime.Now.ToString("HH:mm:ss"), Event, Message));
                }
            }
        }
    }
    

    没有控制台应用程序(只有日志)

    using System;
    using System.IO;
    
    namespace YOURNAMESPACEHERE
    {
        enum LogEvent
        {
            Info = 0,
            Success = 1,
            Warning = 2,
            Error = 3
        }
    
    internal static class Log
    {
        private static readonly string LogSession = DateTime.Now.ToLocalTime().ToString("ddMMyyyy_HHmmss");
        private static readonly string LogPath = AppDomain.CurrentDomain.BaseDirectory + "logs";
    
        internal static void Write(LogEvent Level, string Message)
        {
            string Event = string.Empty;
    
            switch (Level)
            {
                case LogEvent.Info:
                    Event = "INFO";
                    break;
                case LogEvent.Success:
                    Event = "SUCCESS";
                    break;
                case LogEvent.Warning:
                    Event = "WARNING";
                    break;
                case LogEvent.Error:
                    Event = "ERROR";
                    break;
            }
    
            if (!Directory.Exists(LogPath))
                Directory.CreateDirectory(LogPath);
    
            File.AppendAllText(LogPath + @"\" + LogSession + ".log", string.Format("[{0}] => {1}: {2}\n", DateTime.Now.ToString("HH:mm:ss"), Event, Message));
        }
    }
    

    用法:

    控制台应用

    Log.Write(LogEvent.Info, "Test message"); // It will print an info in your console, also will save a copy of this print in a .log file.
    Log.Write(LogEvent.Warning, "Test message", false); // It will save the print as warning only in your .log file.
    Log.Write(LogEvent.Error, "Test message", true, false); // It will print an error only in your console.
    

    没有控制台应用程序(只有日志)

    Log.Write(LogEvent.Info, "Test message"); // It will print an info in your .log file.
    

    【讨论】:

      猜你喜欢
      • 2020-07-04
      • 1970-01-01
      • 2021-01-04
      • 2022-01-13
      • 2010-10-23
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      相关资源
      最近更新 更多