Log4j memberikan kendali atas logging

Hampir setiap aplikasi besar menyertakan API logging atau pelacakannya sendiri. Pengalaman menunjukkan bahwa penebangan merupakan komponen penting dari siklus pengembangan. Dengan demikian, logging menawarkan beberapa keuntungan. Pertama, dapat memberikan konteks yang tepat tentang jalannya aplikasi. Setelah dimasukkan ke dalam kode, pembuatan keluaran logging tidak memerlukan campur tangan manusia. Kedua, keluaran log dapat disimpan dalam media persisten untuk dipelajari di lain waktu. Terakhir, selain penggunaannya dalam siklus pengembangan, paket logging yang cukup kaya juga dapat digunakan sebagai alat audit.

Sesuai dengan aturan tersebut, pada awal tahun 1996 proyek EU SEMPER (Secure Electronic Marketplace for Europe) memutuskan untuk membuat API pelacakannya sendiri. Setelah peningkatan yang tak terhitung jumlahnya, beberapa inkarnasi, dan banyak pekerjaan, API tersebut telah berevolusi menjadi log4j, paket logging populer untuk Java. Paket didistribusikan di bawah Lisensi Publik IBM, yang disertifikasi oleh inisiatif sumber terbuka.

Logging memang memiliki kekurangan. Itu bisa memperlambat aplikasi. Jika terlalu bertele-tele, dapat menyebabkan scrolling blindness. Untuk mengatasi masalah tersebut, log4j dirancang untuk menjadi cepat dan fleksibel. Karena logging jarang menjadi fokus utama aplikasi, API log4j berusaha agar mudah dipahami dan digunakan.

Artikel ini dimulai dengan menjelaskan komponen utama arsitektur log4j. Ini dilanjutkan dengan contoh sederhana yang menggambarkan penggunaan dan konfigurasi dasar. Ini diakhiri dengan menyentuh masalah kinerja dan API logging yang akan datang dari Sun.

Kategori, pelengkap, dan tata letak

Log4j memiliki tiga komponen utama:

  • Kategori
  • Penambah
  • Tata Letak

Ketiga komponen tersebut bekerja sama untuk memungkinkan pengembang mencatat pesan sesuai dengan jenis dan prioritas pesan, dan untuk mengontrol pada waktu proses bagaimana pesan ini diformat dan di mana pesan tersebut dilaporkan. Mari kita lihat satu per satu.

Hierarki kategori

Keuntungan pertama dan terpenting dari setiap API logging dibandingkan dengan plain System.out.printlnterletak pada kemampuannya untuk menonaktifkan pernyataan log tertentu sambil memungkinkan orang lain untuk mencetak tanpa hambatan. Kemampuan tersebut mengasumsikan bahwa ruang logging, yaitu ruang dari semua pernyataan logging yang mungkin, dikategorikan menurut beberapa kriteria yang dipilih pengembang.

Sesuai dengan observasi tersebut, org.log4j.Categorykelas merupakan sosok inti dari paket tersebut. Kategori diberi nama entitas. Dalam skema penamaan yang akrab bagi pengembang Java, sebuah kategori dikatakan sebagai induk dari kategori lain jika namanya, diikuti dengan titik, adalah awalan dari nama kategori anak. Misalnya, kategori bernama com.fooadalah induk dari kategori bernama com.foo.Bar. Demikian pula, javaadalah orang tua java.utildan leluhur java.util.Vector.

Kategori root, yang berada di bagian atas hierarki kategori, luar biasa dalam dua hal:

  1. Itu selalu ada
  2. Itu tidak dapat diambil dengan nama

Di Categorykelas, memanggil getRoot()metode statis akan mengambil kategori root. getInstance()Metode statis membuat instance semua kategori lainnya. getInstance()mengambil nama kategori yang diinginkan sebagai parameter. Beberapa metode dasar di Categorykelas tercantum di bawah ini:

