Cara mencatat detail kesalahan saat menggunakan try / catch untuk perintah cadangan SQL dinamis


10

Saat mengeluarkan perintah cadangan dalam prosedur tersimpan yang menggunakan try catch dan sql dinamis, pesan kesalahan sangat umum jika dibandingkan dengan menjalankan perintah cadangan secara langsung.

Coba / Tangkap dalam SP:

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

Hasil dalam

50000: usp_Backup: 117: BATUP DATABASE berakhir secara tidak normal.

ketika mengeluarkan perintah mentah:

    backup DATABASE someDb to disk...

Hasil dalam detail yang lebih baik:

Kesalahan Pencarian - Kesalahan Database SQL Server: Terjadi kesalahan I / O yang tidak dapat dipulihkan pada file "H: \ FolderName \ Filename.bak:" 112 (Tidak ada cukup ruang pada disk.).

Apakah ada cara untuk menangkap rincian ini ke dalam variabel dalam prosedur tersimpan (untuk log, kembali ke pemanggil, untuk coba lagi logika)? Sepertinya detailnya datang melalui saluran pesan tapi saya ingin mereka tersedia dalam SP.


Anda mungkin ingin melihat ini: stackoverflow.com/questions/5966670/…
8kb

Jawaban:


13

Ketika BACKUP DATABASEmenghasilkan kesalahan, itu sebenarnya menghasilkan dua. Sayangnya TRY/CATCHtidak mampu menangkap kesalahan pertama; hanya menangkap kesalahan kedua.

Saya menduga taruhan terbaik Anda untuk menangkap alasan sebenarnya di balik cadangan yang gagal adalah untuk mengotomatiskan cadangan Anda melalui SQLCMD (dengan -omengirim hasil ke file), SSIS, C #, PowerShell dll. Semua yang akan memberi Anda kontrol lebih besar untuk menangkap semua dari kesalahan.

Jawaban SO dalam komentar menyarankan menggunakan DBCC OUTPUTBUFFER- selagi mungkin, ini sepertinya bukan permainan anak-anak sama sekali. Merasa bebas untuk bersenang-senang dengan prosedur ini dari situs Erland Sommarskog , tetapi ini masih tidak berfungsi dengan baik jika dikombinasikan TRY/CATCH.

Satu-satunya cara saya tampaknya dapat menangkap pesan kesalahan spGET_LastErrorMessageadalah jika kesalahan yang sebenarnya terjadi. Jika Anda membungkusnya dalam TRY/CATCHkesalahan tertelan dan prosedur yang tersimpan tidak melakukan apa-apa:

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

Di SQL Server <2012 Anda tidak bisa menaikkan kembali kesalahan sendiri, tetapi Anda bisa di SQL Server 2012 dan yang lebih baru. Jadi dua variasi ini berfungsi:

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;

Atau pada 2012 dan di atas, ini berfungsi, tetapi sebagian besar mengalahkan tujuan TRY/CATCH, karena kesalahan asli masih dilemparkan:

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;

Dalam kedua kasus ini, kesalahan masih terjadi pada klien, tentu saja. Jadi jika Anda menggunakan TRY/CATCHuntuk menghindari itu, kecuali ada beberapa celah yang tidak saya pikirkan, saya khawatir Anda harus membuat pilihan ... baik memberikan kesalahan kepada pengguna dan dapat menangkap detail tentang itu, atau menekan kesalahan dan alasan sebenarnya.


Sungguh konyol, pendekatan Sommarskog tampaknya tidak keluar dari pertanyaan jika saya hanya ingin memberikan beberapa konteks kepada penelepon dalam sebuah antarmuka. Lebih baik daripada memulai proses terpisah. Apakah Anda mengatakan itu tidak akan berhasil dalam TRY / CATCH?
crokusek

@crokusek Saya mencoba satu variasi dan hasilnya kosong. Saya akan mencobanya lagi hari ini.
Aaron Bertrand

Dalam hal tidak ada kesalahan, apakah LastErrorMessage () mendapatkan hasil dari kesalahan sebelumnya dari sesi? Kemudian jika dua eksekutif terakhir dijalankan sebagai skrip, mungkin eksekutif pertama dapat dibungkus dengan try / catch dan dalam catch, atur variabel, lalu lempar kembali. Kemudian LastError dipanggil hanya jika variabel disetel. Mengasumsikan lemparan ulang tidak melewatkan panggilan kedua yang menurut saya biasanya benar dalam konteks skrip. Saya masih mungkin tidak dapat menggunakan pendekatan ini pada akhirnya karena tidak semua bisa ditempatkan dalam SP jika saya mengerti dengan benar. Terimakasih Meskipun!
crokusek

2

Yah saya tahu ini adalah utas lama, dan saya tahu apa yang akan saya usulkan adalah peretasan yang berbelit-belit, tetapi untuk berjaga-jaga kalau itu bisa membantu siapa saja, begini: Karena kesalahan cadangan ini dicatat, Anda dapat menggunakan xp_readerrorlog di tangkapan layar blokir untuk mengikis log untuk pesan terkait (kesalahan atau info). Anda dapat mencari Google untuk xp_readerrorlog params tetapi singkatnya Anda dapat menentukan string pencarian dan filter waktu mulai yang berguna dalam kasus ini. Tidak yakin apakah ini akan membantu Anda coba lagi logika, tetapi untuk menangkap info atau kesalahan untuk login, saya datang dengan sesuatu seperti ini ...

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


Ini berfungsi baik untuk beberapa kesalahan umum tetapi ada beberapa kesalahan yang tampaknya hanya dilemparkan langsung ke klien. Log sp_readerrorlog akan menyertakan pesan yang mengatakan merujuk ke "log aplikasi", di mana saya menganggap dengan "aplikasi" yang mereka maksudkan adalah proses eksternal yang mengeluarkan perintah. SO Link
crokusek

0

Anda bisa mencatat rincian kesalahan ke tabel. Anda juga bisa membuat file log, tetapi itu mungkin memerlukan CLR atau xp_cmdshell untuk melakukannya. Anda juga dapat mengirim email basis data, tetapi itu dapat menyebabkan masalah spam dan bukan log yang tepat.

Tabelnya paling sederhana.

  1. Buat tabel untuk menyimpan kesalahan
  2. Buat prosedur tersimpan yang menyisipkan ke dalam tabel kesalahan
  3. Panggil prosedur yang tersimpan di blok tangkap

Lihat contoh Jeremy Kadlec yang disediakan di tautan di bawah ini:

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


3
Masalahnya bukan tentang apa yang harus dilakukan dengan kesalahan, itu adalah bahwa pesan kesalahan yang tepat tidak tersedia untuk beberapa perintah di dalamnya CATCH. Ini karena hanya pesan kesalahan terakhir yang dikembalikan dalam ERROR_MESSAGE()...
Aaron Bertrand
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.