倍洽

一个简单的 C# 异步日志记录器

本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划

Clearcove.Logging是一个非常简单的日志库,旨在通过直接许可条款满足大多数日志记录需求。

介绍

我知道你在想什么——代码世界真的需要另一个日志库吗?

如果你在.NET中寻找一个日志库,那么你有很多选择。有NLog,Log4Net,Enterprise Logging,erilog and Common.Logging,这些只是我现在暂时能想到的。我们不难找到一些由才华横溢的开发人员编写的日志库,他们花费了大量时间和精力创建了一些功能强大且功能丰富的软件。

那么,这个问题是否还需要解决呢?

背景

几个月前,我进入了一个日志库市场。我是一个商业桌面应用程序的创建者,该商业桌面应用程序通过互联网分发。因此,我有三个硬性需求:

  1. 异步写入日志条目。我看到过有太多的应用程序因为是同步日志记录,因而出现了严峻的性能问题。
  2. 库应该尽可能小。我不希望我的用户就为了一个简单的日志功能就得下载和加载1 MB的DLL。越小越好。
  3. 我不想增加应用程序许可的复杂性。目前,我的客户必须同意我的许可条款。添加具有单独许可条款的第三方组件可能意味着需要额外的工作来评估我的产品。也许这有点偏执,但我只是想保持简单。

我认为这些都是非常简单的要求,但事实证明,我找不到任何符合我需求的产品。特别是,我发现许多日志库的许可条款是我无法接受的,因为我不想被迫分发“另一个”许可证。

所以我决定自己来写日志库——Clearcove.Logging。它只有83行代码,非常轻巧。完整的实现放在一个单独的.cs文件中,以便在不必导入库的情况下重用。代码是用VS 2017编写的,但我曾试图编写可与早期版本兼容的代码。日志库以.NET 2.0为目标,为了吸引更多的用户。

我认为这种日志记录方法是一个很好的选择,是因为:

  1. 应用程序没有复杂的日志记录需求
  2. 这是一个小程序,让你从简化的部署中受益
  3. 许可复杂性必须保持在最低限度

那么它是怎样工作的?

使用代码

首先,我要知道我想记录什么信息。我想要一个简单的API,可以用来记录时间戳、记录器名称、线程ID和消息等信息。我对Log4Net API非常熟悉,并且从中借鉴了很多。

要声明和使用记录器,可以使用如下语法:

var log = new Logger(typeof(Program));    // Class level declaration.
log.Error("My error message", exception); // Logging from within a method.
log.Info("My info message");

如果你以前使用过其他日志记录库,那么你可能熟悉这种语法。

数据封装

接下来,我要将我的日志条目表示为一个简单的对象。这样做的主要原因是我希望我的记录器能够提升日志记录事件。有时我会在创建单元和集成测试时使用这些事件,因为我发现它们可以提供帮助。这只是个人喜好。如果你对提升日志记录事件不感兴趣,则可以简化此代码。

日志记录事件封装在LogMessageInfo 对象中,该对象的实现方式如下:

public sealed class LogMessageInfo
{
    public readonly DateTime Timestamp;
    public readonly string ThreadId;
    public readonly string Level;
    public readonly string Logger;
    public readonly string Message;
}

编写日志条目

上面用于API实现和数据封装部分的代码虽然冗长但非常简单。但是,异步日志记录略有差别。例如,如果抛出导致应用程序关闭的异常,那么会发生什么情况?我们如何知道所有日志条目将按照接收到的顺序编写?有若干方法可以解决这个问题。 Clearcove.Logger 就是其中一种简单但不优雅的方式:

static void Main(string[] args)
{
      var targetLogFile = new FileInfo("./MyApp.log");
      Logger.Start(targetLogFile); // Loggers will complains if you skip initialization
      try
      {
          Run(args);
      }
      finally
      {
          Logger.ShutDown(); // Removing this line may result in lost log entries.
      }
}

这是一个Clearcove.Logging 背离其他实现,例如Log4Net的示例。我们必须告诉我们的记录器何时开始记录以及何时停止记录。在尝试将任何日志条目写入日志文件之前,我们必须执行这个操作。将Logger.ShutDown() 调用放在finally语句中应该使我们的记录器有机会在应用程序关闭之前将所有待处理的日志条目写入日志文件。当然,也有日志条目不写入的情况。例如,如果机器没电了。如果其中有些边缘情况是你所关心的,则可能需要考虑同步日志记录。

Clearcove.Logging 通过使用单个System.Thread.Timer 实例来实现异步日志写入。我们没有设置线程计时器的周期,因此计时器只会触发一次。在所有待处理日志条目被成功写入日志文件后,计时器将重置,等到下一个间隔触发。这种行为类似于在计时器上设置一段时间,但会阻止计时器在间隔延迟的情况下被多次触发。

最后,通过简单调用File.AppendAllText将日志条目写入文件。这种调用可能不是多次写入日志文件最有效的方法,但是出于保持代码尽可能简单的前提。

好了。一个非常简单的日志实现完成了,它完全能够满足大多数应用程序的日志需求。而且非常适合我,帮助我解决了所有的日志记录问题,同时将依赖关系降到最低。

进一步的工作

记录器简单的好处之一是它很容易理解,并且可以快速定制以满足你的需求。例子包括滚动日志文件,同步日志记录,外部配置等。这些功能的实现就留给大家练习吧。玩的开心!

此记录器实现的一个很大的缺点是它只是.NET。我打算尽快发布这个日志记录库的Java实现。

另外,请注意,一些CodeProject用户可能会在下面发布增强功能。我会尝试在不增加复杂性的情况下合并更改,但如果你发现此记录器不怎么满足你的需求,则可能需要阅读下面的注释。

兴趣点

让我陷入混乱的一件事是希望简化软件许可。我努力想使得Clearcove.Logger 免费,而不增加许可复杂性。根据我的研究,我相信Ms-PL是最宽容的许可证。不但简单,易于阅读和理解,并且重要的是要求你的二进制发行版“在遵循本许可证的许可下”发布。在我看来,这个陈述是一个开放的解释,给了我们很大的灵活性。当然,我的看法是,一方面软件应该尽可能免费,另一方面应该仍然给予用户需要的保护。如果你有更加开放的许可建议,请告诉我。

下载源代码 – 7.7 KB

译文链接:http://www.codeceo.com/article/simple-csharp-logger.html
英文原文:A Simple Asynchronous Logger in C#
翻译作者:码农网 – 小峰
转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

发表我的评论

取消评论
表情 插代码

Hi,您需要填写昵称和邮箱!

  • 必填项
  • 必填项