4

我想编写自己的 Logging 类(在 C# 中),它实现了一个标准接口,我可以从代码的任何部分调用它。

我的想法是让多个 Log 类实现 Logger 接口,每个类都有其特定的日志目标,例如,FileLogger 将实现对文件的日志记录,TextBox 记录器将实现对 Form 中的多行 TextBox 的日志记录,DBLogger 将实现对数据库表的日志记录等。

此外,每个记录器类可以有一个嵌套记录器或链式记录器类,以便从应用程序代码对 Log() 方法的单个调用可以将消息记录到多个目的地;示例在一次调用中记录到 Form 上的文件和文本框。

我面临的困难是这样的:

通常我会登录到一个正在运行的日志文件(它将包含调试所需的所有日志消息)、一个审查日志文件(它只包含要由用户审查或需要用户操作的日志消息)、一个多行文本框屏幕(将复制所有日志消息以向用户提供进度指示)和另一个多行文本框(将仅记录用户需要查看的消息)。

当我调用 logger.Log(message) 时,某些消息可能不适用于特定的日志目标。例如,某些消息可能只记录在运行日志文件或进度文本框中,而不是用户评论文本框中,反之亦然。

由于记录器将被链接,因此单个函数调用可以登录到所有必需的目的地,特定记录器如何识别日志消息不是针对它的,从而忽略日志消息?

我的示例日志界面是:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public void AddLogger(Logger chainedLogger);
    public void RemoveLogger(Logger chainedLogger);
}

public class FileLogger : Logger
{
      //implement methods
}

public class TextBoxLogger : Logger
{
      //implement methods
}

public class DBLogger : Logger
{
      //implement methods
}

编辑1:

更准确地说,可能有 4 个记录器:2 个文件记录器和 2 个文本框记录器。假设一条特定消息适用于 1 个文本框记录器和 1 个文件记录器;我的设计应该如何处理这个?

编辑 2:请不要建议现有的日志框架。我只想自己写!

编辑3:好的。我有一个设计。请提供您的反馈,并可能填补空白。

修改后的界面:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void Log(int loggerIds, string msg);
    public void Log(int loggerIds, string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public int getLoggerId();
}

public enum LoggerType
{
    File,
    TextBox
};

public class LoggerFactory
{
    public Logger getLogger(LoggerType loggerType)
    {

    }
}

LoggerFactory 类将是实例化记录器的唯一方法。此类将为记录器的每个实例分配一个唯一的 ID。这个唯一的 id 将是 2 的幂。例如,第一个记录器将获得 id 1,第二个将获得 id 2,第三个将获得 4,第四个将获得 8,依此类推。

返回的记录器对象可以被类型转换为特定的类,调用者可以设置文件路径、文本框等进一步的值,或者我可以在 LoggerFactory 中有多个方法:一种用于每种类型的记录器,它将接受特定的参数.

因此,假设我们有 4 个记录器,其 ID 为 1、2、4、8。必须使用以下函数记录必须由第一个和第三个记录器(即记录器 ID 1 和 4)处理的特定消息:

    public void Log(int loggerIds, string msg);

要传递给 loggerIds 的值应该是“0101”。每个记录器将检查其记录器 ID 位是否为 ON。如果是,那么它只会记录该消息。

现在在函数签名中,我已经提到了 int 类型,但是执行位操作和比较的具体优化类型是什么?

在这种方法中,最大数量可能会受到限制。记录器,但这对我来说很好。请提供您的反馈。

注意:目前我仍在使用 .NET 2.0。如果可能,建议 .NET 2.0 中的解决方案,否则很好,我可以移动到更高版本。

这种设计的缺点:每个需要记录的类都需要知道应用程序实例化的所有可用记录器,并相应地设置位模式。任何想法如何进行松散耦合的设计?

4

6 回答 6

8

您为什么不查看(或确实使用)现有的日志框架,例如log4netNLog

它们具有日志级别(例如跟踪、信息、错误等)的概念,并且能够按日志名称(通常是调用日志调用的完全限定类型名称)进行过滤。然后,您可以将它们映射到一个或多个“目标”。

于 2012-12-28T09:35:58.850 回答
2

“请不要推荐现有的日志框架。我只想自己写!”

接受的答案:不要重新发明轮子!使用这个现有的日志框架!

掌心

