基于.Net Remoting的应用程序日志
 

2009-11-18 作者:rickie 来源:rickie的BLOG

 

本文对《AppLogger, a Simple Distributed Application Logger - Part 1, By sebma, http://www.codeproject.com/csharp/MaAppLogger.asp#xx870711xx》一文进行了分析,并在此基础上进行功能扩展(DBLogger & EmailLogger),同时将Remote Object移植到IIS环境。如果将Remote Object移植到IIS环境中,需要修改FileLogger,另外还会出现许多相关的问题。下面针对这些问题的原因及其解决办法分别进行介绍。

一般而言,在实际的应用系统中,由IIS承载Remote Object比较容易维护和管理,并且也比较稳定。关于如何将Remote Object部署在IIS下,可以参考《如何在IIS中承载远程对象》一文。

经过修改后的Application Logger based on .Net Remoting总体架构如下:

Client & Server Side共享AppRemotingLogger::BusinessFacade。Client只需要引用AppRemoting.BusinessFacade.dll,任何在BusinessRules和DataAccess层的修改对Client端没有影响。Server端可以根据需要修改Logger的类型。

注:AppRemotingLoggerHelp Class移到到Implementation/Client层,AppRemotingLoggerHelp实现对Remote Object的调用,并封装对各种类型Message的log操作,如Exception, Error, Warning, Info and Debug(Debug Message只有在调试阶段才记录日志)。

1. 主要类库分析

(1)BusinessRules Layer的Logger

FileLoger – 记录日志文件(text file)到指定的目录,每个Application有对应的日志文件。

EventLoger – 日志输出到Server端Event Viewer,个人觉得不是很实用。

DebugLogger - 记录日志输出到DebugView(最后的参考文档处提供有下载URL),如果Remote Object部署在IIS环境下,则失效。

ConsoleLogger – 如果Remote Object部署在IIS环境下,则失效。如果Remote Object由EXE承载,ConsoleLogger比较方便调试。

下面2个logger是我自己加进去的,代码比较简单,就不贴出来了。

EmailLogger – 以邮件的方式输出日志到指定的receiver。

DBLogger – 将日志记录到指定数据库的数据表中,个人觉得这个logger比较实用。

DefaultAppRemotingLogger – 实现IappLogger接口,调用LoggerFactory,并根据Web.config文件的设置生成Logger实体。

DefaultHome Class实现Ihome接口,并且是Client可以访问的Remote Object。从如下Web.config文件中可以看到:

<wellknown

mode="SingleCall"

type="AppRemotingLogger.BusinessRules.DefaultHome, AppRemotingLogger.BusinessRules"

objectUri="AppRemotingLogger.IHome.soap" />

DefaultHome Class中GetAppLogger()方法调用DefaultAppRemotingLogger,返回Logger实例。

DefaultAppRemotingLogger Class调用LoggerFactory,并根据Web.config中

<add key="AppRemotingLogger.Type" value="AppRemotingLogger.BusinessRules.FileLogger" />

的设定,生成Logger对象。

另外,DefaultAppRemotingLogger Class中LogMessage()方法通过使用线程池来实现异步记录日志,避免造成系统瓶颈。

2. 如何调试部署在IIS中的Remote Object

为了调试部署在IIS中的Remote Object,需要将aspnet_wp进程附加进来(attach),这个在开发测试过程中非常重要。具体过程如下:

(1)在client端设置断点,并进入调试状态。

(2)点击”Tools/Debug Processes…”,附加aspnet_wp进程。如下图所示:

注意,其中Name根据IIS服务运行的机器,选择/输入相应的computer name。

3. 需要注意的若干问题

DBLogger & EmailLogger的coding比较简单,这里省略了。不过在调试过程中,需要注意如下一些问题。

(I)EmailLogger可能存在“Could not access ‘CDO.Message’ object”异常

导致的原因及其解决办法:

(1)SmtpServer等参数设置有问题,确保参数设置正确。

(2)因为Web.Mail需要调用CDOSYS.dll,有可能CDOSYS.dll没有注册好,可通过regsvr32 /u 先取消注册,然后在用regsvr32在注册一次。

(3)如问题仍没有解决,可以尝试修改

C:\WINNT\Microsoft.NET\Framework\v1.1.4322\CONFIG目录下的machine.config文件,并将<processModel> Section中的UserName属性设置为SYSTEM。

其实,我使用默认的UserName=machine也可以成功的发送email。

(II)EventLogger可能存在“Requested registry access is not allowed”异常

导致的原因及其解决办法:

ASP.Net账号没有足够的权限写Event List,只要将machine.config中的UserName属性设置为SYSTEM就可以解决了。不过,EventLogger其实不是很实用。

(III)FileLogger可能存在“Access to the path c:\winnt\system32\<filename> is denied”异常

导致的原因及其解决办法:

FileLogger默认将日志记录在c:\winnt\system32\目录,ASP.NET帐户根本就没有对该目录的访问权限。因此,需要修改DataAccess项目中的LogFileDataWriter.cs文件。

public LogFile(string strFileName)

{

       string strFileLoggerPath = ConfigurationSettings.AppSettings["FileLoggerPath"];

       if(strFileLoggerPath == null || strFileLoggerPath.Trim().Length == 0)

       {

                   strFileLoggerPath = @"c:\Inetpub\wwwroot";

       }

 

       strFileName = @strFileLoggerPath + strFileName;

       m_objFileStream = new FileStream(strFileName,

                   FileMode.Append,

                   FileAccess.Write,

                   FileShare.Read);

}

明确指定日志文件存放的路径"FileLoggerPath"(在web.config文件<appSettings>中设定),同时需要给予ASP.NET帐户对该目录的Full Control权限。

(IV)DebugLogger在IIS环境中失效,不过没有Exception抛出

的确如此,这个问题没有找到解决办法。不过关系不大,不使用DebugLogger就可以了。

DebugView可以从http://www.sysinternals.com/ntw2k/freeware/debugview.shtml下载。

我在测试EXE承载的Remote Object时,DebugView可以捕获到Trace消息,一切运行正常。但当Remote Object部署在IIS中后,DebugView就无法捕获到Trace消息了。

通过断点测试,Trace.WriteLine(objBuilder.ToString())语句是正确执行的,没有抛出异常。

(V)The remote server returned an error: (407) Proxy Authentication Required异常.

导致的原因及其解决办法:

一般而言是由于IE中设置采用Proxy,如果Disable IE proxy settings,应该就没有问题了。先测试一下,如果可行,则通过如下的code告诉HttpChannel不要访问Proxy Server Settings,来彻底上述解决。

HttpChannel theChannel = (HttpChannel)ChannelServices.GetChannel("http");

theChannel.Properties["proxyName"] = null;

将上面的2行代码放在获取Remote Object的代码后面。

我采用Activator.GetObject()方法来获取Remote Object,上述代码会出现Cast错误。对上述代码稍做修改如下:

System.Runtime.Remoting.Channels.Http.HttpClientChannel theChannel = (System.Runtime.Remoting.Channels.Http.HttpClientChannel) ChannelServices.GetChannel("http");

theChannel.Properties["proxyName"] = null;

同样将上面的2行代码放在获取Remote Object的代码后面就可以了。

(VI)The underlying connection was closed: Unable to connect to the remote server异常.

可以从如下几个方面进行检测:

(1)确认网络正常,并且在Client端可以通过IE访问到Remote Object。

(2)确认Client的应用程序的配置文件URL正确访问Remote Object。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织