it-swarm-id.com

Cara menulis pesan pengecualian yang baik

Saat ini saya sedang melakukan review kode dan salah satu hal yang saya perhatikan adalah jumlah pengecualian di mana pesan pengecualian sepertinya mengulangi di mana pengecualian terjadi. misalnya.

throw new Exception("BulletListControl: CreateChildControls failed.");

Ketiga item dalam pesan ini saya dapat bekerja dari sisa pengecualian. Saya tahu kelas dan metode dari jejak stack dan saya tahu itu gagal (karena saya punya pengecualian).

Itu membuat saya berpikir tentang pesan apa yang saya masukkan dalam pesan pengecualian. Pertama saya membuat kelas pengecualian, jika seseorang belum ada, untuk alasan umum (misalnya PropertyNotFoundException - the why), dan kemudian ketika saya melemparnya, pesan menunjukkan apa yang salah (mis. "Tidak dapat menemukan properti 'IDontExist' di Node 1234" - the what). Di mana berada di StackTrace. The - when dapat berakhir di log (jika ada). bagaimana untuk pengembang untuk bekerja (dan memperbaiki)

Apakah Anda punya kiat lain untuk melempar pengecualian? Khususnya berkaitan dengan pembuatan jenis baru dan pesan pengecualian.

102
Colin Mackay

Saya akan mengarahkan jawaban saya lebih lanjut ke apa yang terjadi setelah pengecualian: untuk apa manfaatnya dan bagaimana seharusnya perangkat lunak berperilaku, apa yang harus dilakukan pengguna Anda dengan pengecualian? Teknik hebat yang saya temui di awal karier saya adalah selalu melaporkan masalah dan kesalahan dalam 3 bagian: konteks, masalah & solusi. Menggunakan disiplin ini mengubah penanganan kesalahan sangat besar dan membuat perangkat lunak jauh lebih baik bagi operator untuk digunakan.

Berikut beberapa contohnya.

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

Dalam hal ini, operator tahu persis apa yang harus dilakukan dan ke file mana yang harus terpengaruh. Mereka juga tahu bahwa perubahan koneksi pooling tidak mengambil dan harus diulang.

Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell '[email protected]' about this problem.

Saya menulis sistem sisi server dan operator saya umumnya mendukung lini pertama yang mengerti teknologi. Saya akan menulis pesan secara berbeda untuk perangkat lunak desktop yang memiliki audiens yang berbeda tetapi menyertakan informasi yang sama.

Beberapa hal indah terjadi jika seseorang menggunakan teknik ini. Pengembang perangkat lunak sering kali ditempatkan paling baik untuk mengetahui bagaimana menyelesaikan masalah dalam kode mereka sendiri sehingga solusi enkode dengan cara ini saat Anda menulis kode adalah manfaat besar bagi pengguna akhir yang berada pada solusi pencarian yang kurang menguntungkan karena mereka sering kehilangan informasi tentang apa sebenarnya yang dilakukan perangkat lunak. Siapa pun yang pernah membaca pesan kesalahan Oracle akan tahu apa yang saya maksud.

Hal luar biasa kedua yang terlintas dalam pikiran adalah ketika Anda menemukan diri Anda mencoba untuk menggambarkan solusi dalam pengecualian Anda dan Anda sedang menulis "Periksa X dan jika A maka B lagi C". Ini adalah tanda yang sangat jelas dan jelas bahwa pengecualian Anda sedang diperiksa di tempat yang salah. Anda programmer memiliki kapasitas untuk membandingkan hal-hal dalam kode sehingga "jika" pernyataan harus dijalankan dalam kode, mengapa melibatkan pengguna dalam sesuatu yang dapat diotomatisasi? Kemungkinannya adalah dari kode yang lebih dalam dan seseorang telah melakukan hal yang malas dan melempar IOException dari sejumlah metode dan menangkap potensi kesalahan dari semuanya dalam blok kode panggilan yang tidak dapat secara memadai menggambarkan apa yang salah, apa yang specific konteks dan bagaimana cara memperbaikinya. Ini mendorong Anda untuk menulis kesalahan butir yang lebih halus, menangkap dan menanganinya di tempat yang tepat dalam kode Anda sehingga Anda dapat mengartikulasikan dengan baik langkah-langkah yang harus diambil operator.

