So protokollieren Sie Fehlerdetails, wenn Sie try / catch für dynamische SQL-Sicherungsbefehle verwenden


10

Wenn Sie einen Sicherungsbefehl innerhalb einer gespeicherten Prozedur ausgeben, die einen try catch und eine dynamische SQL verwendet, sind die Fehlermeldungen im Vergleich zur direkten Ausführung des Sicherungsbefehls sehr allgemein.

Try / Catch in SP:

    begin try
        execute sp_executesql @sql;  -- a backup command
    end try
    begin catch  
        print ERROR_MESSAGE();  -- save to log, etc.
    end catch

Ergebnisse in

50000: usp_Backup: 117: BACKUP DATABASE wird abnormal beendet.

wheareas, die den rohen Befehl ausgeben:

    backup DATABASE someDb to disk...

Ergebnisse in besseren Details:

Suchfehler - SQL Server-Datenbankfehler: In der Datei "H: \ Ordnername \ Dateiname.bak:" 112 ist ein nicht behebbarer E / A-Fehler aufgetreten. 112 (Auf der Festplatte ist nicht genügend Speicherplatz vorhanden.)

Gibt es eine Möglichkeit, diese Details in Variablen innerhalb der gespeicherten Prozedur zu erfassen (protokollieren, an den Aufrufer zurückgeben, um die Logik erneut zu versuchen)? Es scheint, dass die Details auf dem Nachrichtenkanal durchkommen, aber ich möchte, dass sie im SP verfügbar sind.


Vielleicht möchten Sie dies sehen: stackoverflow.com/questions/5966670/…
8kb

Antworten:


13

Wenn BACKUP DATABASEein Fehler generiert wird, werden tatsächlich zwei generiert. Leider TRY/CATCHist es nicht möglich, den ersten Fehler zu erfassen. es erfasst nur den zweiten Fehler.

Ich vermute, dass Ihre beste Möglichkeit, den wahren Grund für eine fehlgeschlagene Sicherung zu erfassen, darin besteht, Ihre Sicherungen über SQLCMD (mit -oAusgabe an eine Datei), SSIS, C #, PowerShell usw. zu automatisieren . All dies gibt Ihnen eine viel bessere Kontrolle über die Erfassung aller Sicherungen der Fehler.

Die SO-Antwort im Kommentar schlägt die Verwendung vor DBCC OUTPUTBUFFER- obwohl dies möglich ist, scheint dies überhaupt kein Kinderspiel zu sein. Fühlen Sie sich frei, Spaß mit diesem Verfahren von Erland Sommarskogs Website zu haben , aber dies scheint in Kombination mit immer noch nicht gut zu funktionieren TRY/CATCH.

Die einzige Möglichkeit, mit der ich die Fehlermeldung erfassen konnte, bestand darin, spGET_LastErrorMessageden tatsächlichen Fehler auszulösen. Wenn Sie es in ein TRY/CATCHeinpacken, wird der Fehler verschluckt und die gespeicherte Prozedur führt nichts aus:

BEGIN TRY
  EXEC sp_executesql N'backup that fails...';
END TRY
BEGIN CATCH
  EXEC dbo.spGet_LastErrorMessage;
END CATCH

In SQL Server <2012 können Sie den Fehler nicht selbst erneut auslösen, in SQL Server 2012 und höher jedoch. Diese beiden Varianten funktionieren also:

CREATE PROCEDURE dbo.dothebackup
AS
BEGIN
  SET NOCOUNT ON;
  EXEC sp_executesql N'backup that fails...';
END
GO

EXEC dbo.dothebackup;
EXEC dbo.spGET_LastErrorMessage;

Oder ab 2012 funktioniert dies, macht aber den Zweck von weitgehend zunichte TRY/CATCH, da der ursprüngliche Fehler immer noch ausgelöst wird:

CREATE PROCEDURE dbo.dothebackup2
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRY
    EXEC sp_executesql N'backup that fails...';
  END TRY
  BEGIN CATCH
    THROW;
  END CATCH
END
GO

EXEC dbo.dothebackup2;
EXEC dbo.spGET_LastErrorMessage;

In beiden Fällen wird der Fehler natürlich immer noch auf den Client übertragen. Wenn Sie dies verwenden TRY/CATCH, um dies zu vermeiden, müssen Sie leider eine Entscheidung treffen, es sei denn, es gibt eine Lücke, an die ich nicht denke. Geben Sie dem Benutzer entweder den Fehler und können Sie Details darüber erfassen es, oder unterdrücken Sie sowohl den Fehler als auch den tatsächlichen Grund.


