Arsitektur Multi-Layered: di mana saya harus mengimplementasikan kesalahan logging \ handling?

17

Saat ini saya sedang refactoring subsistem besar dengan arsitektur multi-layered, dan saya berjuang untuk merancang strategi logging kesalahan \ penanganan yang efektif.

Katakanlah arsitektur saya terdiri dari tiga lapisan berikut:

  • Antarmuka Umum (Pengontrol MVC)
  • Lapisan Domain
  • Lapisan Akses Data

Sumber kebingungan saya adalah di mana saya harus mengimplementasikan kesalahan logging \ handling:

  1. Solusi termudah adalah menerapkan penebangan di tingkat atas (yaitu Pengendali Antarmuka Publik \ MVC). Namun ini terasa salah karena itu berarti meluapkan pengecualian melalui lapisan yang berbeda, dan kemudian mencatatnya; daripada mencatat pengecualian pada sumbernya.

  2. Mencatat pengecualian pada sumbernya jelas merupakan solusi terbaik karena saya memiliki informasi paling banyak. Masalah saya dengan hal ini adalah bahwa saya tidak dapat menangkap setiap pengecualian pada sumber tanpa menangkap SEMUA pengecualian, dan pada lapisan antarmuka domain / publik, ini akan mengarah pada menangkap pengecualian yang telah ditangkap, dicatat dan dilemparkan kembali oleh lapisan di bawah .

  3. Strategi lain yang mungkin adalah campuran # 1 dan # 2; di mana saya menangkap pengecualian khusus pada layer mereka kemungkinan besar akan dibuang (IE Catching, logging and re-throwing SqlExceptionsdi Layer Akses Data) dan kemudian mencatat pengecualian yang tidak tertangkap lebih lanjut di level atas. Namun ini juga akan mengharuskan saya untuk menangkap dan me-relog setiap pengecualian di tingkat atas, karena saya tidak dapat membedakan antara kesalahan yang telah dicatat \ ditangani dengan yang tidak.

Sekarang, jelas ini adalah masalah di sebagian besar aplikasi perangkat lunak, jadi harus ada solusi standar untuk masalah ini yang menghasilkan pengecualian yang ditangkap pada sumbernya, dan dicatat sekali; namun saya tidak bisa melihat bagaimana melakukannya sendiri.

Catatan, judul pertanyaan ini sangat mirip dengan ' Pengecualian pencatatan dalam aplikasi multi-tier " ', namun jawaban dalam posting itu kurang detail dan tidak cukup untuk menjawab pertanyaan saya.

KidCode
sumber
1
Salah satu pendekatan yang kadang-kadang saya gunakan adalah membuat subkelas Pengecualian (atau RuntimeException) saya sendiri dan membuangnya, termasuk pengecualian asli sebagai yang bersarang. Tentu saja itu berarti bahwa ketika menangkap pengecualian di tingkat atas, saya perlu memeriksa jenis pengecualian (pengecualian saya sendiri baru saja diposisikan ulang, pengecualian lain dicatat dan dimasukkan ke dalam contoh baru pengecualian saya). Tapi saya sudah bekerja solo sejak lama sehingga saya tidak bisa memberikan saran "resmi".
SJuan76
4
The easiest solution would be to implement the logging at the top level- melakukan hal ini. Mengecualikan pengecualian pada sumbernya bukan ide yang baik dan setiap aplikasi yang saya temui melakukan hal ini adalah PITA untuk debug. Seharusnya menjadi tanggung jawab penelepon untuk menangani pengecualian.
Justin
Anda tampaknya memiliki beberapa teknik implementasi / bahasa tertentu dalam pikiran, jika tidak, pernyataan Anda "pengecualian yang dicatat tidak dapat dibedakan dari yang tidak" sulit untuk dipahami. Bisakah Anda memberi lebih banyak konteks?
Vroomfondel
@Roomfondel - Anda benar. Saya menggunakan C #, dan membungkus kode di setiap Layer dengan try{ ... } catch(Exception ex) { Log(ex); }akan menghasilkan pengecualian yang sama dicatat pada setiap layer. (Sepertinya ini agak buruk untuk menangkap setiap pengecualian pada setiap lapisan dalam basis kode.)
KidCode