Di satu perusahaan, kami memiliki operator terbaik yang mengenal perangkat lunak dengan sangat baik dan menyimpan "buku bacaan" mereka sendiri yang menambah pelaporan kesalahan kami dan menyarankan solusi. Untuk mengenali hal ini, perangkat lunak mulai menyertakan tautan wiki ke run book dalam pengecualian sehingga tersedia penjelasan dasar serta tautan ke diskusi dan pengamatan yang lebih maju oleh operator dari waktu ke waktu.

Jika Anda memiliki disiplin untuk mencoba teknik ini, akan jauh lebih jelas apa yang harus Anda beri nama pengecualian Anda dalam kode saat membuat Anda sendiri. NonRecoverableConfigurationReadFailedException menjadi sedikit singkatan untuk apa yang akan Anda gambarkan lebih lengkap kepada operator. Saya suka menjadi verbose dan saya pikir itu akan lebih mudah bagi pengembang berikutnya yang menyentuh kode saya untuk menafsirkan.

72
Sir Wobin

Di pertanyaan yang lebih baru ini saya menyatakan bahwa pengecualian tidak boleh mengandung pesan sama sekali. Menurut pendapat saya, fakta yang mereka lakukan adalah kesalahpahaman yang sangat besar. Yang saya usulkan adalah itu

"Pesan" dari pengecualian adalah (kelas yang memenuhi syarat) nama kelas pengecualian.

Pengecualian harus berisi dalam variabel anggotanya sendiri sebanyak mungkin detail tentang apa yang terjadi; misalnya, IndexOutOfRangeException harus berisi nilai indeks yang ditemukan tidak valid, serta nilai atas dan bawah yang valid pada saat pengecualian dilemparkan. Dengan cara ini, menggunakan refleksi Anda dapat membuat pesan yang dibuat secara otomatis yang berbunyi seperti ini: IndexOutOfRangeException: index = -1; min=0; max=5 dan ini, bersama dengan jejak tumpukan, harus menjadi semua informasi objektif yang Anda butuhkan untuk memecahkan masalah. Memformatnya menjadi pesan cantik seperti "index -1 tidak antara 0 dan 5" tidak menambah nilai apa pun.

Dalam contoh khusus Anda, kelas NodePropertyNotFoundException akan berisi nama properti yang tidak ditemukan, dan referensi ke simpul yang tidak mengandung properti. Ini penting: harus tidak berisi nama simpul; itu harus berisi referensi ke simpul yang sebenarnya. Dalam kasus khusus Anda ini mungkin tidak perlu, tetapi ini adalah masalah prinsip dan cara berpikir yang disukai: perhatian utama ketika membangun pengecualian adalah bahwa itu harus dapat digunakan oleh kode yang mungkin menangkapnya. Kegunaan oleh manusia adalah hal yang penting, tetapi hanya masalah sekunder.

Ini menangani situasi yang sangat membuat frustrasi yang mungkin Anda saksikan pada suatu saat dalam karir Anda, di mana Anda mungkin telah menangkap pengecualian yang berisi informasi penting tentang apa yang terjadi dalam teks pesan, tetapi tidak di dalam variabel anggotanya, sehingga Anda harus melakukan penguraian string teks untuk mencari tahu apa yang terjadi, berharap bahwa teks pesan akan tetap sama di versi berikutnya dari lapisan yang mendasarinya, dan berdoa agar teks pesan tidak akan dalam bahasa asing ketika program Anda dijalankan di negara lain.

Tentu saja, karena nama kelas pengecualian adalah pesan pengecualian, (dan variabel anggota pengecualian adalah detail spesifik), ini berarti bahwa Anda memerlukan banyak dan banyak pengecualian berbeda untuk menyampaikan semua pesan yang berbeda, dan ini baik saja.

Sekarang, kadang-kadang, ketika kita menulis kode, kita menemukan situasi yang salah di mana kita hanya ingin cepat kode pernyataan throw dan melanjutkan menulis kode kita daripada harus mengganggu apa yang kita lakukan untuk pergi membuat yang baru kelas pengecualian sehingga kita bisa membuangnya di sana. Untuk kasus ini, saya memiliki kelas GenericException yang sebenarnya menerima pesan string sebagai parameter waktu konstruksi, tetapi konstruktor dari kelas pengecualian ini dihiasi dengan warna ungu terang yang sangat besar FIXME XXX TODO komentar yang menyatakan bahwa setiap instantiasi dari kelas ini harus diganti dengan instantiasi dari beberapa kelas pengecualian yang lebih khusus sebelum sistem perangkat lunak dirilis, lebih disukai sebelum kode dikomit.

