Come registrare i dettagli dell'errore quando si utilizza usando try / catch per comandi di backup SQL dinamici


10

Quando si emette un comando di backup all'interno di una procedura memorizzata che utilizza un catch try e sql dinamico, i messaggi di errore sono molto generici rispetto all'esecuzione diretta del comando di backup.

Prova / Cattura in SP:

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

Risultati in

50000: usp_Backup: 117: BACKUP DATABASE sta terminando in modo anomalo.

quando si esegue il comando raw:

    backup DATABASE someDb to disk...

Risultati in dettagli migliori:

Errore di ricerca - Errore database SQL Server: si è verificato un errore I / O non recuperabile sul file "H: \ NomeCartella \ Nomefile.bak:" 112 (Spazio insufficiente sul disco.).

C'è un modo per catturare questi dettagli in variabili all'interno della procedura memorizzata (per accedere, tornare al chiamante, per riprovare la logica)? Sembra che i dettagli stiano arrivando sul canale dei messaggi, ma li vorrei disponibili all'interno di SP.


Potresti voler vedere questo: stackoverflow.com/questions/5966670/…
8kb

Risposte:


13

Quando BACKUP DATABASEgenera un errore, ne genera effettivamente due. Sfortunatamente TRY/CATCHnon è in grado di catturare il primo errore; cattura solo il secondo errore.

Ho il sospetto che la tua migliore scommessa per catturare il vero motivo dietro un backup fallito sia automatizzare i tuoi backup tramite SQLCMD (con -ol'invio di output a un file), SSIS, C #, PowerShell ecc. Tutto ciò ti darà un controllo molto maggiore sulla cattura di tutti degli errori.

La risposta SO nel commento suggerisce l'uso DBCC OUTPUTBUFFER- mentre è possibile, questo non sembra affatto un gioco da ragazzi. Sentiti libero di divertirti con questa procedura dal sito di Erland Sommarskog , ma questo sembra non funzionare bene in combinazione con TRY/CATCH.

L'unico modo in cui mi è sembrato di riuscire a catturare il messaggio di errore spGET_LastErrorMessageè se l'errore reale viene generato. Se lo avvolgi in un TRY/CATCHerrore viene ingoiato e la procedura memorizzata non fa nulla:

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

In SQL Server <2012 non è possibile aumentare nuovamente l'errore dall'utente, ma è possibile in SQL Server 2012 e versioni successive. Quindi queste due varianti funzionano:

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;

O nel 2012 e oltre, questo funziona, ma in gran parte sconfigge lo scopo TRY/CATCH, poiché l'errore originale viene ancora generato:

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 entrambi questi casi, l'errore viene comunque generato al client, ovviamente. Quindi, se stai usando TRY/CATCHper evitarlo, a meno che non ci sia qualche scappatoia a cui non sto pensando, temo che dovrai fare una scelta ... o dare all'utente l'errore ed essere in grado di acquisire dettagli su esso o sopprime sia l'errore sia il motivo reale.


Per quanto ridicolo com'è, l'approccio Sommarskog non sembra fuori discussione se volevo solo fornire un contesto al chiamante all'interno di un'interfaccia. Meglio che avviare un processo separato. Stai dicendo che non funzionerà in un TRY / CATCH?
crokusek,

@crokusek Ho provato una variante e il risultato è risultato vuoto. Ci proverò ancora oggi.
Aaron Bertrand

In caso di errore, LastErrorMessage () ottiene i risultati di qualsiasi errore precedente dalla sessione? Quindi, se gli ultimi due exec vengono eseguiti come script, forse il primo exec potrebbe essere racchiuso in un tentativo / catch e all'interno del catch, impostare una variabile, quindi rilanciare. Quindi LastError viene chiamato solo se la variabile è impostata. Suppone che il rilancio non salti la seconda chiamata, che penso sia generalmente vera in un contesto di scripting. Potrei ancora non essere in grado di utilizzare questo approccio alla fine perché non posso essere inserito in un SP se ho capito bene. Grazie comunque!
crokusek,

2

Bene, so che questo è un vecchio thread e so che cosa sto per proporre è un hack contorto, ma nel caso in cui possa aiutare chiunque, ecco qui: Dato che questi errori di backup vengono registrati, puoi usare xp_readerrorlog nella cattura bloccare per cancellare il registro per il messaggio correlato (errore o informazioni). Puoi cercare google around per i parametri xp_readerrorlog ma in breve puoi specificare una stringa di ricerca e un filtro dell'ora di inizio che sono utili in questo caso. Non sono sicuro che ciò possa aiutare la tua logica di nuovo tentativo, ma per acquisire informazioni o errori per la registrazione, ho trovato qualcosa del genere ...

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


Funziona benissimo per alcuni errori comuni ma ci sono alcuni errori che apparentemente vengono lanciati direttamente sul client. Il registro sp_readerrorlog includerà un messaggio che dice "registro dell'applicazione", dove suppongo che per "applicazione" intendano il processo esterno che sta inviando comandi. SO Link
crokusek,

0

È possibile registrare i dettagli dell'errore in una tabella. È inoltre possibile creare un file di registro, ma ciò potrebbe richiedere un CLR o xp_cmdshell. È inoltre possibile inviare la posta del database, ma ciò può causare problemi di spam e non è un registro corretto.

La tabella è più semplice.

  1. Creare una tabella per la memorizzazione degli errori
  2. Creare una procedura memorizzata che si inserisce nella tabella degli errori
  3. Chiamare la procedura memorizzata nel blocco catch

Guarda l'esempio di Jeremy Kadlec fornito nel link seguente:

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


3
Il problema non riguarda cosa fare degli errori, è che il messaggio di errore corretto non è disponibile per alcuni comandi all'interno CATCH. Questo perché viene restituito solo l' ultimo messaggio di errore in ERROR_MESSAGE()...
Aaron Bertrand
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.