So lächerlich es auch ist, der Sommarskog-Ansatz scheint nicht ausgeschlossen zu sein, wenn ich dem Anrufer nur einen Kontext innerhalb einer Schnittstelle bereitstellen wollte. Besser als einen separaten Prozess zu starten. Wollen Sie damit sagen, dass es in einem TRY / CATCH nicht funktioniert?
Crokusek

@crokusek Ich habe eine Variante ausprobiert und das Ergebnis war leer. Ich werde es heute noch einmal versuchen.
Aaron Bertrand

Erhält LastErrorMessage () im Falle eines Fehlers die Ergebnisse eines vorherigen Fehlers aus der Sitzung? Wenn dann die letzten beiden Execs als Skript ausgeführt werden, könnte der erste Exec möglicherweise in einen Versuch / Fang und in den Fang eingeschlossen werden, eine Variable setzen und dann erneut werfen. Dann wird der LastError nur aufgerufen, wenn die Variable gesetzt ist. Angenommen, der erneute Wurf überspringt nicht den zweiten Aufruf, der meiner Meinung nach normalerweise in einem Skriptkontext wahr ist. Ich kann diesen Ansatz am Ende möglicherweise immer noch nicht verwenden, da nicht alles in einem SP platziert werden kann, wenn ich es richtig verstehe. Trotzdem danke!
Crokusek

2

Nun, ich weiß, dass dies ein alter Thread ist, und ich weiß, dass ich einen verschlungenen Hack vorschlagen werde, aber nur für den Fall, dass er jemandem helfen kann, geht es so: Da diese Sicherungsfehler protokolliert werden, können Sie xp_readerrorlog im catch verwenden Block, um das Protokoll nach verwandten Nachrichten (Fehler oder Informationen) zu durchsuchen. Sie können nach xp_readerrorlog-Parametern suchen, aber kurz gesagt, Sie können eine Suchzeichenfolge und einen Startzeitfilter angeben, die in diesem Fall nützlich sind. Ich bin mir nicht sicher, ob dies Ihrer Wiederholungslogik helfen würde, aber um entweder Informationen oder Fehler für die Protokollierung zu erfassen, habe ich mir so etwas ausgedacht ...

IF OBJECT_ID('tempdb.dbo.#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (LogDate datetime,ProcessInfo nvarchar(100),LogText nvarchar(4000))
BEGIN TRY
SELECT @begintime = GETDATE()
EXEC sp_executesql @SQL --your backup statement string
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'backed up',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'Backup' order by logdate desc
END TRY
BEGIN CATCH
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'Backup',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'spid'+cast(@@SPID as varchar(6)) order by logdate desc
END CATCH
PRINT @result

HTH


Dies funktioniert gut bei einigen häufigen Fehlern, aber es gibt einige Fehler, die anscheinend nur direkt auf den Client geworfen werden. Das sp_readerrorlog-Protokoll enthält eine Meldung mit der Aufschrift "Anwendungsprotokoll", wobei unter "Anwendung" der externe Prozess zu verstehen ist, der Befehle ausgibt. SO Link
crokusek

0

Sie können die Fehlerdetails in einer Tabelle protokollieren. Sie können auch eine Protokolldatei erstellen, für die jedoch möglicherweise eine CLR oder xp_cmdshell erforderlich ist. Sie können auch Datenbank-E-Mails senden, dies kann jedoch zu Spam-Problemen führen und ist kein ordnungsgemäßes Protokoll.

Die Tabelle ist am einfachsten.

  1. Erstellen Sie eine Tabelle zum Speichern der Fehler
  2. Erstellen Sie eine gespeicherte Prozedur, die in die Fehlertabelle eingefügt wird
  3. Rufen Sie die gespeicherte Prozedur im catch-Block auf

Schauen Sie sich das Beispiel von Jeremy Kadlec an, das Sie unter dem folgenden Link finden:

http://www.mssqltips.com/sqlservertip/1152/standardized-sql-server-error-handling-and-centralized-logging/


3
Das Problem besteht nicht darin, was mit den Fehlern zu tun ist, sondern darin, dass für einige Befehle nicht die richtige Fehlermeldung verfügbar ist CATCH. Dies liegt daran, dass nur die letzte Fehlermeldung in ERROR_MESSAGE()...
Aaron Bertrand
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.