23
Mike Nakis

Sebagai aturan umum, pengecualian akan membantu pengembang menentukan penyebabnya dengan memberikan informasi yang berguna (nilai yang diharapkan, nilai aktual, kemungkinan penyebab/solusi, dll.).

Jenis pengecualian baru harus dibuat ketika tidak ada jenis bawaan yang masuk akal. Jenis tertentu memungkinkan pengembang lain menangkap pengecualian khusus dan menanganinya. Jika pengembang tahu bagaimana menangani pengecualian Anda tetapi tipenya Exception, ia tidak akan bisa menangani dengan benar.

13
mbillard

Dalam. NET jangan pernah throw new Exception("...") (seperti yang ditunjukkan oleh penulis pertanyaan). Exception adalah jenis Exception root dan tidak seharusnya dibuang secara langsung. Alih-alih melempar salah satu jenis pengecualian .NET yang diturunkan atau membuat pengecualian kustom Anda sendiri yang berasal dari Pengecualian (atau jenis pengecualian lainnya).

Mengapa tidak membuang Pengecualian? Karena melemparkan Exception tidak melakukan apa pun untuk menggambarkan pengecualian Anda dan memaksa kode panggilan Anda untuk menulis kode seperti catch(Exception ex) { ... } yang umumnya bukan hal yang baik! :-).

4
bytedev

Hal-hal yang ingin Anda cari untuk "ditambahkan" ke pengecualian adalah elemen-elemen data yang tidak melekat dalam pengecualian atau jejak stack. Apakah itu bagian dari "pesan" atau perlu dilampirkan ketika login adalah pertanyaan yang menarik.

Seperti yang telah Anda catat, pengecualian mungkin memberi tahu Anda apa, stacktrace mungkin memberi tahu Anda di mana tetapi "mengapa" mungkin lebih terlibat (seharusnya, orang akan berharap) daripada hanya mengintip satu atau dua baris dan berkata " doh! Tentu saja ". Ini bahkan lebih benar ketika mencatat kesalahan dalam kode produksi - Saya sudah terlalu sering digigit oleh data buruk sehingga menemukan jalannya ke sistem hidup yang tidak ada dalam sistem pengujian kami. Sesuatu yang sesederhana mengetahui ID apa dari catatan dalam database yang menyebabkan (atau berkontribusi) kesalahan dapat menghemat banyak waktu.

Jadi ... Entah terdaftar atau, untuk .NET, ditambahkan ke pengumpulan data pengecualian yang dicatat (lih. @ Klip!):

  • Parameter (ini bisa sedikit menarik - Anda tidak bisa menambahkan ke pengumpulan data jika itu tidak akan bersambung dan kadang-kadang satu parameter bisa sangat kompleks)
  • Data tambahan dikembalikan oleh ADO.NET atau Linq ke SQL atau serupa (ini juga bisa sedikit menarik!).
  • Apa pun yang lain mungkin tidak terlihat.

Beberapa hal, tentu saja, Anda tidak akan tahu Anda perlu sampai Anda tidak memilikinya dalam laporan/log kesalahan awal Anda. Beberapa hal yang tidak Anda sadari bisa Anda dapatkan sampai Anda menemukan Anda membutuhkannya.

2
Murph

Apa itu Pengecualian untuk ?

(1) Memberitahu pengguna ada yang salah?

Ini harus menjadi pilihan terakhir, karena kode Anda harus bersyafaat dan menunjukkan kepada mereka sesuatu yang "lebih baik" daripada Pengecualian.

Pesan "kesalahan" harus menyatakan dengan jelas dan ringkas apa yang salah dan apa, jika ada, yang dapat dilakukan pengguna untuk pulihkan dari kondisi kesalahan.

misalnya "Tolong jangan tekan tombol ini lagi"

(2) Memberitahu Pengembang ketika terjadi kesalahan?

Ini adalah jenis hal yang Anda login ke file untuk analisis selanjutnya.
The Stack Trace akan memberi tahu Pengembang di mana kode rusak; pesan harus, sekali lagi, menunjukkan kesalahan apa .

(3) Memberitahu penangan pengecualian (mis. Kode) bahwa ada yang salah?

Jenis dari Pengecualian akan memutuskan pengendali pengecualian mana yang akan melihatnya dan properti yang didefinisikan pada objek Pengecualian akan memungkinkan pawang untuk menghadapinya.

Pesan Pengecualian adalah sama sekali tidak relevan .

0
Phill W.