Saya dapat melihat beberapa pos di mana pentingnya menangani pengecualian di lokasi pusat atau pada batas proses ditekankan sebagai praktik yang baik daripada membuang sampah sembarangan setiap blok kode di sekitar try / catch. Saya sangat percaya bahwa sebagian besar dari kita memahami pentingnya hal itu, tetapi saya melihat orang-orang masih berakhir dengan pola anti tangkapan-log-rethrow terutama karena untuk memudahkan pemecahan masalah selama pengecualian apa pun, mereka ingin mencatat lebih banyak informasi spesifik konteks (misalnya: parameter metode berlalu) dan caranya adalah dengan membungkus metode sekitar try / catch / log / rethrow.
public static bool DoOperation(int num1, int num2)
{
try
{
/* do some work with num1 and num2 */
}
catch (Exception ex)
{
logger.log("error occured while number 1 = {num1} and number 2 = {num2}");
throw;
}
}
Apakah ada cara yang benar untuk mencapai hal ini sambil tetap mempertahankan pengecualian dalam menangani praktik yang baik? Saya mendengar tentang kerangka kerja AOP seperti PostSharp untuk ini tetapi ingin tahu apakah ada kerugian atau biaya kinerja utama yang terkait dengan kerangka kerja AOP ini.
Terima kasih!
sumber
Jawaban:
Masalahnya bukan blok tangkap lokal, masalahnya adalah log dan rethrow . Baik menangani pengecualian atau membungkusnya dengan pengecualian baru yang menambahkan konteks tambahan dan membuangnya. Kalau tidak, Anda akan mengalami beberapa entri log duplikat untuk pengecualian yang sama.
Idenya di sini adalah untuk meningkatkan kemampuan untuk debug aplikasi Anda.
Contoh # 1: Tangani
Jika Anda menangani pengecualian, Anda dapat dengan mudah menurunkan peringkat pentingnya entri log pengecualian dan tidak ada alasan untuk meresap pengecualian itu ke atas rantai. Sudah ditangani.
Menangani pengecualian dapat mencakup memberi tahu pengguna bahwa ada masalah, mencatat acara, atau mengabaikannya.
CATATAN: jika Anda sengaja mengabaikan pengecualian, saya sarankan memberikan komentar di klausa tangkapan kosong yang dengan jelas menunjukkan alasannya. Ini membuat pengelola di masa depan tahu bahwa itu bukan kesalahan atau pemrograman yang malas. Contoh:
Contoh # 2: Tambahkan konteks tambahan dan lempar
Menambahkan konteks tambahan (seperti nomor baris dan nama file dalam kode parsing) dapat membantu meningkatkan kemampuan untuk debug file input - dengan asumsi masalahnya ada di sana. Ini semacam kasus khusus, jadi balaslah pengecualian dalam "ApplicationException" hanya untuk mengubah citra itu tidak membantu Anda melakukan debug. Pastikan Anda menambahkan informasi tambahan.
Contoh # 3: Jangan melakukan apa pun dengan pengecualian
Dalam kasus terakhir ini, Anda hanya membiarkan pengecualian pergi tanpa menyentuhnya. Handler pengecualian di lapisan terluar dapat menangani logging. The
finally
klausa digunakan untuk memastikan sumber daya yang dibutuhkan oleh metode Anda dibersihkan, tapi ini bukan tempat untuk log bahwa pengecualian dilemparkan.sumber
Saya tidak percaya tangkapan lokal adalah anti-pola, bahkan jika saya ingat benar itu benar-benar diberlakukan di Jawa!
Apa kunci saya ketika menerapkan penanganan kesalahan adalah strategi keseluruhan. Anda mungkin menginginkan filter yang menangkap semua pengecualian di batas layanan, Anda mungkin ingin mencegatnya secara manual - keduanya baik-baik saja selama ada strategi keseluruhan, yang akan masuk ke dalam standar pengkodean tim Anda.
Secara pribadi saya suka menangkap kesalahan di dalam suatu fungsi ketika saya dapat melakukan salah satu dari yang berikut:
Jika bukan salah satu dari kasus ini saya tidak menambahkan coba / tangkapan lokal. Jika ya, tergantung pada skenario saya dapat menangani pengecualian (misalnya metode TryX yang mengembalikan false) atau rethrow sehingga pengecualian akan ditangani oleh strategi global.
Sebagai contoh:
Atau contoh rethrow:
Kemudian Anda menangkap kesalahan di bagian atas tumpukan dan menyajikan pesan ramah pengguna yang bagus kepada pengguna.
Apapun pendekatan yang Anda lakukan, selalu layak untuk membuat unit test untuk skenario ini sehingga Anda dapat memastikan fungsionalitasnya tidak berubah dan mengganggu aliran proyek di kemudian hari.
Anda belum menyebutkan bahasa tempat Anda bekerja, tetapi menjadi pengembang .NET dan terlalu sering melihat ini.
JANGAN MENULIS:
Menggunakan:
Yang pertama me-reset jejak stack dan membuat tangkapan level atas Anda sama sekali tidak berguna!
TLDR
Menangkap secara lokal bukanlah anti-pola, sering dapat menjadi bagian dari desain dan dapat membantu menambahkan konteks tambahan ke kesalahan.
sumber
Ini sangat tergantung pada bahasa. Misalnya C ++ tidak menawarkan jejak stack dalam pesan kesalahan pengecualian, jadi melacak pengecualian melalui catch-log-rethrow yang sering dapat membantu. Sebaliknya, Java dan bahasa serupa menawarkan jejak tumpukan yang sangat baik, meskipun format jejak tumpukan ini mungkin tidak terlalu dapat dikonfigurasi. Pengecualian penangkapan dan rethrowing dalam bahasa-bahasa ini sama sekali tidak ada artinya kecuali Anda benar-benar dapat menambahkan beberapa konteks penting (misalnya menghubungkan pengecualian SQL tingkat rendah dengan konteks operasi logika bisnis).
Setiap strategi penanganan kesalahan yang diterapkan melalui refleksi hampir selalu kurang efisien daripada fungsi yang dibangun ke dalam bahasa. Selain itu, logging yang meluas memiliki overhead kinerja yang tidak dapat dihindari. Jadi Anda perlu menyeimbangkan aliran informasi yang Anda dapatkan dengan persyaratan lain untuk perangkat lunak ini. Yang mengatakan, solusi seperti PostSharp yang dibangun pada instrumentasi tingkat kompiler umumnya akan melakukan jauh lebih baik daripada refleksi run-time.
Saya pribadi percaya bahwa mencatat semuanya tidak bermanfaat, karena memuat banyak informasi yang tidak relevan. Karena itu saya skeptis terhadap solusi otomatis. Dengan kerangka logging yang baik, mungkin cukup untuk memiliki pedoman pengkodean yang disepakati yang membahas jenis informasi apa yang ingin Anda catat dan bagaimana info ini harus diformat. Anda kemudian dapat menambahkan logging di tempat penting.
Masuk pada logika bisnis jauh lebih penting daripada masuk pada fungsi utilitas. Dan mengumpulkan jejak tumpukan laporan kerusakan dunia nyata (yang hanya membutuhkan penebangan di tingkat atas suatu proses) memungkinkan Anda untuk menemukan area kode tempat penebangan memiliki nilai paling besar.
sumber
Ketika saya melihat
try/catch/log
dalam setiap metode itu menimbulkan kekhawatiran bahwa pengembang tidak tahu apa yang mungkin atau mungkin tidak terjadi dalam aplikasi mereka, dianggap yang terburuk, dan terlebih dahulu mencatat semuanya di mana-mana karena semua bug yang mereka harapkan.Ini adalah gejala bahwa pengujian unit dan integrasi tidak cukup dan pengembang terbiasa untuk melangkah melalui banyak kode dalam debugger dan berharap bahwa entah bagaimana banyak logging akan memungkinkan mereka untuk menyebarkan kode buggy di lingkungan pengujian dan menemukan masalah dengan melihat log.
Kode yang melempar pengecualian bisa lebih berguna daripada kode redundan yang menangkap dan mencatat pengecualian. Jika Anda melemparkan pengecualian dengan pesan yang bermakna ketika suatu metode menerima argumen yang tidak terduga (dan mencatatnya di batas layanan) itu jauh lebih bermanfaat daripada langsung mencatat pengecualian yang dilemparkan sebagai efek samping dari argumen yang tidak valid dan harus menebak apa yang menyebabkannya. .
Nulls adalah contohnya. Jika Anda mendapatkan nilai sebagai argumen atau hasil dari pemanggilan metode dan itu tidak boleh nol, lempar pengecualian itu. Jangan hanya mencatat hasil yang
NullReferenceException
dilemparkan lima baris kemudian karena nilai nol. Apa pun cara Anda mendapatkan pengecualian, tetapi yang satu memberi tahu Anda sesuatu sementara yang lain membuat Anda mencari sesuatu.Seperti yang dikatakan oleh orang lain, yang terbaik adalah mencatat pengecualian di batas layanan, atau setiap kali pengecualian tidak ditata ulang karena ditangani dengan anggun. Perbedaan yang paling penting adalah antara sesuatu dan tidak sama sekali. Jika pengecualian Anda masuk di satu tempat yang mudah dijangkau, Anda akan menemukan informasi yang Anda butuhkan saat Anda membutuhkannya.
sumber
Jika Anda perlu merekam informasi konteks yang belum ada dalam pengecualian, Anda membungkusnya dalam pengecualian baru, dan memberikan pengecualian asli sebagai
InnerException
. Dengan begitu Anda masih memiliki jejak stack asli yang dipertahankan. Begitu:Parameter kedua ke
Exception
konstruktor memberikan pengecualian dalam. Kemudian Anda bisa mencatat semua pengecualian di satu tempat, dan Anda masih mendapatkan jejak tumpukan penuh dan informasi kontekstual dalam entri log yang sama.Anda mungkin ingin menggunakan kelas pengecualian kustom, tetapi intinya sama.
coba / tangkap / log / rethrow berantakan karena akan menyebabkan log membingungkan - misalnya bagaimana jika pengecualian berbeda terjadi di utas lain di antara mencatat informasi kontekstual dan mencatat pengecualian aktual di dalam penangan tingkat atas? coba / tangkap / lempar baik-baik saja, jika pengecualian baru menambahkan informasi ke aslinya.
sumber
Pengecualian itu sendiri harus menyediakan semua info yang diperlukan untuk pencatatan yang benar, termasuk pesan, kode kesalahan, dan apa yang tidak. Karena itu seharusnya tidak perlu menangkap pengecualian hanya untuk rethrow atau melemparkan pengecualian yang berbeda.
Seringkali Anda akan melihat pola beberapa pengecualian yang ditangkap dan diubah kembali sebagai pengecualian umum, seperti menangkap DatabaseConnectionException, InvalidQueryException, dan InvalidSQLParameterException dan rethrowing DatabaseException. Meskipun untuk ini, saya berpendapat bahwa semua pengecualian khusus ini harus berasal dari DatabaseException di tempat pertama, jadi rethrowing tidak diperlukan.
Anda akan menemukan bahwa menghapus klausa catch catch yang tidak perlu (bahkan klausa untuk tujuan logging murni) sebenarnya akan membuat pekerjaan lebih mudah, bukan lebih sulit. Hanya tempat-tempat di program Anda yang menangani pengecualian yang harus mencatat pengecualian, dan, jika semuanya gagal, penangan pengecualian program untuk satu upaya terakhir untuk mencatat pengecualian sebelum keluar dari program dengan anggun. Pengecualian harus memiliki jejak tumpukan penuh yang menunjukkan titik tepat di mana pengecualian dilemparkan, sehingga seringkali tidak perlu menyediakan pencatatan "konteks".
Yang mengatakan AOP mungkin merupakan solusi perbaikan cepat untuk Anda, meskipun biasanya memerlukan sedikit perlambatan secara keseluruhan. Saya akan mendorong Anda untuk menghapus klausa catch catch yang tidak perlu sepenuhnya jika tidak ada nilai tambah yang ditambahkan.
sumber
try { tester.test(); } catch (NullPointerException e) { logger.error("Variable tester was null!"); }
. Jejak tumpukan cukup dalam banyak kasus, tetapi kurang dari itu, jenis kesalahan biasanya memadai.