Ada banyak dari bahan luar sana yang menunjukkan bahwa mencetak jejak tumpukan pengecualian adalah praktek yang buruk.
Misalnya dari pemeriksaan RegexpSingleline di Checkstyle:
Pemeriksaan ini dapat digunakan [...] untuk menemukan praktik buruk umum seperti memanggil ex.printStacktrace ()
Namun, saya berjuang untuk menemukan di mana saja yang memberikan alasan yang sah mengapa karena tentu saja jejak tumpukan sangat berguna dalam melacak apa yang menyebabkan pengecualian. Hal-hal yang saya ketahui:
Jejak tumpukan tidak boleh terlihat oleh pengguna akhir (untuk pengalaman pengguna dan tujuan keamanan)
Menghasilkan jejak tumpukan adalah proses yang relatif mahal (meskipun tidak mungkin menjadi masalah dalam keadaan paling 'luar biasa')
Banyak kerangka kerja pencatatan akan mencetak jejak tumpukan untuk Anda (milik kami tidak dan tidak, kami tidak dapat mengubahnya dengan mudah)
Mencetak jejak tumpukan bukan merupakan penanganan kesalahan. Ini harus dikombinasikan dengan pencatatan informasi dan penanganan pengecualian lainnya.
Apa alasan lain yang ada untuk menghindari pencetakan jejak tumpukan dalam kode Anda?
sumber
.printStackTrace()
kode Anda :)Jawaban:
Throwable.printStackTrace()
menulis jejak stack keSystem.err
PrintStream. TheSystem.err
sungai dan mendasari standar "error" output stream dari proses JVM dapat diarahkan olehSystem.setErr()
perubahan tujuan yang ditunjukkan olehSystem.err
./dev/null
.Sebagai kesimpulan dari hal di atas, memohon hanya
Throwable.printStackTrace()
merupakan perilaku penanganan pengecualian yang valid (tidak baik / hebat)System.err
dipindahkan selama masa aplikasi,System.err
(dan aliran output kesalahan standar JVM).Dalam kebanyakan kasus, kondisi di atas tidak memuaskan. Seseorang mungkin tidak menyadari kode lain yang berjalan di JVM, dan orang tidak dapat memprediksi ukuran file log atau durasi runtime proses, dan praktik logging yang dirancang dengan baik akan berputar di sekitar menulis file log "machine-parseable" (a lebih disukai tetapi fitur opsional dalam logger) di tujuan yang dikenal, untuk membantu dalam dukungan.
Akhirnya, orang harus ingat bahwa output dari
Throwable.printStackTrace()
pasti akan disisipkan dengan konten lain yang ditulisSystem.err
(dan mungkin bahkanSystem.out
jika keduanya diarahkan ke file / perangkat yang sama). Ini adalah gangguan (untuk aplikasi single-threaded) yang harus dihadapi, karena data di sekitar pengecualian tidak mudah diuraikan dalam peristiwa semacam itu. Lebih buruk lagi, sangat mungkin bahwa aplikasi multi-threaded akan menghasilkan log yang sangat membingungkan karenaThrowable.printStackTrace()
tidak aman untuk thread .Tidak ada mekanisme sinkronisasi untuk menyinkronkan penulisan jejak stack
System.err
saat beberapa utas meminta secaraThrowable.printStackTrace()
bersamaan. Menyelesaikan ini sebenarnya membutuhkan kode Anda untuk melakukan sinkronisasi pada monitor yang terkait dengan instance. Jika Anda ingin memiliki jaminan yang sama untuk menggunakan catatan log non-interleavedSystem.err
(dan jugaSystem.out
, jika file / perangkat tujuan sama), dan itu adalah harga yang cukup mahal untuk membayar kewarasan file log. Untuk mengambil contoh, kelasConsoleHandler
danStreamHandler
bertanggung jawab untuk menambahkan catatan log ke konsol, di fasilitas logging yang disediakan olehjava.util.logging
; operasi aktual penerbitan catatan log disinkronkan - setiap utas yang berupaya untuk mempublikasikan catatan log juga harus mendapatkan kunci pada monitor yang terkait denganStreamHandler
System.out
/System.err
, Anda harus memastikan hal yang sama - pesan diterbitkan ke stream ini secara serial.Mempertimbangkan semua hal di atas, dan skenario yang sangat terbatas di mana
Throwable.printStackTrace()
sebenarnya berguna, seringkali ternyata memohon itu adalah praktik yang buruk.Memperluas argumen di salah satu paragraf sebelumnya, itu juga merupakan pilihan yang buruk untuk digunakan
Throwable.printStackTrace
bersama dengan logger yang menulis ke konsol. Ini sebagian, karena alasan bahwa logger akan menyinkronkan pada monitor yang berbeda, sementara aplikasi Anda akan (mungkin, jika Anda tidak ingin catatan log yang disisipkan) melakukan sinkronisasi pada monitor yang berbeda. Argumen ini juga berlaku ketika Anda menggunakan dua penebang berbeda yang menulis ke tujuan yang sama, dalam aplikasi Anda.sumber
System.out.println
danThrowable.printStackTrace
dan tentu saja, penilaian pengembang diperlukan. Saya agak khawatir bahwa bagian tentang keamanan benang tidak terjawab. Jika Anda melihat sebagian besar implementasi logger, Anda akan melihat bahwa mereka menyinkronkan bagian di mana catatan log ditulis (bahkan ke konsol), meskipun mereka tidak mendapatkan monitor padaSystem.err
atauSystem.out
.Anda menyentuh beberapa masalah di sini:
Ya, harus dapat diakses untuk mendiagnosis masalah pengguna akhir, tetapi pengguna akhir tidak boleh melihatnya karena dua alasan:
Menghasilkan jejak tumpukan terjadi ketika pengecualian sedang dibuat / dilempar (itu sebabnya melempar pengecualian datang dengan harga), pencetakan tidak terlalu mahal. Bahkan Anda dapat mengganti
Throwable#fillInStackTrace()
pengecualian kustom Anda secara efektif membuat melemparkan pengecualian hampir semurah pernyataan GOTO sederhana.Poin yang sangat bagus. Masalah utama di sini adalah: jika framework mencatat pengecualian untuk Anda, jangan lakukan apa-apa (tapi pastikan ya!) Jika Anda ingin mencatat pengecualian sendiri, gunakan framework logging seperti Logback atau Log4J , untuk tidak meletakkannya di konsol mentah karena sangat sulit untuk mengendalikannya.
Dengan kerangka logging, Anda dapat dengan mudah mengarahkan jejak stack ke file, konsol atau bahkan mengirimnya ke alamat email tertentu. Dengan hardcoded
printStackTrace()
Anda harus hidup dengansysout
.Sekali lagi: masuk
SQLException
dengan benar (dengan jejak tumpukan penuh, menggunakan kerangka logging) dan tunjukkan bagus: " Maaf, kami saat ini tidak dapat memproses permintaan Anda " pesan. Apakah Anda benar-benar berpikir pengguna tertarik pada alasannya? Pernahkah Anda melihat layar kesalahan StackOverflow? Ini sangat lucu, tetapi tidak mengungkapkan detail apa pun . Namun itu memastikan pengguna bahwa masalahnya akan diselidiki.Tetapi dia akan segera menghubungi Anda dan Anda harus dapat mendiagnosis masalahnya. Jadi, Anda membutuhkan keduanya: pencatatan pengecualian yang tepat dan pesan yang ramah pengguna.
Untuk menyelesaikannya: selalu catat pengecualian (lebih disukai menggunakan kerangka logging ), tetapi jangan memaparkannya kepada pengguna akhir. Pikirkan baik-baik dan tentang pesan kesalahan di GUI Anda, tampilkan jejak tumpukan hanya dalam mode pengembangan.
sumber
Hal pertama printStackTrace () tidak mahal seperti yang Anda sebutkan, karena jejak stack diisi ketika pengecualian dibuat sendiri.
Idenya adalah untuk melewatkan apa pun yang masuk ke log melalui kerangka logger, sehingga logging dapat dikontrol. Karenanya alih-alih menggunakan printStackTrace, cukup gunakan sesuatu seperti
Logger.log(msg, exception);
sumber
Mencetak jejak stack pengecualian itu sendiri bukan merupakan praktik yang buruk, tetapi hanya mencetak jejak stace ketika pengecualian terjadi mungkin masalah di sini - sering kali, hanya mencetak jejak stack tidak cukup.
Juga, ada kecenderungan untuk mencurigai bahwa penanganan pengecualian yang tepat tidak dilakukan jika semua yang dilakukan dalam suatu
catch
blok adalah ae.printStackTrace
. Penanganan yang tidak tepat bisa berarti masalah yang terbaik sedang diabaikan, dan paling buruk program yang terus dijalankan dalam keadaan yang tidak ditentukan atau tidak terduga.Contoh
Mari kita perhatikan contoh berikut:
Di sini, kami ingin melakukan beberapa pemrosesan inisialisasi sebelum melanjutkan ke beberapa pemrosesan yang mengharuskan inisialisasi dilakukan.
Dalam kode di atas, pengecualian seharusnya ditangkap dan ditangani dengan benar untuk mencegah program melanjutkan ke
continueProcessingAssumingThatTheStateIsCorrect
metode yang dapat kita asumsikan akan menyebabkan masalah.Dalam banyak kasus,
e.printStackTrace()
merupakan indikasi bahwa beberapa pengecualian sedang ditelan dan pemrosesan diizinkan untuk melanjutkan seolah-olah tidak ada masalah setiap terjadi.Mengapa ini menjadi masalah?
Mungkin salah satu alasan terbesar bahwa penanganan pengecualian yang buruk menjadi lebih umum adalah karena bagaimana IDE seperti Eclipse akan secara otomatis menghasilkan kode yang akan melakukan
e.printStackTrace
penanganan pengecualian:(Di atas adalah yang
try-catch
dihasilkan secara otomatis oleh Eclipse untuk menangani yangInterruptedException
dilemparkan olehThread.sleep
.)Untuk sebagian besar aplikasi, hanya mencetak jejak stack ke kesalahan standar mungkin tidak akan cukup. Dalam banyak kasus, penanganan pengecualian yang tidak benar dapat menyebabkan aplikasi berjalan dalam keadaan yang tidak terduga dan dapat mengarah pada perilaku yang tidak terduga dan tidak terdefinisi.
sumber
Saya pikir daftar alasan Anda cukup komprehensif.
Salah satu contoh buruk yang saya temui lebih dari sekali adalah seperti ini:
Masalah dengan kode di atas adalah bahwa penanganan seluruhnya terdiri dari
printStackTrace
panggilan: pengecualian tidak benar-benar ditangani dengan baik dan tidak diizinkan untuk melarikan diri.Di sisi lain, sebagai aturan saya selalu mencatat jejak stack setiap kali ada pengecualian yang tidak terduga dalam kode saya. Selama bertahun-tahun kebijakan ini telah menyelamatkan saya banyak waktu debugging.
Akhirnya, dengan nada yang lebih ringan, Pengecualian Sempurna Allah .
sumber
printStackTrace()
mencetak ke konsol. Dalam pengaturan produksi, tidak ada yang pernah menonton itu. Suraj benar, harus meneruskan informasi ini ke logger.sumber
Dalam aplikasi server stacktrace meledakkan file stdout / stderr Anda. Mungkin menjadi lebih besar dan lebih besar dan diisi dengan data yang tidak berguna karena biasanya Anda tidak memiliki konteks dan tidak ada cap waktu dan sebagainya.
misalnya catalina.out saat menggunakan tomcat sebagai wadah
sumber
Ini bukan praktik buruk karena ada sesuatu yang 'salah' tentang PrintStackTrace (), tetapi karena 'kode bau'. Sebagian besar waktu panggilan PrintStackTrace () ada karena seseorang gagal menangani pengecualian dengan benar. Setelah Anda menangani pengecualian dengan cara yang benar, Anda biasanya tidak peduli lagi dengan StackTrace.
Selain itu, menampilkan stacktrace pada stderr umumnya hanya berguna ketika debugging, bukan dalam produksi karena sangat sering stderr tidak ke mana-mana. Logging lebih masuk akal. Tetapi hanya mengganti PrintStackTrace () dengan logging pengecualian masih menyisakan Anda dengan aplikasi yang gagal tetapi terus berjalan seperti tidak ada yang terjadi.
sumber
Seperti beberapa orang telah disebutkan di sini masalahnya adalah dengan menelan pengecualian dalam kasus Anda hanya memanggil
e.printStackTrace()
dicatch
blok. Itu tidak akan menghentikan eksekusi utas dan akan berlanjut setelah blok coba seperti dalam kondisi normal.Alih-alih itu Anda perlu mencoba untuk memulihkan dari pengecualian (jika itu dapat dipulihkan), atau untuk melempar
RuntimeException
, atau untuk menggelembungkan pengecualian ke pemanggil untuk menghindari crash diam (misalnya, karena konfigurasi logger yang tidak tepat).sumber
Untuk menghindari masalah aliran output kusut yang dirujuk oleh @Vineet Reynolds
Anda dapat mencetaknya di stdout:
e.printStackTrace(System.out);
sumber