Haruskah logger statis pribadi atau tidak

103

Haruskah logger dinyatakan statis atau tidak? Biasanya saya telah melihat dua jenis deklarasi untuk seorang logger:

    dilindungi Log log = baru Log4JLogger (aClass.class);

atau

    pribadi Log log statis = baru Log4JLogger (aClass.class);

Mana yang harus digunakan? apa pro dan kontra dari keduanya?

Drahakar
sumber
1
Penebangan adalah masalah lintas sektoral. Gunakan Aspects dan pertanyaannya diperdebatkan.
Dave Jarvis
4
staticadalah satu referensi per kelas. non-statis adalah satu referensi per instance (+ inisialisasi). Jadi dalam beberapa kasus, yang terakhir datang pada dampak memori yang signifikan jika Anda memiliki banyak contoh. Jangan pernah menggunakan non-statis di objek yang sering . Saya selalu menggunakan versi statis. (yang harus diberi huruf besar LOG )
Memiliki QUIT - Anony-Mousse
2
seperti yang sudah disarankan, gunakan AOP dan anotasi, misalnya: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256
1
RobertHume versi statis adalah dengan menggunakan konstan. Itulah mengapa itu harus diberi huruf besar.
Memiliki QUIT - Anony-Mousse
2
Tidak, itu harus private static final Log loghuruf kecil. Logger bukanlah sebuah konstanta, logger adalah objek akhir statis (yang dapat dimutasi). Secara pribadi saya selalu menggunakan logger.
osundblad

Jawaban:

99

Keuntungan dari bentuk non-statis adalah Anda dapat mendeklarasikannya dalam kelas dasar (abstrak) seperti berikut tanpa khawatir bahwa nama kelas yang tepat akan digunakan:

protected Log log = new Log4JLogger(getClass());

Namun kerugiannya jelas bahwa instance logger baru akan dibuat untuk setiap instance kelas. Ini mungkin tidak mahal, tetapi menambah biaya tambahan yang signifikan. Jika Anda ingin menghindari ini, Anda ingin menggunakan staticformulir sebagai gantinya. Tetapi kerugiannya adalah pada gilirannya Anda harus mendeklarasikannya di setiap kelas individu dan berhati-hati di setiap kelas bahwa nama kelas yang tepat telah digunakan selama konstruksi logger karena getClass()tidak dapat digunakan dalam konteks statis. Namun, di IDE rata-rata Anda dapat membuat template pelengkapan otomatis untuk ini. Misalnya logger+ ctrl+space.

Di sisi lain, jika Anda mendapatkan logger oleh pabrik yang pada gilirannya dapat menyimpan logger yang sudah ada, maka menggunakan formulir non-statis tidak akan menambah banyak overhead. Log4j misalnya memiliki a LogManageruntuk tujuan ini.

protected Log log = LogManager.getLogger(getClass());
BalusC
sumber
6
Deklarasikan abstract Log getLogger();di kelas abstrak. Terapkan metode ini, mengembalikan logger statis untuk instance tertentu. Tambahkan private final static Log LOG = LogManager.getLogger(Clazz.class);ke template kelas IDE Anda.
Memiliki QUIT - Anony-Mousse
2
Untuk slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt
3
@BalusC Masalah dengan meneruskan getClass () ke metode getLogger adalah ia mengembalikan kelas dari instance saat ini. Biasanya lebih diinginkan logging dikaitkan dengan kelas di mana kodenya. Misalnya jika kode logging ada di kelas Parent maka kita ingin logging dikaitkan dengan Parent meskipun instance pelaksananya adalah dan instance kelas Child yang merupakan sub-kelas Parent. Dengan getClass () itu akan dikaitkan dengan Anak, secara tidak benar
atau
@inor: "salah"? Jika Anda tidak ingin mengabstraksi kelas, sebaiknya Anda tidak menggunakan getClass () yang diwarisi terlebih dahulu. Ada pengembang yang merasa benar dan berguna karena mengungkapkan informasi di mana subclass persis logika telah dilakukan.
BalusC
2
@ BalusC getLogger (getClass ()) menghasilkan selalu pencatatan nama sub-kelas secara tidak benar. Kelas pencatatan harus selalu melakukan getLogger (Clazz.class) untuk mengaitkan pencatatan yang dibuat oleh kode di kelas Clazz. Pengembang yang ingin mengetahui sub-kelas mana yang mengeksekusi (Misalnya SubClazz meluas Clazz) harus melakukannya di SubClazz: getLogger (SubClazz.class) dan sesuatu seperti: log.info ("memanggil <sesuatu di kelas basis saya>");
atau
44

