使用C#创建日志文件的最佳方法

33

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

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

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

我在思考是否有更好的方法来处理这个问题?


1
你的应用程序是在远程扫描所有机器还是会单独部署在每台机器上? - Wouter de Kort
你想要创建一个日志文件还是每台机器一个? - Renatas M.
6
log4net也许值得一看:http://logging.apache.org/log4net/release/features.html - Anders Lindahl
我将远程访问这些机器,并希望为每台机器创建一个日志文件。理想情况下,那些我已经修复的将会进入特定的文件夹,而失败的则会被转储到另一个文件夹中。我只是不确定最佳方法是什么?谢谢大家。 - Derek
12个回答

13
我建议使用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

Sources/Good links:

Log4Net: 使用编程方式指定多个记录器(具有多个文件附加器)

使用编程方式添加附加器

如何从头开始以编程方式配置log4net(无配置)

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

编辑2:

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

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

在log4net配置中:
<param name="File" value="${log4netFileName}".log/>

你完成一个 appender 之后如何处理它? - millejos

8

我不会使用第三方库,我会将日志记录到一个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");
    }
}

通常我会反对并建议使用log4net或Enterprise Library...为什么要重复造轮子呢。但是这个解决方案简单且适合当前情况,如果您以前没有使用过那些解决方案,那么这可能比阅读文档和配置它们更快。干得好! - bopapa_1979
3
我可以翻译中文。这段话的意思是,虽然作者不是.NET专家,但看起来每次调用“Log(string, string)”函数时,您都会将整个日志文件保存到磁盘上,而且还在“_doc”变量中保留了该文件的副本,这显然不是一个好主意。请注意,我的翻译尽可能保持原意和易懂性,没有其他额外内容。 - Gareth Davis
4
如果应用程序崩溃、进程被终止或计算机重新启动且没有为每个调用保存日志,那么我们真的会陷入麻烦。关于内存,我认为为每个 Log(string, string) 调用加载 XML 文档将耗费更多的内存(考虑100个线程)。 - croisharp
2
到底发生了什么,只用 File.AppendAllLines 将其转储为简单的分隔格式并继续使用。通过输出平面文本,无需保留任何内容在内存中或重新读取文件,只需将一些行附加到文件末尾并继续前进。 - Seph
3
这绝非一个好主意。如果应用程序在执行过程中崩溃,那么最终你将没有锁定文件或者有一个包含截断数据的锁定文件。 - akd
1
-1 - 我已经提到的主要问题是持续保存xml文件。使用普通文件追加每个日志会更好,这样您就不必每次重写5MB的文件,而只需追加200字节。要读取日志文件,您可以使用记事本阅读它,或者最好创建一个应用程序来读取和解释它(按严重性、按主机等排序),在需要时使用。 - Matteo TeoMan Mangano

5
你可能需要使用事件日志!以下是从C#访问它的方法:http://support.microsoft.com/kb/307024/en 但无论你使用什么方法,我建议每次向日志添加内容时都将其输出到文件中,而不是在进程退出时输出,这样在崩溃或进程被终止的情况下就不会丢失数据。

5

5

4
您可以使用Apache log4net库
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.........");
    }
}

4

请添加此配置文件


*************************************************************************************
<!--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>

3

2

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接