Untuk pertama kalinya dalam hidup saya, saya menemukan diri saya dalam posisi di mana saya sedang menulis Java API yang akan bersumber terbuka. Semoga bisa dimasukkan dalam banyak proyek lainnya.
Untuk logging saya (dan memang orang yang bekerja dengan saya) selalu menggunakan JUL (java.util.logging) dan tidak pernah memiliki masalah dengan itu. Namun sekarang saya perlu memahami secara lebih rinci apa yang harus saya lakukan untuk pengembangan API saya. Saya telah melakukan beberapa penelitian tentang ini dan dengan informasi yang saya dapatkan saya menjadi semakin bingung. Karenanya pos ini.
Karena saya berasal dari JUL, saya bias dalam hal itu. Pengetahuan saya tentang sisanya tidak sebesar itu.
Dari penelitian yang saya lakukan, saya menemukan alasan mengapa orang tidak suka JUL:
"Saya mulai berkembang di Jawa jauh sebelum Sun merilis JUL dan lebih mudah bagi saya untuk melanjutkan logging-framework-X daripada belajar sesuatu yang baru" . Hmm. Saya tidak bercanda, ini sebenarnya yang dikatakan orang. Dengan argumen ini kita semua bisa melakukan COBOL. (Namun saya pasti bisa menghubungkan ini menjadi cowok malas sendiri)
"Saya tidak suka nama level logging di JUL" . Ok, serius, ini tidak cukup alasan untuk memperkenalkan ketergantungan baru.
"Saya tidak suka format standar keluaran dari JUL" . Hmm. Ini hanya konfigurasi. Anda bahkan tidak perlu melakukan apapun secara bijaksana. (benar, di masa lalu Anda mungkin harus membuat kelas Formatter Anda sendiri untuk memperbaikinya).
"Saya menggunakan perpustakaan lain yang juga menggunakan logging-framework-X jadi saya pikir lebih mudah hanya menggunakan yang itu" . Ini argumen melingkar, bukan? Mengapa 'semua orang' menggunakan logging-framework-X dan bukan JULI?
"Semua orang menggunakan logging-framework-X" . Ini bagi saya hanyalah kasus khusus di atas. Mayoritas tidak selalu benar.
Jadi pertanyaan besar sebenarnya adalah mengapa tidak JULI? . Apa yang saya lewatkan? The raison d'être untuk fasad logging (SLF4J, JCL) adalah bahwa beberapa implementasi logging telah ada secara historis dan alasan untuk itu benar-benar kembali ke era sebelum JUL seperti yang saya lihat. Jika JUL sempurna maka fasad logging tidak akan ada, atau apa? Untuk membuat hal-hal yang lebih membingungkan, JUL adalah suatu fasad itu sendiri, memungkinkan Handler, Formatters dan bahkan LogManager untuk ditukar.
Daripada merangkul banyak cara untuk melakukan hal yang sama (logging), bukankah kita seharusnya mempertanyakan mengapa mereka diperlukan? (dan lihat apakah alasan itu masih ada)
Ok, penelitian saya sejauh ini telah mengarah pada beberapa hal yang dapat saya lihat mungkin masalah nyata dengan JUL:
Performa . Ada yang mengatakan bahwa kinerja di SLF4J lebih unggul dari yang lain. Bagi saya ini adalah optimasi yang prematur. Jika Anda perlu mencatat ratusan megabyte per detik maka saya tidak yakin Anda berada di jalur yang benar. JUL juga telah berevolusi dan tes yang Anda lakukan di Java 1.4 mungkin tidak lagi benar. Anda dapat membacanya di sini dan perbaikan ini telah membuatnya menjadi Java 7. Banyak juga yang berbicara tentang overhead dari penggabungan string dalam metode logging. Namun logging berbasis template menghindari biaya ini dan ada juga di JUL. Secara pribadi saya tidak pernah benar-benar menulis logging berbasis template. Terlalu malas untuk itu. Sebagai contoh jika saya melakukan ini dengan JULI:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
IDE saya akan memperingatkan saya dan meminta izin untuk mengubahnya menjadi:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. yang tentu saja akan saya terima. Izin diberikan ! Terima kasih untuk bantuannya.
Jadi saya tidak benar-benar menulis pernyataan seperti itu sendiri, yang dilakukan oleh IDE.
Kesimpulannya pada masalah kinerja saya belum menemukan apa pun yang menunjukkan bahwa kinerja JUL tidak ok dibandingkan dengan kompetisi.
Konfigurasi dari classpath . JUL out-of-the-box tidak dapat memuat file konfigurasi dari classpath. Ini adalah beberapa baris kode untuk membuatnya melakukannya. Saya bisa melihat mengapa ini mungkin mengganggu tetapi solusinya pendek dan sederhana.
Ketersediaan penangan keluaran . JUL hadir dengan 5 penangan keluaran out-of-the-box: konsol, aliran file, soket dan memori. Ini dapat diperpanjang atau yang baru dapat ditulis. Misalnya, ini dapat ditulis ke UNIX / Linux Syslog dan Windows Event Log. Saya pribadi tidak pernah memiliki persyaratan ini atau saya melihatnya digunakan tetapi saya pasti bisa menghubungkan mengapa itu mungkin fitur yang berguna. Logback dilengkapi dengan appender untuk Syslog misalnya. Masih saya akan membantah itu
- 99,5% dari kebutuhan untuk destinasi keluaran dicakup oleh apa yang ada di JUL out-of-the-box.
- Kebutuhan khusus dapat dipenuhi oleh penangan khusus di atas JUL daripada di atas sesuatu yang lain. Tidak ada bagi saya yang menunjukkan bahwa dibutuhkan lebih banyak waktu untuk menulis handler keluaran Syslog untuk JUL daripada yang dilakukan untuk kerangka kerja logging lain.
Saya benar-benar khawatir ada sesuatu yang saya abaikan. Penggunaan fasad penebangan dan implementasi penebangan selain JUL sangat luas sehingga saya harus sampai pada kesimpulan bahwa saya yang tidak mengerti. Itu bukan pertama kalinya, saya khawatir. :-)
Jadi apa yang harus saya lakukan dengan API saya? Saya ingin menjadi sukses. Tentu saja saya bisa hanya "mengikuti arus" dan menerapkan SLF4J (yang tampaknya paling populer akhir-akhir ini) tetapi demi saya sendiri, saya masih perlu memahami persis apa yang salah dengan JULI hari ini yang menjamin semua fuzz? Apakah saya akan menyabot diri dengan memilih JUL untuk perpustakaan saya?
Menguji kinerja
(bagian ditambahkan oleh nolan600 pada 07-JUL-2012)
Ada referensi di bawah ini dari Ceki tentang parametri SLF4 menjadi 10 kali atau lebih cepat daripada JUL. Jadi saya sudah mulai melakukan beberapa tes sederhana. Sepintas klaim itu tentu benar. Berikut adalah hasil awal (tapi baca terus!):
- Waktu pelaksanaan SLF4J, backend Logback: 1515
- Waktu eksekusi SLF4J, backend JUL: 12938
- Waktu eksekusi JUL: 16911
Angka-angka di atas adalah msec jadi lebih sedikit lebih baik. Jadi perbedaan kinerja 10 kali dengan yang pertama sebenarnya cukup dekat. Reaksi awal saya: Banyak sekali!
Inilah inti dari tes ini. Seperti dapat dilihat integer dan string dibangun dalam satu loop yang kemudian digunakan dalam pernyataan log:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(Saya ingin pernyataan log memiliki tipe data primitif (dalam hal ini int) dan tipe data yang lebih kompleks (dalam hal ini sebuah String). Tidak yakin itu penting tetapi Anda memilikinya.)
Pernyataan log untuk SLF4J:
logger.info("Logging {} and {} ", i, someString);
Pernyataan log untuk JUL:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
JVM 'menghangat' dengan tes yang sama dilakukan sekali sebelum pengukuran yang sebenarnya dilakukan. Java 1.7.03 digunakan pada Windows 7. Versi terbaru SLF4J (v1.6.6) dan Logback (v1.0.6) digunakan. Stdout dan stderr dialihkan ke perangkat nol.
Namun, hati-hati sekarang, ternyata JUL menghabiskan sebagian besar waktunya getSourceClassName()
karena JUL secara default mencetak nama kelas sumber dalam output, sementara Logback tidak. Jadi kami membandingkan apel dan jeruk. Saya harus melakukan tes lagi dan mengkonfigurasi implementasi logging dengan cara yang sama sehingga mereka benar-benar menghasilkan hal yang sama. Namun saya menduga bahwa SLF4J + Logback masih akan keluar di atas tetapi jauh dari angka awal seperti yang diberikan di atas. Tetap disini.
Btw: Tes ini pertama kali saya benar-benar bekerja dengan SLF4J atau Logback. Pengalaman yang menyenangkan. JUL tentu jauh lebih tidak ramah ketika Anda memulai.
Menguji kinerja (bagian 2)
(bagian ditambahkan oleh nolan600 pada 08-JUL-2012)
Ternyata tidak masalah untuk kinerja bagaimana Anda mengkonfigurasi pola Anda di JUL, yaitu apakah itu termasuk nama sumber atau tidak. Saya mencoba dengan pola yang sangat sederhana:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
dan itu tidak mengubah timing di atas sama sekali. Profiler saya mengungkapkan bahwa logger masih menghabiskan banyak waktu dalam panggilan getSourceClassName()
bahkan jika ini bukan bagian dari pola saya. Polanya tidak masalah.
Oleh karena itu saya menyimpulkan pada masalah kinerja bahwa setidaknya untuk pernyataan log berbasis template yang diuji tampaknya ada kira-kira faktor 10 dalam perbedaan kinerja nyata antara JUL (lambat) dan SLF4J + Logback (cepat). Seperti kata Ceki.
Saya juga bisa melihat hal lain yaitu bahwa getLogger()
panggilan SLF4J jauh lebih mahal daripada JUL. (95 ms vs 0,3 ms jika profiler saya akurat). Ini masuk akal. SLF4J harus melakukan beberapa waktu untuk mengikat implementasi logging yang mendasarinya. Ini tidak membuatku takut. Panggilan ini harus agak jarang terjadi dalam masa aplikasi. Tahan luntur harus dalam panggilan log yang sebenarnya.
Kesimpulan akhir
(bagian ditambahkan oleh nolan600 pada 08-JUL-2012)
Terima kasih atas semua jawaban Anda. Bertentangan dengan apa yang awalnya saya pikir saya akhirnya memutuskan untuk menggunakan SLF4J untuk API saya. Ini didasarkan pada sejumlah hal dan masukan Anda:
Ini memberikan fleksibilitas untuk memilih implementasi log pada waktu penyebaran.
Masalah dengan kurangnya fleksibilitas konfigurasi JUL saat dijalankan di dalam server aplikasi.
SLF4J tentu jauh lebih cepat seperti yang dijelaskan di atas khususnya jika Anda memasangkannya dengan Logback. Bahkan jika ini hanya tes kasar saya punya alasan untuk percaya bahwa lebih banyak upaya telah dilakukan untuk optimasi pada SLF4J + Logback daripada pada JUL.
Dokumentasi. Dokumentasi untuk SLF4J jauh lebih komprehensif dan tepat.
Fleksibilitas pola. Ketika saya melakukan tes saya menetapkan untuk JUL meniru pola default dari Logback. Pola ini termasuk nama utas. Ternyata JULI tidak bisa melakukan ini di luar kotak. Ok, saya belum melewatkannya sampai sekarang, tetapi saya tidak berpikir itu adalah hal yang harus hilang dari kerangka log. Titik!
Sebagian besar (atau banyak) proyek Java saat ini menggunakan Maven sehingga menambahkan ketergantungan bukanlah hal yang besar terutama jika ketergantungan itu agak stabil, yaitu tidak selalu mengubah API-nya. Ini tampaknya berlaku untuk SLF4J. Gelas dan teman SLF4J juga berukuran kecil.
Jadi hal aneh yang terjadi adalah saya benar-benar kesal dengan JUL setelah bekerja sedikit dengan SLF4J. Saya masih menyesal bahwa ini harus dengan JUL. Juli memang jauh dari sempurna, tetapi jenis pekerjaan yang dilakukannya. Tidak cukup baik. Hal yang sama dapat dikatakan tentang Properties
sebagai contoh tetapi kami tidak berpikir tentang abstrak sehingga orang dapat menyambungkan pustaka konfigurasi mereka sendiri dan apa pun yang Anda miliki. Saya pikir alasannya adalah yang Properties
datang tepat di atas bar sementara yang sebaliknya berlaku untuk JUL hari ini ... dan di masa lalu itu datang pada nol karena tidak ada.
InternalLoggerFactory.java
.java.lang.System.Logger
, yang merupakan antarmuka , yang dapat diarahkan ke kerangka kerja logging aktual apa pun yang Anda inginkan, selama kerangka itu berhasil dan menyediakan implementasi antarmuka itu. Dikombinasikan dengan modularisasi, Anda bahkan bisa menggunakan aplikasi dengan JRE yang dibundel tidak berisijava.util.logging
, jika Anda lebih suka kerangka kerja yang berbeda.Jawaban:
Penafian : Saya adalah pendiri proyek log4j, SLF4J dan logback.
Ada alasan obyektif untuk memilih SLF4J. Pertama, SLF4J memungkinkan pengguna akhir kebebasan untuk memilih kerangka kerja logging yang mendasarinya . Selain itu, pengguna yang berpengalaman cenderung lebih suka logback yang menawarkan kemampuan di luar log4j , dengan jul yang jauh tertinggal. Fitur-fitur jul mungkin cukup untuk beberapa pengguna tetapi bagi banyak orang lain tidak. Singkatnya, jika logging penting bagi Anda, Anda ingin menggunakan SLF4J dengan logback sebagai implementasi yang mendasarinya. Jika logging tidak penting, jul baik-baik saja.
Namun, sebagai pengembang oss, Anda harus memperhitungkan preferensi pengguna dan bukan hanya preferensi Anda. Oleh karena itu Anda harus mengadopsi SLF4J bukan karena Anda yakin bahwa SLF4J lebih baik daripada jul tetapi karena sebagian besar pengembang Java saat ini (Juli 2012) lebih memilih SLF4J sebagai API logging mereka. Jika pada akhirnya Anda memutuskan untuk tidak peduli dengan pendapat umum, pertimbangkan fakta-fakta berikut:
Dengan demikian, memegang "fakta keras" di atas opini publik, meskipun tampak berani, adalah kekeliruan logis dalam kasus ini.
Jika masih tidak yakin, JB Nizet membuat argumen tambahan dan kuat:
Jika karena alasan apa pun Anda membenci API SLF4J dan menggunakannya akan memadamkan kesenangan dari pekerjaan Anda, maka tentu saja berlaku untuk jul. Bagaimanapun, ada cara untuk mengarahkan jul ke SLF4J .
Omong-omong, parametri jul setidaknya 10 kali lebih lambat dari SLF4J yang akhirnya membuat perbedaan nyata.
sumber
java.util.logging
diperkenalkan di Jawa 1.4. Ada kegunaan untuk logging sebelum itu, itu sebabnya banyak API logging lain ada. API mana yang banyak digunakan sebelum Java 1.4 dan karenanya memiliki pangsa pasar yang hebat yang tidak hanya turun ke 0 saat 1.4 dirilis.JULI tidak memulai semua yang hebat, banyak hal yang Anda sebutkan di mana jauh lebih buruk di 1,4 dan hanya menjadi lebih baik di 1,5 (dan saya kira di 6 juga, tapi saya tidak terlalu yakin).
JUL tidak cocok untuk beberapa aplikasi dengan konfigurasi berbeda di JVM yang sama (pikirkan beberapa aplikasi web yang tidak boleh berinteraksi). Tomcat perlu melompati beberapa rintangan untuk membuatnya bekerja (mengimplementasikan kembali JUL secara efektif jika saya memahaminya dengan benar).
Anda tidak selalu dapat memengaruhi kerangka kerja pencatatan yang digunakan perpustakaan Anda. Oleh karena itu menggunakan SLF4J (yang sebenarnya hanya lapisan API yang sangat tipis di atas pustaka lainnya) membantu menjaga gambaran yang agak konsisten dari seluruh dunia logging (sehingga Anda dapat memutuskan kerangka kerja logging yang mendasarinya sementara masih memiliki library logging di sistem yang sama).
Perpustakaan tidak dapat dengan mudah diubah. Jika versi sebelumnya dari perpustakaan yang digunakan untuk menggunakan logging-library-X, ia tidak dapat dengan mudah beralih ke logging-library-Y (misalnya JUL), bahkan jika yang terakhir jelas superious: setiap pengguna perpustakaan itu perlu belajar kerangka kerja logging baru dan (setidaknya) mengkonfigurasi ulang logging mereka. Itu sangat tidak boleh, terutama ketika itu tidak membawa keuntungan bagi kebanyakan orang.
Setelah mengatakan semua yang saya pikir JUL setidaknya merupakan alternatif yang valid untuk kerangka kerja logging lainnya hari ini.
sumber
IMHO, keuntungan utama dalam menggunakan fasad logging seperti slf4j adalah Anda membiarkan pengguna akhir dari perpustakaan memilih implementasi logging logging yang ia inginkan, daripada memaksakan pilihan Anda kepada pengguna akhir.
Mungkin dia telah menginvestasikan waktu dan uang di Log4j atau LogBack (formatters khusus, appenders, dll.) Dan lebih suka terus menggunakan Log4j atau LogBack, daripada mengkonfigurasi jul. Tidak masalah: slf4j memungkinkan itu. Apakah ini pilihan bijak untuk menggunakan Log4j daripada jul? Mungkin tidak. Tapi kamu tidak peduli. Biarkan pengguna akhir memilih apa yang diinginkannya.
sumber
Saya mulai, seperti Anda saya kira, menggunakan JULI karena itu yang paling mudah untuk segera dilakukan. Namun, selama bertahun-tahun, saya berharap saya telah menghabiskan lebih banyak waktu untuk memilih.
Masalah utama saya sekarang adalah bahwa kita memiliki sejumlah besar kode 'perpustakaan' yang digunakan di banyak aplikasi dan mereka semua menggunakan JUL. Setiap kali saya menggunakan alat ini dalam aplikasi jenis layanan web, pencatatan hanya menghilang atau pergi ke suatu tempat yang tidak terduga atau aneh.
Solusi kami adalah menambahkan fasad ke kode perpustakaan yang berarti bahwa panggilan log perpustakaan tidak berubah tetapi dialihkan secara dinamis ke mekanisme log apa pun yang tersedia. Ketika dimasukkan dalam alat POJO mereka diarahkan ke JUL tetapi ketika digunakan sebagai aplikasi web mereka diarahkan ke LogBack.
Penyesalan kami - tentu saja - adalah bahwa kode pustaka tidak menggunakan pencatatan parameter tetapi sekarang ini dapat dipasang kembali saat dan ketika dibutuhkan.
Kami menggunakan slf4j untuk membangun fasad.
sumber
Saya menjalankan jul terhadap slf4j-1.7.21 melalui logback-1.1.7, output ke SSD, Java 1.8, Win64
Juli berlari 48449 ms, logback 27185 ms untuk loop 1M.
Namun, sedikit lebih cepat dan API yang sedikit lebih baik tidak bernilai 3 perpustakaan dan 800 ribu untuk saya.
dan
sumber
logger.info()
. Jadi Anda sengaja melumpuhkan kinerja jul untuk mengkompensasi kekurangan pada antarmuka slf4j. Anda sebaiknya mengkode kedua metode dengan cara mereka dikodekan secara idiomatis.