PDF.NET的SQL日志 ASP.net 路径问题 详解

PDF.NET(PWMIS数据开发框架)是一个基于SQL-MAP(将SQL语句映射成数据访问代码)和OQL(对象化的SQL查询表达式)技术的数据开发框架,兼有SQL和ORM的特点。尽管有些数据库系统能够提供SQL监视,比如SQLSERVER的事务日志跟踪,但好多数据库都没有提供这样的功能,所以很多数据(或者ORM)框架都会有一个记录执行的SQL日志的功能,PDF.NET也不例外,下面讲讲它是怎么实现的。

1,SQL日志功能代码介绍

PDF.NET 底层访问数据库使用的是兼容MS DAAB 3.1的AdoHelper,它在最终执行Command命令查询的时候,调用了 CommandLog对象,而这个类内部有一个Stopwatch对象,用于精确记录执行时间。

private System.Diagnostics.Stopwatch watch = null;
        /// <summary>
        /// 是否开启执行时间记录
        /// </summary>
        /// <param name="startStopwatch"></param>
        public CommandLog(bool startStopwatch)
        {
            if (startStopwatch)
            {
                watch = new System.Diagnostics.Stopwatch();
                watch.Start();            
            }
        }

CommandLog类公开一个WriteLog方法,记录Ado.Net的Command对象的执行情况:

/// <summary>
        /// 写命令日志和执行时间(如果开启的话)
        /// </summary>
        /// <param name="command">命令对象</param>
        /// <param name="who">调用命令的源名称</param>
        public void WriteLog(IDbCommand command,string who)
        {
            CommandText = command.CommandText;
            if (SaveCommandLog)
            {
                if (watch != null)
                {
                    if ((LogExecutedTime > 0 && watch.ElapsedMilliseconds > LogExecutedTime) || LogExecutedTime==0)
                    {
                        RecordCommandLog(command, who);
                        WriteLog("Execueted Time(ms):" + watch.ElapsedMilliseconds + "\r\n", who);
                    }
                }
                else
                {
                    RecordCommandLog(command, who);
                }
            }
        }

在CommandDB类(AdoHelper的基类)的相关数据访问方法内,如下调用CommandLog类:

/// <summary>
        /// 根据查询返回数据阅读器对象
        /// </summary>
        /// <param name="SQL">SQL</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="cmdBehavior">对查询和返回结果有影响的说明</param>
        /// <param name="parameters">参数数组</param>
        /// <returns>数据阅读器</returns>
        protected virtual IDataReader ExecuteDataReader(ref string SQL, CommandType commandType, CommandBehavior cmdBehavior,ref IDataParameter[] parameters)
        {
            IDbConnection conn=GetConnection();
            IDbCommand cmd=conn.CreateCommand ();
            CompleteCommand(cmd,ref SQL,ref commandType,ref parameters);
            CommandLog cmdLog = new CommandLog(true);
            IDataReader reader=null;
            try
            {
                //如果命令对象的事务对象为空,那么强制在读取完数据后关闭阅读器的数据库连接 2008.3.20
                if(cmd.Transaction ==null && cmdBehavior==CommandBehavior.Default )
                    cmdBehavior=CommandBehavior.CloseConnection ;
                reader = cmd.ExecuteReader (cmdBehavior);
            }
            catch(Exception ex)
            {
                ErrorMessage=ex.Message ;
                //只有出现了错误而且没有开启事务,可以关闭连结
                if(cmd.Transaction==null && conn.State ==ConnectionState.Open )
                    conn.Close ();
                
                bool inTransaction = cmd.Transaction == null ? false : true;
                CommandLog.Instance.WriteErrLog(cmd, "AdoHelper:" + ErrorMessage);
                if (OnErrorThrow)
                {
                    throw new QueryException(ex.Message, cmd.CommandText, commandType, parameters, inTransaction, conn.ConnectionString);
                }
            }
            cmdLog.WriteLog(cmd, "AdoHelper");
            return reader;
        }

2,SQL日志功能使用配置

使用SQL日志很简单,只需要在应用程序配置文件中做如下配置即可,注意看配置中的注释:

