Como registrar detalhes do erro ao usar o comando try / catch para comandos de backup dinâmico do SQL

10

Ao emitir um comando de backup em um procedimento armazenado que usa um try catch e sql dinâmico, as mensagens de erro são muito gerais quando comparadas à execução direta do comando de backup.

Try / Catch dentro de SP:

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

Resulta em

50000: usp_Backup: 117: BACKUP DATABASE está sendo finalizado de forma anormal.

wheareas emitindo o comando raw:

    backup DATABASE someDb to disk...

Resultados em melhores detalhes:

Erro de pesquisa - Erro no banco de dados do SQL Server: Ocorreu um erro irrecuperável de E / S no arquivo "H: \ FolderName \ Filename.bak:" 112 (não há espaço suficiente no disco.).

Existe uma maneira de capturar esses detalhes em variáveis ​​dentro do procedimento armazenado (para registrar, retornar ao chamador, para repetir a lógica)? Parece que os detalhes estão chegando no canal de mensagens, mas eu gostaria que eles estivessem disponíveis no SP.

crokusek
fonte
Você pode querer ver isso: stackoverflow.com/questions/5966670/…
8kb

Respostas:

13

Quando BACKUP DATABASEgera um erro, na verdade gera dois. Infelizmente TRY/CATCHnão é capaz de capturar o primeiro erro; ele captura apenas o segundo erro.

Eu suspeito que sua melhor aposta para capturar a verdadeira razão por trás de um backup com falha é automatizar seus backups através do SQLCMD (com -osaída para enviar para um arquivo), SSIS, C #, PowerShell etc. Tudo isso lhe dará um controle muito maior sobre a captura de todos os dos erros.

A resposta do SO no comentário sugere o uso DBCC OUTPUTBUFFER- embora seja possível, isso não parece brincadeira de criança. Sinta-se à vontade para se divertir com este procedimento no site de Erland Sommarskog , mas isso ainda não parece funcionar bem em combinação com TRY/CATCH.

A única maneira pela qual pareci conseguir capturar a mensagem de erro spGET_LastErrorMessageé se o erro real for gerado. Se você o envolver, TRY/CATCHo erro será engolido e o procedimento armazenado não fará nada:

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

No SQL Server <2012, você não pode gerar o erro novamente, mas no SQL Server 2012 e mais recente. Portanto, essas duas variações funcionam:

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;

Ou em 2012 e acima, isso funciona, mas em grande parte anula o objetivo de TRY/CATCH, uma vez que o erro original ainda é gerado:

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;

Nos dois casos, o erro ainda é gerado para o cliente, é claro. Portanto, se você estiver usando TRY/CATCHpara evitar isso, a menos que haja alguma brecha em que não esteja pensando, receio que você tenha que fazer uma escolha ... ou dê ao usuário o erro e seja capaz de capturar detalhes sobre ou suprimir o erro e o motivo real.

Aaron Bertrand
fonte
Por mais ridícula que seja, a abordagem Sommarskog não parece estar fora de questão se eu apenas quisesse fornecer algum contexto ao chamador dentro de uma interface. Melhor do que iniciar um processo separado. Você está dizendo que não funcionará dentro de uma tentativa / captura?
26612 crokusek
@ crokusek Tentei uma variação e o resultado ficou vazio. Vou tentar outra vez hoje.
Aaron Bertrand
No caso de nenhum erro, LastErrorMessage () obtém os resultados de qualquer erro anterior da sessão? Então, se os dois últimos execs forem executados como script, talvez o primeiro exec possa ser empacotado dentro de um try / catch e dentro do catch, defina uma variável e, em seguida, jogue novamente. Em seguida, o LastError é chamado apenas se a variável estiver configurada. Supõe que o re-lançamento não pule a segunda chamada, que eu acho que geralmente é verdadeira dentro de um contexto de script. Ainda posso não ser capaz de usar essa abordagem no final porque ela não pode ser colocada dentro de um SP se eu entendi corretamente. Obrigado embora!
26612 crokusek
2

Bem, eu sei que esse é um encadeamento antigo, e sei o que estou prestes a propor é um hack complicado, mas para o caso de ajudar alguém, aqui vai: Como esses erros de backup são registrados, você pode usar xp_readerrorlog na captura bloco para raspar o log da mensagem relacionada (erro ou informação). Você pode procurar no google por parâmetros xp_readerrorlog, mas, em suma, pode especificar uma string de pesquisa e um filtro de horário de início que sejam úteis nesse caso. Não tenho certeza se isso ajudaria sua nova tentativa de lógica, mas para capturar informações ou erros no registro, criei algo assim ...

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

Andy McD
fonte
Isso funciona muito bem para alguns erros comuns, mas existem alguns que aparentemente são lançados diretamente diretamente no cliente. O log sp_readerrorlog incluirá uma mensagem dizendo referir-se a "log de aplicativos", onde eu assumo por "aplicativo" eles significam o processo externo que está emitindo comandos. SO Link
crokusek
0

Você pode registrar os detalhes do erro em uma tabela. Você também pode criar um arquivo de log, mas isso pode exigir um CLR ou xp_cmdshell. Você também pode enviar correio do banco de dados, mas isso pode causar problemas de spam e não é um log adequado.

A tabela é mais simples.

  1. Crie uma tabela para armazenar os erros
  2. Crie um procedimento armazenado que insira na tabela de erros
  3. Chame o procedimento armazenado no bloco de captura

Veja o exemplo de Jeremy Kadlec fornecido no link abaixo:

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

Brian
fonte
3
O problema não é sobre o que fazer com os erros, é que a mensagem de erro correta não está disponível para alguns comandos CATCH. Isso ocorre porque somente a última mensagem de erro é retornada em ERROR_MESSAGE()...
Aaron Bertrand