最好的答案,也就是我的答案,是这样的。如果您想要即插即用功能,请使用接口。您可以轻松配置它。这是高层的失败。

  1. 使用配置文件来指示您想要实现日志接口的记录器类型

  2. 使用反射来实例化您在运行时从配置文件中提取的类型。

  3. 传入您刚刚通过类中的构造函数注入创建的记录器接口。

您不会通过界面设计来重新发明轮子。如果您使您的界面足够通用,那么它是非特定的实现(理想情况下)。这意味着如果 log4net 废话,或者不再受支持,您不必撕掉并修改所有您的调用代码。这就像将一盏灯直接连接到你的房子里以打开它。看在上帝的份上,请不要那样做。接口定义了组件交互的契约,而不是实现。

我唯一能想到的就是,看看现有的日志框架,找到共同的元素,然后把你的界面写成共同特征的交集。显然,您会错过一些功能。这取决于你想要多大的灵活性。您可以使用 Log4net 或 Microsoft Event Viewer 记录器,或两者都使用!没有重新实施实施细节。与将代码中的所有内容都绑定到一个技术/框架相比,它是一个耦合度更低的系统。

于 2016-02-08T16:09:14.580 回答
1

正如 devdigital 所写,这些框架通常通过提供指定的日志记录方法来做到这一点,例如:Warn("...")、Fail("...")...

您还可以查找城堡项目的日志记录工具ILogger的接口。(也许尝试谷歌 ILogger.cs 源代码)

如果您仍然坚持使用通用接口链接记录器的方法(您还必须为此实现链接机制),则必须为 Log() 方法提供一种日志记录级别。这可能只是一个整数或枚举。

像这样:

    public interface Logger
    {
        public void Log(LogLevel level, string msg);
        public void Log(LogLevel level, string msgType, string msg);
        public void InitLogSession();
        public void EndLogSession();
        public void AddLogger(Logger chainedLogger);
        public void RemoveLogger(Logger chainedLogger);
    }

使用这样的日志记录级别枚举:

public enum LogLevel
{
    Info,
    Warn,
    Debug,
    Error,
    Fail
}

然后将在责任链中选择要使用的记录器。

于 2012-12-28T09:51:50.230 回答
1

前段时间我写了自己的记录器。老实说,它不如免费提供的那么好,我意识到我正在尝试重新发明一个已经是圆的轮子!

我看到您想编写自己的代码,但查看开源解决方案并可能根据您自己的特定需求使用或修改它们可能仍然是一个想法

我现在使用 TracerX: http: //www.codeproject.com/Articles/23424/TracerX-Logger-and-Viewer-for-NET 这是一个开源项目,因此可以轻松修改所需的源代码。提到的其他记录器当然也很好。

编辑

这是基于对我的问题的公认答案:如何在松散耦合的应用程序中将状态信息传递给 GUI 所以我声称这没有独创性。目前我认为您的日志消息很简单

我建议的答案是,您使用的消息类型可以处理(例如)根据运行时传递给它的某些逻辑将自身发送到不同的记录器,或者使用工厂根据运行时条件创建不同的消息类型。

所以

  1. 创建具有处理方法的抽象消息类或接口。
  2. 创建许多从抽象类或接口继承的消息类型,它们代表您要执行的不同类型的日志记录。处理方法可以确定将它们发送到哪里。
  3. 考虑使用工厂在运行时创建您需要的消息类型,这样您就无需提前决定需要哪些类型
  4. 当您生成日志消息时,使用进程消息将消息路由到您希望它转到的记录器
于 2012-12-28T10:36:27.657 回答
0

这似乎是使用扩展方法的好地方。

创建您的基类,然后为其创建扩展方法

BaseLogger(LogMessage).toTextBoxLog().toFileLog().toDatabaseLog().

这样,您总是在需要时调用扩展方法,BaseLogger然后只调用扩展方法

于 2013-07-30T20:29:06.417 回答
0

.NET 现在提供了 ILogger 接口,该接口可以通过依赖注入与各种 .NET 或 3rd 方日志记录工具一起使用。

使用它,您可以将代码中的日志记录功能与架构中的具体实现分开,并且以后可以在不对业务代码进行重大修改的情况下更换记录器。

https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger?view=dotnet-plat-ext-6.0

https://docs.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line

于 2021-12-25T03:45:05.237 回答