Cómo iniciar sesión en T-SQL
-
09-06-2019 - |
Pregunta
Estoy usando ADO.NET para acceder a SQL Server 2005 y me gustaría poder iniciar sesión desde dentro de los procedimientos almacenados de T-SQL a los que estoy llamando.¿Es eso posible de alguna manera?
No puedo ver el resultado de la declaración 'imprimir' cuando uso ADO.NET y como quiero usar el registro solo para depurar, la solución ideal sería emitir mensajes a DebugView desde SysInternals.
Solución
Resolví esto escribiendo un procedimiento SQLCLR como sugirió Eric Z Beard.El ensamblado debe firmarse con un archivo de clave de nombre seguro.
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;
}
}
}
Creó una clave y un inicio de sesión:
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
Lo importé a 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
Luego pude iniciar sesión en los procedimientos T-SQL usando
exec Debug @message = 'Hello World'
Otros consejos
Creo que mi preferencia sería escribir en una tabla de registro.
Alternativamente, como está utilizando 2005, puede escribir un procedimiento SQLCLR simple para ajustar el EventLog.
O podrías usar xp_logevent si quisieras escribir en el registro SQL
Puede iniciar sesión en una tabla simplemente insertando una nueva fila o puede implementar un procedimiento almacenado CLR para escribir en un archivo.
Tenga cuidado al escribir en una tabla, porque si la acción ocurre en una transacción y la transacción se revierte, su entrada de registro desaparecerá.
Sería mejor iniciar sesión desde dentro de un proceso SQL en la propia base de datos.T-SQL puede escribir en archivos pero en realidad no está diseñado para ello.
Ahí está el IMPRIMIR comando, pero prefiero iniciar sesión en una tabla para que puedas consultarla.
Puede escribir filas en una tabla de registro desde un procedimiento almacenado.Como han indicado otros, podría hacer todo lo posible para escribir en algún archivo de texto u otro registro con CLR o xp_logevent, pero parece que necesita más volumen del que sería práctico para tales usos.
Los casos difíciles ocurren (y es en estos para los que realmente necesita su registro) cuando las transacciones fallan.Dado que cualquier registro que se produzca durante estas transacciones se revertirá junto con la transacción de la que forman parte, es mejor tener una API de registro que sus clientes puedan usar para registrar errores.Puede ser un DAL simple que se registra en la misma base de datos o en una compartida.
Por si sirve de algo, descubrí que cuando no asigno un controlador de InfoMessage a mi SqlConnection:
sqlConnection.InfoMessage += new SqlInfoMessageEventHandler(MySqlConnectionInfoMessageHandler);
donde la firma de InfoMessageHandler se ve así:
MySqlConnectionInfoMessageHandler(object sender, SqlInfoMessageEventArgs e)
entonces mis declaraciones PRINT en mis procesos almacenados no aparecen en DbgView.
Podría usar variables de salida para devolver mensajes, pero eso depende de que el proceso se ejecute sin errores.
create procedure usp_LoggableProc
@log varchar(max) OUTPUT
as
-- T-SQL statement here ...
select @log = @log + 'X is foo'
Y luego en tu código ADO algo así:
string log = (string)SqlCommand.Parameters["@log"].Value;
Puede utilizar riserror para crear sus propios errores personalizados con la información que necesita y que estará disponible a través de la colección habitual de errores SqlException en su código ADO:
RAISERROR('X is Foo', 10, 1)
Hmmm, pero sí, no puedo evitar sentir que solo necesito depurar y, en su situación, simplemente inserte mensajes varchar en una tabla de error como los demás han sugerido y seleccione * cuando esté depurando.
Quizás quieras comprobar Log4TSQL.Proporciona registro de bases de datos para procedimientos almacenados y activadores en SQL Server 2005 - 2008.Tiene la posibilidad de establecer niveles de registro separados e independientes según el procedimiento/activador.
- El siguiente DDL SQL le creará la tabla para almacenar el uso de datos de registro [DB] GO
/****** 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>
*/