Jawaban:

18

Untuk pertanyaan Anda:

Solusi termudah adalah menerapkan penebangan di tingkat atas

Memiliki gelembung pengecualian hingga ke tingkat atas adalah pendekatan yang benar-benar benar dan masuk akal. Tidak satu pun dari metode lapisan yang lebih tinggi mencoba untuk melanjutkan beberapa proses setelah kegagalan, yang biasanya tidak dapat berhasil. Dan pengecualian yang dilengkapi dengan baik berisi semua informasi yang diperlukan untuk logging. Dan tidak melakukan apa-apa tentang pengecualian membantu Anda menjaga kode Anda tetap bersih dan fokus pada tugas utama alih-alih kegagalan.

Mencatat pengecualian pada sumbernya jelas merupakan solusi terbaik karena saya memiliki informasi paling banyak.

Itu setengah benar. Ya, sebagian besar informasi bermanfaat tersedia di sana. Tapi saya akan merekomendasikan untuk memasukkan semua ini ke objek pengecualian (jika belum ada di sana) daripada langsung login. Jika Anda masuk pada level rendah, Anda masih perlu membuang pengecualian untuk memberi tahu penelepon Anda bahwa Anda tidak menyelesaikan pekerjaan Anda. Ini berakhir di beberapa log dari acara yang sama.

Pengecualian

Pedoman utama saya adalah menangkap dan mencatat pengecualian di tingkat atas saja. Dan semua lapisan di bawah ini harus memastikan bahwa semua informasi kegagalan yang diperlukan akan dipindahkan ke tingkat atas. Dalam aplikasi proses tunggal misalnya di Jawa, ini sebagian besar berarti tidak mencoba / menangkap atau masuk sama sekali di luar tingkat atas.

Terkadang, Anda ingin beberapa informasi konteks termasuk dalam log pengecualian yang tidak tersedia dalam pengecualian asli, misalnya pernyataan SQL dan parameter yang dieksekusi ketika pengecualian dilemparkan. Kemudian Anda dapat menangkap pengecualian asli dan melemparkan kembali yang baru, berisi yang asli ditambah konteksnya.

Tentu saja, kehidupan nyata terkadang mengganggu:

  • Di Jawa, terkadang Anda harus menangkap pengecualian dan membungkusnya menjadi jenis pengecualian yang berbeda hanya untuk mematuhi beberapa tanda tangan metode tetap. Tetapi jika Anda melempar kembali pengecualian, pastikan yang dilempar kembali berisi semua informasi yang diperlukan untuk masuk nanti.

  • Jika Anda melewati batas antar-proses, seringkali Anda secara teknis tidak dapat mentransfer objek pengecualian lengkap termasuk jejak stack. Dan tentu saja koneksi mungkin hilang. Jadi, inilah titik di mana suatu layanan harus mencatat pengecualian dan kemudian mencoba yang terbaik untuk mengirimkan sebanyak mungkin informasi kegagalan ke klien. Layanan harus memastikan klien mendapat pemberitahuan kegagalan, baik dengan menerima respons kegagalan atau dengan menjalankan timeout jika terjadi koneksi yang terputus. Ini biasanya akan menyebabkan kegagalan yang sama untuk dicatat dua kali, sekali di dalam layanan (dengan lebih detail) dan sekali di tingkat teratas klien.

Penebangan

Saya menambahkan beberapa kalimat tentang login secara umum, tidak hanya pengecualian-logging.

Selain situasi luar biasa, Anda ingin kegiatan penting aplikasi Anda dicatat dalam log juga. Jadi, gunakan kerangka kerja logging.

