如何登录T-SQL
我正在使用ADO.NET来访问SQL Server 2005,并且希望能够从我正在调用的T-SQL存储过程中进行记录。有可能吗?
使用ADO.NET时,我看不到'print'语句的输出,因为我只想使用日志记录进行调试,所以理想的解决方案是从SysInternals向DebugView发送消息。
解决方案
回答
我们可以通过简单地插入新行来登录到表,也可以实现CLR存储过程来写入文件。
写入表时要小心,因为如果操作在事务中发生并且该事务被回滚,则日志条目将消失。
回答
从SQL sproc内部进行记录最好对数据库本身进行记录。 T-SQL可以写入文件,但并不是真正为它设计的。
回答
我认为写日志表是我的偏爱。
另外,在使用2005时,我们可以编写一个简单的SQLCLR过程来包装EventLog。
或者,如果我们想写入SQL日志,则可以使用xp_logevent
回答
有PRINT命令,但我更喜欢登录表以便查询。
回答
我们可以使用输出变量来传递消息,但这依赖于proc执行而没有错误。
create procedure usp_LoggableProc @log varchar(max) OUTPUT as -- T-SQL statement here ... select @log = @log + 'X is foo'
然后在ADO代码中:
string log = (string)SqlCommand.Parameters["@log"].Value;
我们可以使用raiserror来创建自己的自定义错误,以及所需的信息,这些信息可以通过ADO代码中的常规SqlException Errors集合提供给我们:
RAISERROR('X is Foo', 10, 1)
嗯,但是,是的,不禁感到自己只是调试而已,在我们所处的情况下,只需将varchar消息插入错误表中即可,就像其他人建议的那样,并在调试时从中选择*。
回答
我们可以从存储过程中将行写入日志表。正如其他人指出的那样,我们可以使用CLR或者xp_logevent尽力写一些文本文件或者其他日志,但是似乎我们需要的容量超出了实际使用的容量。
当事务失败时,就会出现棘手的情况(这正是我们真正需要的日志)。由于在这些事务期间发生的任何日志记录都会与其所属的事务一起回滚,因此最好拥有一个日志API,客户端可以使用该API记录错误。这可以是一个简单的DAL,可以记录到同一数据库或者共享的DAL中。
回答
-以下DDL sql将创建用于存储日志数据的表
使用[db]
去
/****** Object: Table [dbo].[tbData_Debug] Script Date: 02/12/2009 22:30:03 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tbData_Debug]( [colTimeStamp] [timestamp] NULL, [colNiceTime] [varchar](200) NULL, [colDomain_User] [varchar](200) NULL, [colMsg] [varchar](4000) NULL, [colDebugLevel] [int] NULL, [colDebugMsg] [varchar](4000) NULL, [colPageName] [varchar](200) NULL, [colClassName] [varchar](200) NULL, [colMethodName] [varchar](200) NULL, [colMethodNameGui] [varchar](4000) NULL, [colRet] [int] NULL, [colLineNumber] [int] NULL, [colLineNumberGui] [int] NULL, [colProcedureName] [varchar](200) NULL, [colProcedureStep] [varchar](4000) NULL ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO -- This stored procedure does write to the log table USE [db] GO /****** Object: StoredProcedure [dbo].[procUtils_AppDebug] Script Date: 02/12/2009 22:29:24 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[procUtils_AppDebug] ( @ret int = null OUT, @msgIn varchar(4000) = null , -- the msg which the user has seen @msgOut varchar(4000) = null OUT , -- the msg which the user has seen @domain_user varchar(200) = null , -- the domain user echoing the message @debugMsgIn varchar(4000) = null , -- the debug msg for internal use @debugMsgOut varchar(4000) = null OUT, -- the debug msg for internal use @pageName varchar(200) = null , -- the pageName originator of error @className varchar(200) = null , -- the class Name orinator of error @methodName varchar(200) = null , -- the methodName where the last error occured @methodNameGui varchar(4000) = null , -- the methodNameOfTheGui where the last error occured @lineNumber int = null , -- the line number of the line issueing the error @lineNumberGui int = null, -- the line number of the line issueing the error @procedureName varchar(200) = null , -- the procedureName currently envoked @procedureStep varchar(4000) = null -- the steps of the procedure concatenated ) AS BEGIN -- proc start SET NOCOUNT ON; BEGIN TRY --begin try declare @debugLevel int select @debugLevel = Debug_Level from User_tb where Domain_Name = @domain_user /* select * from tbData_Debug order by 1 desc delete from tbData_Debug */ insert into tbData_Debug ( colNiceTime , colDomain_User , colMsg , colDebugLevel , colDebugMsg , colPageName , colClassName , colMethodName , colMethodNameGui , colRet , colLineNumber , colLineNumberGui , colProcedureName , colProcedureStep) values ( dbo.funcGetNiceTime() , @domain_user , @msgIn , @debugLevel ,@debugMsgIn , @pageName , @className , @methodName ,@MethodNameGui , @ret , @lineNumber , @lineNumberGui , @procedureName , @procedureStep) set @debugMsgOut = @debugMsgIn set @msgOut = 'Action Registered' set @ret = @@ERROR return @ret END TRY --end try BEGIN CATCH PRINT 'In CATCH block. Error number: ' + CAST(ERROR_NUMBER() AS varchar(10)) + ' Error message: ' + ERROR_MESSAGE() + ' Error severity: ' + CAST(ERROR_SEVERITY() AS varchar(10)) + ' Error state: ' + CAST(ERROR_STATE() AS varchar(10)) + ' XACT_STATE: ' + CAST(XACT_STATE() AS varchar(10)); set @debugMsgOut = 'error at [procUtils_AppDebug]--- Error number: ' + CAST(ERROR_NUMBER() AS varchar(10)) + 'Error message: ' + ERROR_MESSAGE() + 'Error severity: ' + CAST(ERROR_SEVERITY() AS varchar(10)) + 'Error state: ' + CAST(ERROR_STATE() AS varchar(10)) + 'XACT_STATE: ' + CAST(XACT_STATE() AS varchar(10)) set @msgIn= 'error while saving application error info into database' insert into tbData_Debug ( colMsg ) values ( @msgIn ) set @debugMsgOut = @debugMsgIn + @debugMsgOut set @msgOut = 'Action Registration failed' set @ret = 1 END CATCH return @ret END --procedure end /* <procedureDocumentation> <procedurename>procUtils_AppDebug<procedurename> <procedureDescription> Records events from the Application Layer </procedureDescription> <created>20090121</created> <createdby>Yordan Georgiev</createdby> <change> <changewhen><changewhen> <changeDescription></changeDescription> <changedBy></changedBy> </change> <testUsage> USE [db] GO DECLARE @return_value int, @ret int, @msgIn varchar(max), @debugmsg varchar(4000) SELECT @ret = 1 SELECT @msgIn = N'msg' SELECT @debugmsg = N'before' EXEC @return_value = [dbo].[procUtils_AppDebug] @ret = @ret OUTPUT, @msgIn = @msgIn OUTPUT, @domain_user = N'domain_user', @debugmsg = @debugmsg OUTPUT, @methodName = N'methodName' SELECT @ret as N'@ret', @msgIn as N'@msgIn', @debugmsg as N'@debugmsg' SELECT 'Return Value' = @return_value select * from tbData_Debug order by 1 desc GO </testUsage> </procedureDocumentation> */
回答
我通过按照Eric Z Beard的建议编写一个SQLCLR过程来解决此问题。程序集必须使用强名称密钥文件签名。
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static int Debug(string s) { System.Diagnostics.Debug.WriteLine(s); return 0; } } }
创建了一个密钥和一个登录名:
USE [master] CREATE ASYMMETRIC KEY DebugProcKey FROM EXECUTABLE FILE = 'C:\..\SqlServerProject1\bin\Debug\SqlServerProject1.dll' CREATE LOGIN DebugProcLogin FROM ASYMMETRIC KEY DebugProcKey GRANT UNSAFE ASSEMBLY TO DebugProcLogin
将其导入到SQL Server:
USE [mydb] CREATE ASSEMBLY SqlServerProject1 FROM 'C:\..\SqlServerProject1\bin\Debug\SqlServerProject1.dll' WITH PERMISSION_SET = unsafe CREATE FUNCTION dbo.Debug( @message as nvarchar(200) ) RETURNS int AS EXTERNAL NAME SqlServerProject1.[StoredProcedures].Debug
然后,我能够使用以下命令登录T-SQL过程
exec Debug @message = 'Hello World'
回答
对于它的价值,我发现当我没有为我的SqlConnection分配InfoMessage处理程序时:
sqlConnection.InfoMessage += new SqlInfoMessageEventHandler(MySqlConnectionInfoMessageHandler);
InfoMessageHandler的签名如下所示:
MySqlConnectionInfoMessageHandler(object sender, SqlInfoMessageEventArgs e)
那么我的存储过程中的PRINT语句就不会出现在DbgView中。
回答
我们可能要检查Log4TSQL。它提供了SQL Server 2005 2008中存储过程和触发器的数据库日志记录。我们可以在每个过程/触发器的基础上设置独立的独立日志级别。