<!--PDF.NET SQL 日志记录配置(for 4.0)开始
        记录执行的SQL语句,关闭此功能请将SaveCommandLog 设置为False,或者设置DataLogFile 为空;
        如果DataLogFile 的路径中包括~符号,表示SQL日志路径为当前Web应用程序的根目录;
        如果DataLogFile 不为空且为有效的路径,当系统执行SQL出现了错误,即使SaveCommandLog 设置为False,会且仅仅记录出错的这些SQL语句;
        如果DataLogFile 不为空且为有效的路径,且SaveCommandLog 设置为True,则会记录所有的SQL查询。
        在正式生产环境中,如果不需要调试系统,请将SaveCommandLog 设置为False 。
    -->
    <add key="SaveCommandLog" value="True"/>
    <add key="DataLogFile" value="~\SqlLog.txt"/>
    <!--LogExecutedTime 需要记录的时间,如果该值等于0会记录所有查询,否则只记录大于该时间的查询。单位毫秒。-->
    <add key="LogExecutedTime" value ="300"/>
    <!--LogBufferCount 日志信息缓存的数量,如果该值等于0会立即写入日志文件,默认缓存20条信息;注意一次查询可能会写入多条日志信息-->
    <add key="LogBufferCount" value ="20"/>
<!--PDF.NET SQL 日志记录配置 结束-->

注意:日志路径可以使用ASP.NET的服务器路径符号“~”,该符号的具体使用说明是:

ASP.NET 包括了 Web 应用程序根目录运算符 (~),当您在服务器控件中指定路径时可以使用该运算符。ASP.NET 会将 ~ 运算符解析为当前应用程序的根目录。可以结合使用 ~ 运算符和文件夹来指定基于当前根目录的路径。 下面的示例演示了使用 Image 服务器控件时用于为图像指定根目录相对路径的 ~ 运算符。在此示例中,无论页面位于网站中的什么位置,都将从位于 Web 应用程序根目录下的 Images 文件夹中直接读取图像文件。 <asp:image runat="server" id="Image1" ImageUrl="~/Images/SampleImage.jpg" />可以在服务器控件中的任何与路径有关的属性中使用 ~ 运算符。~ 运算符只能为服务器控件识别,并且位于服务器代码中。不能将 ~ 运算符用于客户端元素。

详细内容请看

ASP.net 路径问题 详解

3,查看SQL日志文件

根据配置文件中配置的SQL日志地址,我们查看一下它的内容,看它到底记录了什么内容:

//2011/5/9 14:48:42 @AdoHelper 执行命令:
SQL="SELECT * FROM [JJGaiKuang] (@fundCompany,@fundType,@IsConsignment,@managerID,@openState,@bankName,@Tzfg)"
//命令类型:Text
//7个命令参数:
Parameter["@fundCompany"]    =    ""              //DbType=AnsiString
Parameter["@fundType"]    =    ""              //DbType=AnsiString
Parameter["@IsConsignment"]    =    "是"              //DbType=AnsiString
Parameter["@managerID"]    =    ""              //DbType=AnsiString
Parameter["@openState"]    =    ""              //DbType=AnsiString
Parameter["@bankName"]    =    "中国银行"              //DbType=AnsiString
Parameter["@Tzfg"]    =    ""              //DbType=AnsiString
//2011/5/9 14:48:42 @AdoHelper :Execueted Time(ms):607

//2011/5/9 14:48:59 @AdoHelper 执行命令:
SQL="SELECT * FROM [GetFundTrend_FundAnalysis_FundFeat] (@currentJJDM,@OtherJJDM)"
//命令类型:Text
//2个命令参数:
Parameter["@currentJJDM"]    =    "KF0003"              //DbType=AnsiString
Parameter["@OtherJJDM"]    =    "000001,399001,H11020,000300"              //DbType=AnsiString
//2011/5/9 14:48:59 @AdoHelper :Execueted Time(ms):369

//2011/5/9 14:49:00 @AdoHelper 执行命令:
SQL="SELECT a.id,a.基金名称,round(a.收益率*100,2) 收益率 FROM [GetFundOfTypeNew](@jjdm,@startDate,@endDate,@type) as a"
//命令类型:Text
//4个命令参数:
Parameter["@jjdm"]    =    "KF0003"              //DbType=AnsiString
Parameter["@startDate"]    =    "2011-02-06"              //DbType=AnsiString
Parameter["@endDate"]    =    "2011-05-06"              //DbType=AnsiString
Parameter["@type"]    =    "三个月"              //DbType=AnsiString
//2011/5/9 14:49:00 @AdoHelper :Execueted Time(ms):310

