在SQL Runner网站上,有人提出了一个需求,希望能够创建一个包含所有应用程序执行脚本的统一文件。认为log4Net库可以很容易地实现这个功能,而不需要对应用程序进行太多修改。本文将介绍为了实现这个功能所做的改动。
本文的源代码可以在SQL Runner源代码库中找到,这些修改自2.0.1.3 RC1版本起可用。
log4Net库在SQL Runner图形界面应用程序中用作监控机制。命令行版本使用它来显示脚本执行的进度。记录器在应用程序的配置文件中设置:
SQLRunner.exe.config
首先,需要定义一个新的配置节:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
...
</configSections>
...
</configuration>
接下来,需要决定想要拥有的附加器类型。log4Net允许将日志请求打印到多个目的地。在log4Net中,输出目的地被称为附加器。在SQL Runner的情况下,找到了两个附加器:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
...
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level - %message%newline" />
</layout>
</appender>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender,log4net">
<param name="File" value="logs\\sqlrunner.log" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%date [%thread] %-5level - %message%newline" />
</layout>
</appender>
...
</log4net>
...
</configuration>
ConsoleAppender直接打印到控制台,而LogFileAppender打印到名为sqlrunner.log的文件中。
接下来要定义的是记录器。在log4Net中,记录器是通过给它们命名来创建层次结构的。这种命名约定与类中的命名空间非常相似。一个好主意是在使用记录器时使用类的完全限定名。
根记录器是所有记录器的父记录器,配置文件中需要为它分配一个级别:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
...
<log4net>
...
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="LogFileAppender" />
</root>
...
</log4net>
...
</configuration>
根的级别设置为INFO,并且使用了两个附加器。因此,任何使用INFO级别或更高级别调用记录器的代码都会打印到两个记录器中。
这里的想法是创建一个新的附加器,每次执行脚本时都打印到一个新的文件:allscripts.sql。还需要创建一个新的记录器。最后,需要修改代码;只需要添加两行新代码就可以让一切工作。
配置文件需要更改为:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
...
<log4net>
<appender name="ScriptConsolidator" type="log4net.Appender.FileAppender,log4net">
<param name="File" value="logs\\allscripts.sql" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%message%newline" />
</layout>
</appender>
...
<logger name="SQLRunnerLib.Runners.Runner">
<level value="DEBUG" />
<appender-ref ref="ScriptConsolidator" />
</logger>
...
</log4net>
...
</configuration>
需要注意的几个方面是:
只需要两行代码就可以创建合并的脚本文件。在SQL Runner中,Runner类在调用ExecuteSQL方法时执行脚本。
需要创建一个调用工厂方法GetLogger的ILog的本地实例。传递Runner类的完全限定类名来获取记录器:
public sealed class Runner : IRunner
{
#region Private Instances
...
private readonly ILog _logger = LogManager.GetLogger(typeof(Runner));
...
#endregion
}
然后,需要做的只是在执行脚本之前调用记录器:
public sealed class Runner : IRunner
{
...
private void ExecuteSQL(FileInfo aFile)
{
if (IsCancelled) return;
try
{
OnProgressMsgCreated("Executing {0} script", aFile.Name);
using (StreamReader aStream = new StreamReader(aFile.OpenRead()))
{
try
{
string strSQL = aStream.ReadToEnd();
aStream.Close();
strSQL = ReplacePlaceholders(strSQL, aFile.Name);
_logger.Debug(strSQL);
...
}
catch (Exception e)
{
...
}
}
}
catch (Exception e)
{
...
}
}
...
}