c#中灵活的日志接口设计

本文关键字:接口 日志 | 更新日期: 2023-09-27 17:49:40

我想写我自己的日志类(c#),实现一个标准的接口,我可以从代码的任何部分调用。

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

此外,每个记录器类可以有一个嵌套的记录器或链接的记录器类,这样从应用程序代码中调用Log()方法就可以在多个目的地记录消息;示例日志到一个文件和窗体上的文本框在一个单独的调用。

我面临的困难是:

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

当我调用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,以此类推。

返回的记录器对象可以被类型转换为特定的类,并且可以由调用者设置诸如filePath, textbox等进一步的值,或者我可以在LoggerFactory中有多个方法:每种记录器类型一个,它将接受特定的参数。

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

    public void Log(int loggerIds, string msg);

传递给loggerIds的值应该是"0101"。每个记录器将检查其记录器id位是否为ON。如果是,只有这样它才会记录消息。

现在在函数签名中,我提到了int类型,但是哪一种是执行位操作和比较的特定优化类型?

在这种方法中,可能存在最大no的限制。伐木工人,但我没意见。请给出你的反馈。

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

这个设计的缺点:每个需要记录日志的类,需要知道应用程序实例化的所有可用的记录器,并相应地设置位模式。对如何实现松耦合设计有什么想法吗?

c#中灵活的日志接口设计

您为什么不看看(或确实使用)现有的日志框架,如log4net或NLog呢?

它们具有日志级别(例如trace, info, error等)的概念,并且能够通过日志名称进行过滤(通常是调用日志调用的完全限定类型名称)。然后您可以将它们映射到一个或多个"目标"。

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

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

facepalm指

最好的答案,也就是我的答案,是这样的。如果您想要即插即用功能,请使用接口。您可以使其易于配置。这是高阶运行
  1. 使用配置文件来指示您想要的日志记录器类型实现你的日志接口

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

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

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

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

正如devdigital所写的,这些框架通常通过提供指定的日志方法来实现这一点,例如:Warn("…"),Fail("…")…

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

如果您仍然坚持使用带有公共接口的链式记录器的方法(为此您还必须实现链接机制),则必须为您的Log()方法提供一种日志级别。可以是整型,也可以是enum。

:

    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);
    }

使用如下的日志级别enum:

public enum LogLevel
{
    Info,
    Warn,
    Debug,
    Error,
    Fail
}
然后在责任链中选择要使用的记录器。

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

我知道你想写自己的代码,但它可能仍然是一个想法,看看开源的解决方案,也许使用它们或修改它们为您自己的特定需求

我现在使用TracerX: http://www.codeproject.com/Articles/23424/TracerX-Logger-and-Viewer-for-NET这是一个开源项目,所以很容易修改源代码,如果你需要。上面提到的其他记录器当然也很好。

编辑

这是基于对我的问题的公认答案:如何在松散耦合的应用程序中将状态信息传递给GUI。我认为你的日志信息现在很简单

我的建议答案是,你使用一个消息类型,可以处理(例如)发送自己到不同的记录器基于一些逻辑传递给它在运行时,或通过使用工厂创建不同的消息类型取决于运行时条件。

  1. 创建具有进程方法的抽象消息类或接口。
  2. 创建许多从抽象类或接口继承的消息类型,这些类型代表您想要执行的不同类型的日志记录。进程方法可以决定将它们发送到哪里。
  3. 考虑使用工厂来创建运行时需要的消息类型,这样你就不需要提前决定需要什么类型
  4. 当您生成日志消息时,使用进程消息将消息路由到您希望它到达的记录器

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

创建基类,然后为它创建扩展方法
BaseLogger(LogMessage).toTextBoxLog().toFileLog().toDatabaseLog().
这样,您总是调用BaseLogger,然后只在需要的地方调用扩展方法

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

使用它,您可以将代码中的日志功能从架构中的具体实现中分离出来,并且可以在稍后更换日志记录器,而无需对业务代码进行重大修改。

https://learn.microsoft.com/en - us/dotnet/api/microsoft.extensions.logging.ilogger?view=dotnet平台- ext - 6.0

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