//2011/5/9 14:49:00 @AdoHelper 执行命令:
SQL="SELECT * FROM [GetFundNotice](@jjdm) order by 公告时间 desc"
//命令类型:Text
//1个命令参数:
Parameter["@jjdm"]    =    "KF0003"              //DbType=AnsiString
//2011/5/9 14:49:00 @AdoHelper :Execueted Time(ms):389

从日志文件可以看出,程序记录了详细的SQL信息,包括SQL文本和参数值,还有执行时间,本示例文件中仅仅记录了执行超过300毫秒的查询。

通过框架的SQL日志功能,可以随时打开或者关闭日志,查看日志详细信息,从而为系统性能优化提供依据。

注:本功能的完全支持仅在框架版本 4.1之后,之前的版本不支持 LogExecutedTime 功能和“~”路径功能。

注:日志信息缓存数量配置,需要PDF.NET SOD框架 Ver 5.5.5 版本以后才支持。默认情况下已经开启日志信息缓存,所以如果你需要准确记录全部日志信息,请每次使用AdoHelper对象,调用它的 Dispose 方法,也可以像下面这样:

using(AdoHelper db=MyDB.GetDBHelper())
{
  DataSet result=db.ExecuteDataSet("select * from user");
}

注:在SOD ver 5.6.2.0124 版本后,如果没有做任何日志记录参数的配置,在执行SQL出错的时候,默认会记录这些出错的查询信息到  C:\ProgramData\SODLog 目录。如果配置了

<add key="DataLogFile" value="~\SqlLog.txt"/>

DataLogFile 配置项,假设没有开启日志记录的配置,出错了也会记录错误信息的日志到这个配置项所指定的日志文件中。注意ASP.NET站点上,日志文件必须有写入权限。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术翻译

关于Couchbase-Dzone数据库,你必须了解的10件事情

此功能已经存在了一段时间,但仍值得一提。一些Key-Value Store只允许你将整个文档全部整合在一起,这是一个合理的。但是,如果你使用Couchbase作...

30000
来自专栏数据和云

【循序渐进Oracle】Oracle的逻辑备份与恢复

编辑手记:针对最近发生的炉石及GitLab事件,我们不得不再次强调备份的重要性。DBA的四大守则,第一条就是备份重于一切。年初做好备份,愿你的系统17无恙。 本...

48980
来自专栏散尽浮华

mysql操作命令梳理(5)-执行sql语句查询即mysql状态说明

在日常mysql运维中,经常要查询当前mysql下正在执行的sql语句及其他在跑的mysql相关线程,这就用到mysql processlist这个命令了。 m...

27760
来自专栏c#开发者

分析Oracle数据库日志文件(1)

分析Oracle数据库日志文件(1) 一、如何分析即LogMiner解释 从目前来看,分析Oracle日志的唯一方法就是使用Oracle公司提供的LogMin...

51550
来自专栏码农分享

代理模式(Proxy)

转载 https://blog.csdn.net/lovelion/article/details/8228042

13120
来自专栏Django Scrapy

Django安装及简单使用1.3

Django安装及简单使用1.3 代码都在github: URL:https://github.com/njxshr/codes/tree/master/t...

34680
来自专栏积累沉淀

Python快速学习第九天--安装并操作Mysql数据库

python操作mysql数据库 Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。...

29580
来自专栏chenssy

【死磕Sharding-jdbc】---最大努力型事务

在分布式数据库的场景下,相信对于该数据库的操作最终一定可以成功,所以通过最大努力反复尝试送达操作。

17430
来自专栏芋道源码1024

分布式事务 TCC-Transaction 源码解析 —— 事务存储器

本文主要基于 TCC-Transaction 1.2.3.3 正式版 1. 概述 2. 序列化 2.1 JDK 序列化实现 2.2 Kyro 序列化实现 2.3...

50260
来自专栏NetCore

[实录]解决Migrator.Net 小bug

好久没写了,平时比较忙,只能趁周末的时候,写一点小东西,自己也记录一下。 平时我们做项目的时候,都会有自己的数据访问层,为了能方便以后的升级,我们一般会抽象出数...

25350

扫码关注云+社区

领取腾讯云代金券