【问题标题】:c# Best Method to create a log filec# 创建日志文件的最佳方法
【发布时间】:2012-05-01 19:01:59
【问题描述】:

我正在编写一个工具,它将通过网络检查工作站的健康状况,并根据它发现的问题进行修复。我想创建一个日志文件,因为应用程序在每台机器上运行其任务/检查。我现在只想让它在一台机器上工作,但随着时间的推移,它将一次扫描 100 多台机器(线程已结束)。

创建日志文件的最佳方法是什么?

我正在考虑使用List<string> 在内存中构建日志文件,然后在完成后将其输出到文件中。

我只是在想可能有更好的方法来做到这一点?

【问题讨论】:

  • 只有一个应用程序正在运行远程扫描所有机器,还是您的应用程序会单独部署在每台机器上?
  • 您要创建一个日志文件还是每台机器?
  • 我将远程访问这些机器,我希望为每台机器创建一个日志文件。理想情况下,我修复的那些将转到特定文件夹,那些失败的将转储到不同的文件夹中。我只是不确定这样做的最佳方法?谢谢大家

标签: c# file logging


【解决方案1】:

您可能想要使用事件日志!这是从 C# 访问它的方法 http://support.microsoft.com/kb/307024/en

但是无论您将使用什么方法,我都建议您在每次将某些内容附加到日志时而不是在您的进程退出时输出到一个文件,这样您就不会在崩溃的情况下丢失数据或如果您的进程被杀死。

