本文对《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。
|