paket org.log4j; publik Kategori kelas {// Penciptaan & metode pengambilan: public static Kategori getRoot (); public static Kategori getInstance (nama String); // metode pencetakan: public void debug (String message); info kekosongan publik (pesan String); public void warn (pesan String); kesalahan public void (pesan String); // metode pencetakan umum: public void log (Prioritas p, pesan String); }

Kategori dapat diberikan prioritas dari himpunan yang ditentukan oleh org.log4j.Prioritykelas. Meskipun set prioritas cocok dengan sistem Syslog Unix, log4j mendorong penggunaan hanya empat prioritas: ERROR, WARN, INFO dan DEBUG, terdaftar dalam urutan prioritas yang menurun. Alasan di balik kumpulan yang tampaknya terbatas itu adalah untuk mempromosikan hierarki kategori yang lebih fleksibel daripada kumpulan prioritas statis (meskipun besar). Namun, Anda dapat menentukan prioritas Anda sendiri dengan membuat subclass Prioritykelas. Jika kategori tertentu tidak memiliki prioritas yang ditetapkan, itu mewarisi salah satu dari leluhur terdekatnya dengan prioritas yang ditetapkan. Dengan demikian, untuk memastikan bahwa semua kategori pada akhirnya dapat mewarisi prioritas, kategori root selalu memiliki prioritas yang ditetapkan.

Untuk membuat permintaan logging, panggil salah satu metode pencetakan dari sebuah instance kategori. Metode pencetakan tersebut adalah:

  • error()
  • warn()
  • info()
  • debug()
  • log()

Menurut definisi, metode pencetakan menentukan prioritas permintaan logging. Misalnya, jika cmerupakan instance kategori, maka pernyataannya c.info("..")adalah permintaan logging dari INFO prioritas.

Permintaan logging dikatakan diaktifkan jika prioritasnya lebih tinggi dari atau sama dengan prioritas kategorinya. Jika tidak, permintaan tersebut dikatakan dinonaktifkan. . Kategori tanpa prioritas yang ditetapkan akan mewarisi salah satu dari hierarki.

Di bawah, Anda akan menemukan contoh aturan itu:

// dapatkan turunan kategori bernama "com.foo" Category cat = Category.getInstance ( "com.foo" ); // Sekarang atur prioritasnya. cat .setPriority ( Priority.INFO ); Kategori barcat = Category.getInstance ( "com.foo.Bar" ); // Permintaan ini diaktifkan, karena WARN > = INFO . kucing. memperingatkan ("Tingkat bahan bakar rendah."); // Permintaan ini dinonaktifkan, karena DEBUG < INFO . kucing. debug ("Memulai pencarian pom bensin terdekat."); // Barcat instance kategori, bernama "com.foo.Bar", // akan mewarisi prioritasnya dari kategori bernama // "com.foo" Jadi,permintaan berikut diaktifkan // karena INFO> = INFO . barcat. info ("Terletak pom bensin terdekat."); // Permintaan ini dinonaktifkan, karena DEBUG < INFO . barcat. debug ("Keluar dari pencarian pom bensin");

Memanggil getInstance()metode dengan nama yang sama akan selalu mengembalikan referensi ke objek kategori yang sama persis. Jadi, dimungkinkan untuk mengonfigurasi kategori dan kemudian mengambil contoh yang sama di tempat lain dalam kode tanpa melewatkan referensi. Kategori dapat dibuat dan dikonfigurasi dalam urutan apa pun. Secara khusus, kategori induk akan menemukan dan menautkan ke turunannya meskipun dibuatkan setelah mereka. Lingkungan log4j biasanya mengkonfigurasi saat inisialisasi aplikasi, sebaiknya dengan membaca file konfigurasi, pendekatan yang akan segera kita diskusikan.

Log4j memudahkan pemberian nama kategori berdasarkan komponen perangkat lunak. Itu dapat dicapai dengan membuat instance kategori di setiap kelas secara statis, dengan nama kategori yang sama dengan nama kelas yang sepenuhnya memenuhi syarat - metode yang berguna dan langsung untuk menentukan kategori. Karena keluaran log menyandang nama kategori yang dihasilkan, strategi penamaan seperti itu memfasilitasi identifikasi asal pesan log. Namun, itu hanya satu yang mungkin, meskipun umum, strategi untuk penamaan kategori. Log4j tidak membatasi kemungkinan kumpulan kategori. Memang, pengembang bebas memberi nama kategori sesuai keinginan.

Penambah dan tata letak

Kemampuan untuk mengaktifkan atau menonaktifkan permintaan logging secara selektif berdasarkan kategorinya hanyalah sebagian dari gambaran. Log4j juga memungkinkan permintaan logging untuk mencetak ke beberapa tujuan keluaran yang disebut appenders di log4j speak. Saat ini, appenders ada untuk konsol, file, komponen GUI, server soket jarak jauh, NT Event Loggers, dan daemon UNIX Syslog jarak jauh.

Kategori bisa merujuk ke beberapa pelengkap. Setiap permintaan logging yang diaktifkan untuk kategori tertentu akan diteruskan ke semua appender dalam kategori itu serta appender yang lebih tinggi dalam hierarki. Dengan kata lain, appender diwarisi secara aditif dari hierarki kategori. Misalnya, jika Anda menambahkan appender konsol ke kategori root, semua permintaan logging yang diaktifkan setidaknya akan dicetak di konsol. Jika, sebagai tambahan, penambah file ditambahkan ke kategori, katakanlah C , maka permintaan logging yang diaktifkan untuk anak-anak C dan C akan mencetak pada file dan di konsol. Ketahuilah bahwa Anda bisa mengganti perilaku default tersebut sehingga akumulasi appender tidak lagi bersifat aditif.

Lebih sering daripada tidak, pengguna ingin menyesuaikan tidak hanya tujuan keluaran tetapi juga format keluaran, suatu prestasi yang dicapai dengan mengaitkan tata letak dengan appender. Tata letak memformat permintaan logging sesuai dengan keinginan pengguna, sedangkan appender menangani pengiriman output yang diformat ke tujuannya. The PatternLayout, bagian dari distribusi log4j standar, memungkinkan pengguna menentukan format keluaran sesuai dengan pola konversi yang mirip dengan printffungsi bahasa C.

Misalnya, PatternLayoutdengan pola konversi %r [%t]%-5p %c - %m%nakan menghasilkan sesuatu yang mirip dengan:

176 [utama] INFO org.foo.Bar - Lokasi SPBU terdekat. 

Pada keluaran di atas:

  • Bidang pertama sama dengan jumlah milidetik yang telah berlalu sejak program dimulai
  • The second field indicates the thread making the log request
  • The third field represents the priority of the log statement
  • The fourth field equals the name of the category associated with the log request

The text after the - indicates the statement's message.

Configuration

Inserting log requests into the application code requires a fair amount of planning and effort. Observation shows that code dedicated to logging represents approximately four percent of the application's total. Consequently, even moderately sized applications will have thousands of logging statements embedded within their code. Given their number, it becomes imperative to manage those log statements without the need to modify them manually.

The log4j environment can be fully configured programmatically. However, it is far more flexible to configure log4j by using configuration files. Currently, configuration files can be written in XML or in Java properties (key=value) format.

Let us give a taste of how that is done with the help of an imaginary application -- MyApp -- that uses log4j:

 import com.foo.Bar; // Import log4j classes. import org.log4j.Category; import org.log4j.BasicConfigurator; public class MyApp { // Define a static category variable so that it references the // Category instance named "MyApp". static Category cat = Category.getInstance(MyApp.class.getName()); public static void main(String[] args) { // Set up a simple configuration that logs on the console. BasicConfigurator.configure(); cat.info("Entering application."); Bar bar = new Bar(); bar.doIt(); cat.info("Exiting application."); } } 

As seen in the code above, MyApp begins by importing log4j related classes. It then defines a static category variable with the name MyApp, which happens to be the class' fully qualified name.

MyApp uses the Bar class defined in the package com.foo:

package com.foo; import org.log4j.Category; public class Bar { static Category cat = Category.getInstance(Bar.class.getName()); public void doIt() { cat.debug("Did it again!"); } } 

In MyApp, the invocation of the BasicConfigurator.configure() method creates a rather simple log4j setup. That method is hardwired to add to the root category a FileAppender printing on the console. The output will be formatted by using a PatternLayout set to the pattern %-4r [%t] %-5p %c %x - %m%n.

Note that by default, the root category is assigned to Priority.DEBUG.

The output of MyApp is:

0 [main] INFO MyApp - Entering application. 36 [main] DEBUG com.foo.Bar - Did it again! 51 [main] INFO MyApp - Exiting application. 

Figure 1 depicts MyApp's object diagram immediately after it calls the BasicConfigurator.configure() method.

The MyApp class configures log4j by invoking BasicConfigurator.configure() method. Other classes need only import the org.log4j.Category class, retrieve the categories they want to use and log away.

The previous example always outputs the same log information. Fortunately, it is easy to modify MyApp so that the log output can be controlled at runtime. Below, you'll see a slightly modified version:

 import com.foo.Bar; import org.log4j.Category; import org.log4j.PropertyConfigurator; public class MyApp { static Category cat = Category.getInstance(MyApp.class.getName()); public static void main(String[] args) { // BasicConfigurator replaced with PropertyConfigurator. PropertyConfigurator.configure(args[0]); cat.info("Entering application."); Bar bar = new Bar(); bar.doIt(); cat.info("Exiting application."); } } 

This version of MyApp instructs PropertyConfigurator to parse a configuration file and set up logging accordingly.

Let's look at a sample configuration file that results in exactly the same output as the previous BasicConfigurator-based example:

# Set root category priority to DEBUG and its only appender to A1. log4j.rootCategory=DEBUG, A1 # A1 is set to be a FileAppender which outputs to System.out. log4j.appender.A1=org.log4j.FileAppender log4j.appender.A1.File=System.out # A1 uses PatternLayout. log4j.appender.A1.layout=org.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Suppose we no longer wish to see the output of any component belonging to the com.foo package. The following configuration file shows one possible way of achieving that:

log4j.rootCategory = DEBUG, A1 log4j.appender.A1 = org.log4j.FileAppender log4j.appender.A1.File = System.out log4j.appender.A1.layout = org.log4j.PatternLayout # Cetak tanggal dalam format ISO 8601 log4j.appender.A1.layout.ConversionPattern = % d [% t]% -5p% c -% m% n # Cetak hanya pesan prioritas WARN atau lebih tinggi dalam paket com.foo. log4j.category.com.foo = PERINGATAN