【讨论】:

    【解决方案2】:

    您可以使用http://logging.apache.org/ 库并使用数据库附加程序来收集您的所有日志信息。

    【讨论】:

      【解决方案3】:

      您还可以查看内置的 .NET 跟踪工具。有一组跟踪侦听器允许您输出到日志文件,但您可以将其配置为登录到事件查看器或数据库(或同时登录所有数据库)。

      http://www.codeguru.com/csharp/.net/net_debugging/tracing/article.php/c5919/NET-Tracing-Tutorial.htm

      【讨论】:

        【解决方案4】:

        使用 Nlog http://nlog-project.org/。它是免费的,允许写入文件、数据库、事件日志和其他 20 多个目标。 另一个日志框架是 log4net - http://logging.apache.org/log4net/(从 java Log4j 项目移植)。它也是免费的。

        最佳实践是使用通用日志记录 - http://commons.apache.org/logging/ 因此,您可以稍后将 NLog 或 log4net 更改为其他日志记录框架。

        【讨论】:

          【解决方案5】:

          我们对日志记录进行了大量研究,并认为 NLog 是最适合使用的。

          见http://nlog-project.org/

          另见log4net vs. Nlog 和http://www.dotnetlogging.com/comparison/

          【讨论】:

            【解决方案6】:

            我会推荐log4net。

            您需要多个日志文件。所以多个文件附加程序。另外,您可以动态创建文件附加程序。

            示例代码:

            using log4net;
            using log4net.Appender;
            using log4net.Layout;
            using log4net.Repository.Hierarchy;
            
            // Set the level for a named logger
            public static void SetLevel(string loggerName, string levelName)
            {
                ILog log = LogManager.GetLogger(loggerName);
                Logger l = (Logger)log.Logger;
            
                l.Level = l.Hierarchy.LevelMap[levelName];
                }
            
            // Add an appender to a logger
            public static void AddAppender(string loggerName, IAppender appender)
            {
                ILog log = LogManager.GetLogger(loggerName);
                Logger l = (Logger)log.Logger;
            
                l.AddAppender(appender);
            }
            
            // Create a new file appender
            public static IAppender CreateFileAppender(string name, string fileName)
            {
                FileAppender appender = new
                    FileAppender();
                appender.Name = name;
                appender.File = fileName;
                appender.AppendToFile = true;
            
                PatternLayout layout = new PatternLayout();
                layout.ConversionPattern = "%d [%t] %-5p %c [%x] - %m%n";
                layout.ActivateOptions();
            
                appender.Layout = layout;
                appender.ActivateOptions();
            
                return appender;
            }
            
            // In order to set the level for a logger and add an appender reference you
            // can then use the following calls:
            SetLevel("Log4net.MainForm", "ALL");
            AddAppender("Log4net.MainForm", CreateFileAppender("appenderName", "fileName.log"));
            
            // repeat as desired
            

            来源/好链接:

            Log4Net: Programmatically specify multiple loggers (with multiple file appenders)

            Adding appenders programmatically

            How to configure log4net programmatically from scratch (no config)

            另外,log4net 还允许写入事件日志。一切都是基于配置的,配置也可以在运行时从 xml 动态加载。

            编辑 2:

            动态切换日志文件的一种方法: Log4Net 配置文件支持环境变量:

            Environment.SetEnvironmentVariable("log4netFileName", "MyApp.log");
            

            在 log4net 配置中:

            <param name="File" value="${log4netFileName}".log/>
            

            【讨论】:

            • 非常感谢,非常感谢。
            • 使用完 appender 后你会如何处理它?
            【解决方案7】:

            我不会使用第三方库,我会登录到一个 xml 文件。

            这是一个从不同线程记录到 xml 文件的代码示例:

            private static readonly object Locker = new object();
            private static XmlDocument _doc = new XmlDocument();
            
            static void Main(string[] args)
            {
                if (File.Exists("logs.txt"))
                    _doc.Load("logs.txt");
                else
                {
                    var root = _doc.CreateElement("hosts");
                    _doc.AppendChild(root);
                }
            
                for (int i = 0; i < 100; i++)
                {
                    new Thread(new ThreadStart(DoSomeWork)).Start();
                }
            }
            
            static void DoSomeWork()
            {
                /*
                 * Here you will build log messages
                 */
                Log("192.168.1.15", "alive");
            }
            
            static void Log(string hostname, string state)
            {
                lock (Locker)
                {
                    var el = (XmlElement)_doc.DocumentElement.AppendChild(_doc.CreateElement("host"));
                    el.SetAttribute("Hostname", hostname);
                    el.AppendChild(_doc.CreateElement("State")).InnerText = state;
                    _doc.Save("logs.txt");
                }
            }
            

            【讨论】:

            • -1 - 我不是 .Net 专家,但看起来您正在为每次调用 Log(string,string) 将整个日志文件保存到磁盘,不仅是您维护了日志的副本在内存中_doc 中的文件,这肯定不是一个好主意。
            • Gareth,如果应用程序崩溃、进程被杀死或计算机重新启动并且每次调用都没有保存日志,那么我们真的有麻烦了。关于内存,我认为为每个 Log(string, string) 调用加载 xml 文档会花费更多的内存(想想 100 个线程)。
            • 无论使用File.AppendAllLines 发生了什么,将其转储为简单的分离格式并继续。通过输出纯文本,无需在内存中保留任何内容,或重新读取文件,只需在文件末尾附加一些行并继续。
            • 这根本不是一个好主意。如果应用程序在中间崩溃,那么您最终将没有锁定文件或包含截断数据的锁定文件。
            • -1 - 正如已经发布的那样,我看到的主要问题是 xml 文件的持续保存。附加每个日志的普通文件要好得多,因此您不必每次都重写 5Mb 的文件,而只需附加 200 个字节。要读取日志文件,您可以使用记事本读取它,或者在需要时更好地创建一个应用程序来读取它(按严重性、主机等排序)
            【解决方案8】:

            你可以使用Apache log4net library:

            using System;
            using log4net;
            using log4net.Config;
            
            
            public class MyApp
            {
                // Define a static logger variable so that it references the
                // Logger instance named "MyApp".
                private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
                static void Main(string[] args)
                {
                    XmlConfigurator.Configure(new System.IO.FileInfo(@"..\..\resources\log4net.config"));
                    log.Info("Entering application.");
                    Console.WriteLine("starting.........");
                    log.Info("Entering application.");
                    log.Error("Exiting application.");
                    Console.WriteLine("starting.........");
                }
            }
            

            【讨论】:

              【解决方案9】:

              添加此配置文件


              *************************************************************************************
              <!--Configuration for file appender-->
              
              <configuration>
                <configSections>
                  <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
                </configSections>
                <log4net>
                  <appender name="FileAppender" type="log4net.Appender.FileAppender">
                    <file value="logfile.txt" />
                    <appendToFile value="true" />
                    <layout type="log4net.Layout.PatternLayout">
                      <conversionPattern value="%d [%t] %-5p [%logger] - %m%n" />
                    </layout>
                  </appender>
                  <root>
                    <level value="DEBUG" />
                    <appender-ref ref="FileAppender" />
                  </root>
                </log4net>
              </configuration>
              
              *************************************************************************************
              
              <!--Configuration for console appender-->
              
              
              <configuration>
              
                <configSections>
                  <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,
                      log4net" />
                </configSections>
              
                <log4net>
                  <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
                    <layout type="log4net.Layout.PatternLayout">
                      <param name="ConversionPattern" value="%d [%t] %-5p [%logger] - %m%n" />
                    </layout>
                  </appender>
                 <root>
                    <level value="ALL" />
                    <appender-ref ref="ConsoleAppender" />
                  </root>
                </log4net>
              </configuration>
              

              【讨论】:

                【解决方案10】:

                我正在使用线程安全的 static 类。主要思想是将消息排队到列表中,然后每个时间段或每个计数器限制保存到日志文件。

                重要提示:您应该在退出程序时强制保存文件(DirectLog.SaveToFile();)。 (以防列表中还有一些项目)

                使用很简单:DirectLog.Log("MyLogMessage", 5);

                这是我的代码:

                using System;
                using System.IO;
                using System.Collections.Generic;
                
                namespace Mendi
                {
                
                    /// <summary>
                    /// class used for logging misc information to log file
                    /// written by Mendi Barel
                    /// </summary>
                    static class DirectLog
                    {
                        readonly static int SAVE_PERIOD = 10 * 1000;// period=10 seconds
                        readonly static int SAVE_COUNTER = 1000;// save after 1000 messages
                        readonly static int MIN_IMPORTANCE = 0;// log only messages with importance value >=MIN_IMPORTANCE
                
                        readonly static string DIR_LOG_FILES = @"z:\MyFolder\";
                
                        static string _filename = DIR_LOG_FILES + @"Log." + DateTime.Now.ToString("yyMMdd.HHmm") + @".txt";
                
                        readonly static List<string> _list_log = new List<string>();
                        readonly static object _locker = new object();
                        static int _counter = 0;
                        static DateTime _last_save = DateTime.Now;
                
                        public static void NewFile()
                        {//new file is created because filename changed
                            SaveToFile();
                            lock (_locker)
                            {
                
                                _filename = DIR_LOG_FILES + @"Log." + DateTime.Now.ToString("yyMMdd.HHmm") + @".txt";
                                _counter = 0;
                            }
                        }
                        public static void Log(string LogMessage, int Importance)
                        {
                            if (Importance < MIN_IMPORTANCE) return;
                            lock (_locker)
                            {
                                _list_log.Add(String.Format("{0:HH:mm:ss.ffff},{1},{2}", DateTime.Now, LogMessage, Importance));
                                _counter++;
                            }
                            TimeSpan timeDiff = DateTime.Now - _last_save;
                
                            if (_counter > SAVE_COUNTER || timeDiff.TotalMilliseconds > SAVE_PERIOD)
                                SaveToFile();
                        }
                
                        public static void SaveToFile()
                        {
                            lock (_locker)
                                if (_list_log.Count == 0)
                                {
                                    _last_save = _last_save = DateTime.Now;
                                    return;
                                }
                            lock (_locker)
                            {
                                using (StreamWriter logfile = File.AppendText(_filename))
                                {
                
                                    foreach (string s in _list_log) logfile.WriteLine(s);
                                    logfile.Flush();
                                    logfile.Close();
                                }
                
                                _list_log.Clear();
                                _counter = 0;
                                _last_save = DateTime.Now;
                            }
                        }
                
                
                        public static void ReadLog(string logfile)
                        {
                            using (StreamReader r = File.OpenText(logfile))
                            {
                                string line;
                                while ((line = r.ReadLine()) != null)
                                {
                                    Console.WriteLine(line);
                                }
                                r.Close();
                            }
                        }
                    }
                }
                

                【讨论】:

                • 不需要锁定文件。线程.睡眠(1)!锁定您无法打开文件进行写入的事实。该文件将成为您的锁。这是跨线程、进程、应用程序、服务器……安全的。锁定对象仅是过程安全的。
                【解决方案11】:

                我发现 GitHub 上 heiswayi 的 SimpleLogger 很好。

                【讨论】:

                  【解决方案12】:

                  我没有使用外部库 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.
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-04-14
                    • 2015-06-28
                    • 1970-01-01
                    • 2012-07-13
                    • 2010-09-07
                    • 1970-01-01
                    相关资源
                    最近更新 更多