Berhati-hatilah dengan level log (membaca log di mana informasi debug dan kesalahan serius tidak ditandai dengan yang berbeda karena itu menyebalkan!). Level log yang umum adalah:

  • GALAT: Beberapa fungsi gagal tidak dapat diperbaiki. Itu tidak berarti seluruh program Anda macet, tetapi beberapa tugas tidak dapat diselesaikan. Biasanya, Anda memiliki objek pengecualian yang menggambarkan kegagalan.
  • PERINGATAN: Sesuatu yang aneh terjadi, tetapi tidak menyebabkan tugas gagal (konfigurasi aneh terdeteksi, gangguan koneksi sementara menyebabkan beberapa percobaan ulang, dll.)
  • INFO: Anda ingin mengkomunikasikan beberapa tindakan program yang signifikan kepada administrator sistem lokal (memulai beberapa layanan dengan konfigurasi dan versi perangkat lunaknya, mengimpor file data ke dalam basis data, pengguna yang masuk ke sistem, Anda mendapatkan ide ...).
  • DEBUG: Hal-hal yang ingin dilihat oleh pengembang saat Anda sedang men-debug beberapa masalah (tetapi Anda tidak akan pernah tahu sebelumnya apa yang benar-benar Anda butuhkan jika ada bug khusus ini atau itu - jika Anda dapat memperkirakannya, Anda akan memperbaiki bug tersebut ). Satu hal yang selalu berguna adalah mencatat aktivitas pada antarmuka eksternal.

Dalam produksi, atur level log ke INFO. Hasilnya harus bermanfaat bagi administrator sistem sehingga ia tahu apa yang terjadi. Harapkan dia menelepon Anda untuk meminta bantuan atau memperbaiki bug untuk setiap KESALAHAN dalam log dan setengah dari PERINGATAN.

Aktifkan level DEBUG hanya selama sesi debug nyata.

Kelompokkan entri log ke dalam kategori yang sesuai (misalnya dengan nama kelas yang sepenuhnya memenuhi syarat dari kode yang menghasilkan entri), memungkinkan Anda untuk mengaktifkan log debug untuk bagian-bagian tertentu dari program Anda.

Ralf Kleberhoff
sumber
Terima kasih atas jawaban terinci seperti itu, saya sangat menghargai bagian logging juga.
KidCode
-1

Saya menguatkan diri untuk downvotes, tapi saya akan mengambil risiko dan mengatakan saya tidak yakin saya bisa setuju dengan ini.

Mengecewakan pengecualian, apalagi mencatatnya lagi, adalah upaya ekstra dengan sedikit manfaat. Tangkap pengecualian di sumber (ya, termudah), catat, tapi jangan melemparkan kembali pengecualian, cukup laporkan "kesalahan" ke pemanggil. "-1", null, string kosong, beberapa enum, apa pun. Penelepon hanya perlu tahu bahwa panggilan itu gagal, hampir tidak pernah detail yang mengerikan. Dan itu akan ada di log Anda, bukan? Dalam kasus yang jarang terjadi bahwa penelepon membutuhkan detail, silakan dan muncul, tetapi tidak sebagai default otomatis tanpa berpikir.

Tanpa syaratMonica
sumber
3
Masalah dengan melaporkan nilai kesalahan dengan pengembalian adalah: 1. Jika penelepon peduli dengan kegagalan, ia harus memeriksa nilai khusus. Tapi tunggu, apakah itu nullsenar kosong? Apakah -1 atau angka negatif apa pun? 2. Jika penelepon tidak peduli (yaitu tidak memeriksa) ini mengarah ke tindak lanjut kesalahan yang tidak terkait dengan penyebab aslinya, misalnya a NullPointerException. Atau lebih buruk: Perhitungan berlanjut dengan nilai yang salah. 3. Jika penelepon peduli tetapi programmer tidak menganggap metode ini gagal, kompiler tidak mengingatkannya. Pengecualian tidak memiliki masalah ini, baik Anda menangkap atau Anda mengubah kembali.
siegi
1
Maaf, saya harus membatalkannya. Pendekatan yang Anda gambarkan (ganti pengecualian dengan nilai pengembalian khusus) adalah yang paling canggih di tahun 1970-an, ketika bahasa C berasal, dan ada banyak alasan bagus mengapa bahasa modern memiliki pengecualian, dan bagi saya yang utama adalah bahwa menggunakan pengecualian dengan benar membuatnya lebih mudah untuk menulis kode yang kuat. Dan "Perkecualian pengecualian Anda [...] adalah upaya ekstra [...]" jelas salah: lakukan saja apa-apa tentang pengecualian, dan mereka akan meluap sendiri.
Ralf Kleberhoff