Stacktrace im SQL Server
Allgemein SQL Server 2005 SQL Server 2008
Also nehmen wir mal an, wie es dazugekommen ist - Schweigen - man hat einen Datenbank mit ca. 1000 Stored Procedures und ca. 500 Triggern in den Tabellen.
Was man nun möchte ist was ähnliches wie in C# haben, einen Stacktrace, nach dem Motto, wenn was schief läuft möchte ich auch wissen, wie der gesamte Aufrufpfad war
Kategorie Also nehmen wir mal an, wie es dazugekommen ist - Schweigen - man hat einen Datenbank mit ca. 1000 Stored Procedures und ca. 500 Triggern in den Tabellen.
Was man nun möchte ist was ähnliches wie in C# haben, einen Stacktrace, nach dem Motto, wenn was schief läuft möchte ich auch wissen, wie der gesamte Aufrufpfad war
Also hier nun die Schritte, die wir in
dem Projekt gemacht haben:
Ersten lege eine Tabelle an ->
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[_ERRORLOG](
[tc_session] [int] NULL,
[tc_procname] [varchar](200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[tc_errnumber] [int] NULL,
[tc_errlevel] [int] NULL,
[tc_errseverity] [int] NULL,
[tc_start] [datetime] NULL,
[tc_date] [datetime] NULL,
[tc_callstack] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[tc_errmessage] [varchar](8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
Das war ja einfach, da müssen wir nur noch was reinschreiben Also legen wir die 1001 Stored Procedure an ->
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
CREATE PROC [dbo].[SP_INSERT_ERRORLOG]
(
@procname VARCHAR(500),
@startzeit DATETIME = NULL)
AS
BEGIN
IF @startzeit IS NULL
SET @startzeit = getdate()
DECLARE @ErrorMessage NVARCHAR(4000) ;
DECLARE @ErrorMessage_c NVARCHAR(4000) ;
DECLARE @ErrorSeverity INT ;
DECLARE @ErrorState INT ;
DECLARE @Errornumber INT ;
DECLARE @errorline INT ;
DECLARE @ERRORPROCEDURE VARCHAR(500)
DECLARE @callstack VARCHAR(1000)
DECLARE @calledby VARCHAR(20)
SET @calledby = ' CALLEDBY:'
SELECT @ERRORPROCEDURE = @procname, @errornumber = ERROR_Number(), @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @errorline = error_line()
PRINT @errorprocedure
IF @errornumber = 50000
BEGIN
IF @@nestlevel <= 2
BEGIN
SET @errormessage_c = @errormessage + @procname + ':'
SET @errorprocedure = substring(@errormessage_c, 1, charindex('L:', @errormessage_c) - 2)
SET @callstack = rtrim(substring(@errormessage_c, charindex(@calledby, @errormessage) + len(@calledby), 1000))
INSERT INTO _ERRORLOG WITH (UPDLOCK)
SELECT @@spid, @ERRORPROCEDURE, @errornumber, @errorstate, @errorseverity, @startzeit, getdate(), @callstack, @ERRORMESSAGE_c
RAISERROR (@errormessage_c, @ErrorSeverity, @ErrorState) ;
END
ELSE
BEGIN
SET @errormessage_c = @errormessage + @procname + ':'
RAISERROR (@errormessage_c, @ErrorSeverity, @ErrorState) ;
END
END
ELSE
BEGIN
IF @@nestlevel <= 2
BEGIN
SET @errormessage_c = @errorprocedure + ' L:' + CAST(@errorline AS CHAR(4)) + ' ERRNO:' + CAST(@errornumber AS CHAR(9)) + @errormessage + ' CALLEDBY:'
INSERT INTO _ERRORLOG WITH (UPDLOCK)
SELECT @@spid, @ERRORPROCEDURE, @errornumber, @errorstate, @errorseverity, @startzeit, getdate(), '', @ERRORMESSAGE_c
RAISERROR (@ERRORMessage_c, @ErrorSeverity, @ErrorState) ;
END
ELSE
BEGIN
SET @errormessage_c = @errorprocedure + ' L:' + CAST(@errorline AS CHAR(4)) + ' ERRNO:' + CAST(@errornumber AS CHAR(9)) + @errormessage + @calledby
RAISERROR (@ERRORMessage_c, @ErrorSeverity, @ErrorState) ;
END
END
END
OK, aber was soll die machen?
Die Stored Proc wirft jedes mal einen weiteren Fehler, und hängt ein paar Informationen an die ErrorMessage, welche geworden wird
Jetzt muß man in jeden Trigger und Stored Procedure folgenden Code einfügen ->
BEGIN
BEGIN TRY
DECLARE @tc_syslogstart DATETIME
SET @tc_syslogstart = getdate()
DECLARE @procname VARCHAR(100)
SET @procname = object_name(@@procid)
Alter Stored Proc Code
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK
EXEC SP_INSERT_ERRORLOG @procname, @tc_syslogstart
END CATCH
END
Dann kann man schön z.B. folgende Fehlermeldung "Beispiel" in der Tabelle Error-Log abgreifen ->
FILDO_VEHICLE_UPDATE_TRIGGER L:41 ERRNO:1205 Transaction (Process ID 108) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. CALLEDBY:FILDO_READY_4_RENT_INSERT_TRIGGER:TRIGGER_WIZ_203_DISPLAY_MODIFY_COMPLETED
Schön, oder?
Gruß JJR
P.S.: Das man den Code noch schöner schreiben kann, klar, aber er läuft
Ersten lege eine Tabelle an ->
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[_ERRORLOG](
[tc_session] [int] NULL,
[tc_procname] [varchar](200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[tc_errnumber] [int] NULL,
[tc_errlevel] [int] NULL,
[tc_errseverity] [int] NULL,
[tc_start] [datetime] NULL,
[tc_date] [datetime] NULL,
[tc_callstack] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[tc_errmessage] [varchar](8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
Das war ja einfach, da müssen wir nur noch was reinschreiben Also legen wir die 1001 Stored Procedure an ->
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
CREATE PROC [dbo].[SP_INSERT_ERRORLOG]
(
@procname VARCHAR(500),
@startzeit DATETIME = NULL)
AS
BEGIN
IF @startzeit IS NULL
SET @startzeit = getdate()
DECLARE @ErrorMessage NVARCHAR(4000) ;
DECLARE @ErrorMessage_c NVARCHAR(4000) ;
DECLARE @ErrorSeverity INT ;
DECLARE @ErrorState INT ;
DECLARE @Errornumber INT ;
DECLARE @errorline INT ;
DECLARE @ERRORPROCEDURE VARCHAR(500)
DECLARE @callstack VARCHAR(1000)
DECLARE @calledby VARCHAR(20)
SET @calledby = ' CALLEDBY:'
SELECT @ERRORPROCEDURE = @procname, @errornumber = ERROR_Number(), @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @errorline = error_line()
PRINT @errorprocedure
IF @errornumber = 50000
BEGIN
IF @@nestlevel <= 2
BEGIN
SET @errormessage_c = @errormessage + @procname + ':'
SET @errorprocedure = substring(@errormessage_c, 1, charindex('L:', @errormessage_c) - 2)
SET @callstack = rtrim(substring(@errormessage_c, charindex(@calledby, @errormessage) + len(@calledby), 1000))
INSERT INTO _ERRORLOG WITH (UPDLOCK)
SELECT @@spid, @ERRORPROCEDURE, @errornumber, @errorstate, @errorseverity, @startzeit, getdate(), @callstack, @ERRORMESSAGE_c
RAISERROR (@errormessage_c, @ErrorSeverity, @ErrorState) ;
END
ELSE
BEGIN
SET @errormessage_c = @errormessage + @procname + ':'
RAISERROR (@errormessage_c, @ErrorSeverity, @ErrorState) ;
END
END
ELSE
BEGIN
IF @@nestlevel <= 2
BEGIN
SET @errormessage_c = @errorprocedure + ' L:' + CAST(@errorline AS CHAR(4)) + ' ERRNO:' + CAST(@errornumber AS CHAR(9)) + @errormessage + ' CALLEDBY:'
INSERT INTO _ERRORLOG WITH (UPDLOCK)
SELECT @@spid, @ERRORPROCEDURE, @errornumber, @errorstate, @errorseverity, @startzeit, getdate(), '', @ERRORMESSAGE_c
RAISERROR (@ERRORMessage_c, @ErrorSeverity, @ErrorState) ;
END
ELSE
BEGIN
SET @errormessage_c = @errorprocedure + ' L:' + CAST(@errorline AS CHAR(4)) + ' ERRNO:' + CAST(@errornumber AS CHAR(9)) + @errormessage + @calledby
RAISERROR (@ERRORMessage_c, @ErrorSeverity, @ErrorState) ;
END
END
END
OK, aber was soll die machen?
Die Stored Proc wirft jedes mal einen weiteren Fehler, und hängt ein paar Informationen an die ErrorMessage, welche geworden wird
Jetzt muß man in jeden Trigger und Stored Procedure folgenden Code einfügen ->
BEGIN
BEGIN TRY
DECLARE @tc_syslogstart DATETIME
SET @tc_syslogstart = getdate()
DECLARE @procname VARCHAR(100)
SET @procname = object_name(@@procid)
Alter Stored Proc Code
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK
EXEC SP_INSERT_ERRORLOG @procname, @tc_syslogstart
END CATCH
END
Dann kann man schön z.B. folgende Fehlermeldung "Beispiel" in der Tabelle Error-Log abgreifen ->
FILDO_VEHICLE_UPDATE_TRIGGER L:41 ERRNO:1205 Transaction (Process ID 108) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. CALLEDBY:FILDO_READY_4_RENT_INSERT_TRIGGER:TRIGGER_WIZ_203_DISPLAY_MODIFY_COMPLETED
Schön, oder?
Gruß JJR
P.S.: Das man den Code noch schöner schreiben kann, klar, aber er läuft