Saya dulu berpikir bahwa semua penebang harus statis; namun, artikel di wiki.apache.org ini mengangkat beberapa masalah memori penting, terkait kebocoran classloader. Mendeklarasikan logger sebagai statis mencegah kelas deklarasi (dan classloader terkait) dikumpulkan sampah di kontainer J2EE yang menggunakan classloader bersama. Ini akan menghasilkan kesalahan PermGen jika Anda menerapkan ulang aplikasi cukup sering.

Saya tidak benar-benar melihat cara untuk mengatasi masalah kebocoran classloader ini, selain menyatakan penebang sebagai non-statis.

piepera.dll
sumber
4
Saya menduga bidang statis akan mengalami masalah kebocoran memori juga. Non-statis mungkin memiliki masalah kinerja seperti yang dikatakan orang lain. Lalu bagaimana cara yang ideal?
liang
@piepera masalah utama yang dijelaskan dalam artikel yang Anda rujuk adalah kemampuan untuk mengontrol tingkat pencatatan di setiap aplikasi saat "mempertimbangkan kasus saat kelas yang menggunakan" private static Log log = "disebarkan melalui ClassLoader yang merupakan keturunan dari beberapa yang seharusnya "aplikasi" independen. Saya tidak melihat itu sebagai masalah karena dalam situasi khusus ini aplikasi memiliki "kesamaan" dan dalam "kesamaan" tingkat logging untuk kelas diputuskan dan ya, ini berlaku untuk semua aplikasi ... tapi tetap perlu diingat bahwa kelas ini [dimuat] di luar aplikasi ini
atau
17

Perbedaan terpenting adalah bagaimana pengaruhnya terhadap file log Anda: di kategori manakah log masuk?

  • Dalam pilihan pertama Anda, log dari sebuah subclass berakhir di kategori superclass tersebut. Itu tampak sangat berlawanan dengan intuisi bagi saya.
  • Ada varian dari kasus pertama Anda:

    dilindungi Log log = baru Log4JLogger (getClass ());

    Dalam hal ini, kategori log Anda mengatakan objek mana yang sedang digunakan kode login.

  • Dalam pilihan kedua Anda (private statis), kategori log adalah kelas yang berisi kode logging. Jadi biasanya kelas yang melakukan hal yang sedang dicatat.

Saya sangat merekomendasikan opsi terakhir itu. Ini memiliki kelebihan berikut, dibandingkan dengan solusi lain:

  • Ada hubungan langsung antara log dan kode. Sangat mudah untuk menemukan kembali dari mana pesan log berasal.
  • Jika seseorang harus menyesuaikan level logging (yang dilakukan per kategori), biasanya karena mereka tertarik (atau tidak) pada beberapa pesan tertentu, yang ditulis oleh kelas tertentu. Jika kategorinya bukan kelas yang menulis pesan, akan lebih sulit untuk menyesuaikan levelnya.
  • Anda dapat masuk dengan metode statis
  • Logger hanya perlu diinisialisasi (atau dicari) sekali per kelas, jadi saat memulai, bukan untuk setiap instance yang dibuat.

Ini juga memiliki kekurangan:

  • Itu harus dideklarasikan di setiap kelas tempat Anda mencatat pesan (tidak ada penggunaan ulang pencatat superclass).
  • Anda harus berhati-hati dalam memasukkan nama kelas yang benar saat menginisialisasi logger. (Tapi IDE yang bagus akan mengurusnya untuk Anda).
Wouter Coekaerts
sumber
4

Gunakan kontrol inversi dan berikan logger ke konstruktor. Jika Anda membuat logger di dalam kelas, Anda akan bersenang-senang dengan pengujian unit Anda. Anda sedang menulis tes unit bukan?

Wayne Allen
sumber
5
Tes unit yang memeriksa logging yang Anda hasilkan terdengar tidak berguna dan sangat rapuh.
Michael
1
Kegunaan tergantung pada sistem yang diuji. Terkadang hanya logging yang dapat Anda akses.
Wayne Allen
@Wayne Allen ketika Anda melakukan tes unit, menurut definisi, Anda juga memiliki hasil tes. Apakah Anda menyarankan situasi di mana seseorang melakukan tes unit tetapi tidak memiliki hasil tes? hanya memiliki log?
atau
membuat logger di dalam kelas tidak menimbulkan masalah. Dapatkah Anda menunjukkan contoh sederhana yang sulit bagi UT karena kelas tersebut membuat loggernya sendiri?
atau
1
Tentu. Bagaimana dengan logger yang mengirim email. Tidak ingin melakukannya setiap kali Anda menjalankan pengujian. Ditambah bagaimana Anda menegaskan efek sampingnya?
Wayne Allen