AppLogger,一个简单的分布式应用程序日志记录器 - 第 2 部分(使用 MSMQ)






4.71/5 (7投票s)
2004年7月16日
4分钟阅读

68333

1338
简单的分布式应用程序日志记录的结论性文章。
图 1. 计算机管理控制台屏幕截图
引言
在本文的第一部分中,我们介绍了一个快速简便的分布式应用程序日志记录器 (AppLogger)。TCP Remoting 被用作使 AppLogger “分布式”的基础协议。还介绍了使用 BusinessFacade、BusinessRules、DataAccess 和 SystemFrameworks 层来构建应用程序。还提到日志记录应该是一个快速、简短的操作,也就是说,调用者绝不应该处于阻塞状态。“异步”在 AppLogger 的 BusinessRule 层服务器端实现。
在本文中,我们将“重构”AppLogger,使其更加“异步”。事实上,我们希望通过某种单向消息传递机制来实现客户端调用(通过 AppLoggerHelper
API)的异步。再次,我们不从头开始创建消息队列机制,而是采取一些务实的做法。我们将利用操作系统本身(Windows XP、Windows 2000、Windows 2003)提供的 MSMQ。请注意,MSMQ 在安装操作系统时不会自动激活。您需要自己通过“控制面板 -> 添加/删除程序 -> 添加/删除 Windows 组件”来添加它。
要运行演示,只需解压演示文件,然后执行以下操作:
- 从“控制面板 -> 管理工具 -> 计算机管理”,选择“消息队列”,然后添加一个名为“applogger”的私有队列。(请参阅上面的图 1)
- 启动 \AppLoggerDemo2\AppLogger\AppLogger.exe
- 然后运行 \AppLoggerDemo2\AppLoggerTest\AppLoggerTest.exe
背景
在理想情况下,应用程序错误日志记录应该是例外情况,而不是常态。如果有太多的错误和异常需要记录,您的应用程序实际上已经处于降级模式。然而,世界并非如此理想(我们都知道),而且我们甚至常常需要记录调试消息,以让我们确信一切运行正常。
因此,为了不“降低”AppLogger 的性能,我们希望使 AppLoggerHelper 提供的 API 异步化。本质上,AppLoggerHelper 的用户将有效地“入队”他们的日志消息,而 AppLogger 将“出队”并将它们转发给具体的 IAppLogger。一如既往,我们将在过程中接触一些设计模式。
特点
AppLogger 解决方案中的项目添加了以下内容:
BusinessFacade |
已添加对 System.Messaging.dll 的引用。 |
已添加 MsmqHelper 类。 | |
已添加 ProxyMsmqHome 类。 | |
已添加 ProxyMsmqLogger 类。 | |
BusinessRules |
已添加对 System.Messaging.dll 的引用。 |
已添加 MsmqObserver 类。 | |
已添加 MsmqLogger 类。 |
MsmqHelper
类提供了打开 MSMQ 队列的辅助方法。ProxyMsmqHome
(一个具体的 IHome
)和 ProxyMsmqLogger
(一个具体的 IAppLogger
),顾名思义,只不过是 代理 类。“代理设计模式使组件的客户端与一个代表而不是组件本身进行通信”——Frank Buschmann 等人,来自他们的著作《Pattern-Oriented Software Architecture》(也称为 POSA 书)。事实证明,MSMQ 只是我们需要为客户端(AppLoggerTest.exe)和服务器(AppLogger.exe)的 App.Config 文件设置的另一种“传输”协议。
AppLogger.exe 配置
AppLogger.exe 的 App.Config 中新增了一个名为“AppLogger.MSMQ”的键。请注意,“bordeaux”是我的机器名,请将其更改为您的机器名(不一定是“chablis”,只是开玩笑……)。
<appSettings>
<!--
AppLogger.Type allows changing the type
of logger during runtime. Eg. ConsoleLogger,
FileLogger, EventLogger, DbLogger, DebugLogger
-->
<add key="AppLogger.Type"
value="AppLogger.BusinessRules.ConsoleLogger" >
...
<!--
AppLogger.MSMQ is for MsmqLogger only.
Private queue "applogger" must exists in local machine.
Client side must have the following in <appSettings>
<add key="AppLogger.Home.Location"
value="msmq:FormatName:DIRECT=OS:<hostname>\private$\applogger" />
Server side, which is this App.config,
must have the following in <appSettings>
<add key="AppLogger.MSMQ"
value="FormatName:DIRECT=OS:<hostname>\private$\applogger" />
-->
<add key="AppLogger.MSMQ"
value="FormatName:DIRECT=OS:bordeaux\private$\applogger" />
</appSettings>
客户端配置
在 AppLoggerTest 的 App.Config 中,只需更改“AppLogger.Home.Location”键的值即可使用“msmq:”协议。
<appSettings>
<!--
AppLogger.Home.Location refers to the URL of AppLogger's IHome
<add key="AppLogger.Home.Location"
value="tcp://:20911/AppLogger.IHome.rem" />
OR via MSMQ
<add key="AppLogger.Home.Location"
value="msmq:FormatName:DIRECT=OS:<hostname>\private$\applogger" />
-->
<add key="AppLogger.Home.Location"
value="msmq:FormatName:DIRECT=OS:bordeaux\private$\applogger" />
...
</appSettings>
使用代码
MsmqObserver
和 MsmqLogger
类
顾名思义,MsmqObserver
观察(或监听)发送到 MSMQ 中物理“applogger”队列的消息。MsmqLogger 是一个单例,也实现了 IAppLogger
接口,它将通过回调方法 MsmqLogger.NotifyMe(...)
订阅 MsmqObserver
的通知。简而言之,MsmqLogger
要求 MsmqObserver
在收到来自 MSMQ 的消息时通知它。这种典型的 观察者 模式在 Gang-Of-Four 的《设计模式》一书中得到了很好的解释。MsmqLogger
单例在 AppLogger.exe 启动时启动。
此时,值得指出的是,在“设计模式”这个术语成为常用语之前,有一种叫做“惯用法”的东西,由 James O. Coplien 提出,他是评价很高的书籍《Advanced C++ Programming Styles and Idioms》的作者。MsmqLogger
在收到日志消息到达通知后,会将消息 **转发** 给实际的日志记录器(实际的日志记录器是一个已经实现的具体 IAppLogger
,它知道如何持久化日志消息)。这代表了“句柄/体”惯用法或所谓的 桥梁 设计模式。
public class MsmqLogger : IAppLogger
{
/// <summary>
/// Holds this singleton, with eager initialization.
/// </summary>
private static MsmqLogger m_objInstance = new MsmqLogger();
/// <summary>
/// Holds our message Observer.
/// </summary>
private MsmqObserver m_objMsmqObserver;
/// <summary>
/// Holds the real logger used for logging.
/// </summary>
private IAppLogger m_objRealLogger;
/// <summary>
/// Not for public construction.
/// </summary>
private MsmqLogger()
{
Setup();
}
/// <summary>
/// Initializes class members.
/// </summary>
private void Setup()
{
...not shown for simplicity...
}
/// <summary>
/// This is the callback method for MsmqObserver.
/// </summary>
/// The LogMessage object
private void NotifyMe(object obj)
{
System.Messaging.Message objRawMsg = (System.Messaging.Message)obj;
AppLogger.BusinessFacade.LogMessage objActualMsg =
(AppLogger.BusinessFacade.LogMessage)objRawMsg.Body;
LogMessage(objActualMsg); // forwarding to real logger
objActualMsg = null;
objRawMsg = null;
}
/// <summary>
/// Get this singleton.
/// </summary>
/// <returns>A MsmqLogger</returns>
public static MsmqLogger GetInstance()
{
return m_objInstance;
}
/// <summary>
/// IAppLoggger.LogMessage Implementation.
/// </summary>
///
public void LogMessage(LogMessage objMessage)
{
// forwarding to the real logger
m_objRealLogger.LogMessage(objMessage);
}
...
}
最后,得益于代理模式,AppLoggerTest 中没有客户端代码更改。
结论
这个简陋的分布式应用程序日志记录器可以部署在多个应用程序服务器上。然而,在实际应用中,维护多个错误日志文件或搜索多个机器上的多个 Windows 事件日志是一场噩梦。正如本文第一部分所述,您当然可以在 BusinessRules 和 DataAccess 层提供一个 DbLogger(它将日志消息持久化到数据库)。一旦存储在数据库中,跟踪、搜索和过滤就仅仅是 SQL 语句的问题了。
看来,这个“经济型”日志应用程序不仅带我们进入了设计模式的领域,还适应了我们重构后的设计。希望您在阅读本文时和我一样享受写作的乐趣。再次,请继续提出您的建议,继续摇滚!