Apakah Logger.getLogger (MyClass.class) cara terbaik untuk menginisialisasi log4j logger?

13

Tutorial Mkyong ini menyarankan untuk menginternalkan penebang dengan cara ini:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Sekarang mungkin setiap kelas lain yang Anda gunakan, yang memiliki logger akan menginisialisasi logger mereka dengan cara yang sama.

Pertanyaan saya adalah - apakah ini cara terbaik untuk melakukannya? Sepertinya ... berulang.

djohnston
sumber
2
Apa yang Anda temukan verbose tentang ini (mengesampingkan sintaksis Java) ?. Anda harus membuat variabel untuk menahan logger, dan Anda harus memberi tahu getLogger()nama logger untuk mendapatkannya.
kdgregory
2
@kdgregory fakta bahwa saya melakukan hal yang sama di setiap kelas.
dwjohnston
1
Lihatlah pertanyaan
toniedzwiedz
3
Terlalu tua untuk dimigrasi, tetapi pertanyaan ini di luar topik di sini dan lebih cocok untuk StackOverflow.
Andres F.
1
@AndresF. Saya pikir saya taruh di sini karena ini lebih merupakan pertanyaan tentang gaya kode / pola desain, daripada masalah teknis.
dwjohnston

Jawaban:

16

Komentar Anda mengatakan bahwa "verbose" mengacu pada kebutuhan untuk mengulangi baris kode ini di setiap kelas. Tanggapan pertama saya adalah bahwa, dalam gambar besar, menambahkan dua baris kode (definisi variabel ditambah pernyataan impor) untuk setiap kelas bukan masalah besar. Terutama karena Anda hanya perlu menambahkannya ke kelas yang memiliki perilaku dan karenanya perlu melakukan logging. Yang mengatakan, baris kode tertentu yang Anda gunakan cenderung untuk kesalahan copy-paste (lebih lanjut tentang itu nanti).

Tetapi, karena Anda menginginkan alternatif, berikut adalah beberapa, dengan alasan Anda mungkin atau mungkin tidak ingin menggunakannya.

Gunakan logger tunggal untuk seluruh aplikasi

Jika Anda tidak peduli tentang apa yang dilaporkan oleh kelas, atau bersedia untuk memasukkan semua konteks yang diperlukan ke dalam pesan, maka pekerjaan logger singleton sederhana akan berhasil:

LoggerSingleton.getInstance().debug("MyController is running")

Menurut pendapat saya, salah satu manfaat besar dari kerangka logging adalah memiliki konteks yang disediakan oleh instance logger terpisah - jika hanya untuk mengarahkan pesan log ke tujuan yang berbeda. Saya tidak akan menyerah hanya untuk menyimpan satu baris kode (Anda masih perlu impor).

Plus, ini meningkatkan verbositas pada titik penggunaan, yang akan berakhir dengan penekanan tombol yang jauh lebih banyak.

Buat penebang Anda pada titik penggunaan

Saya membuang yang satu ini hanya karena menghilangkan variabel. Saya rasa saya tidak perlu berkomentar tentang itu. Meskipun tidak menunjukkan teknik pilihan saya untuk mendapatkan contoh logger.

Logger.getLogger(getClass()).debug("blah blah blah");

Gunakan post-prosesor kacang untuk menyuntikkan logger

Contoh Anda menggunakan Spring, dan Spring memungkinkan Anda menghubungkan ke kode inisialisasi kacang. Anda bisa membuat post-prosesor yang memeriksa kacang untuk loggervariabel anggota, dan membuat Loggercontoh ketika menemukannya.

Walaupun post-processor seperti itu hanya beberapa lusin baris kode, ini merupakan bagian yang bergerak dalam aplikasi Anda, dan karenanya berpotensi menjadi sumber bug. Saya lebih suka memiliki beberapa dari mereka mungkin.

Gunakan mixin

Scala dan Groovy memberikan ciri-ciri , yang memungkinkan Anda merangkum perilaku. Pola Scala yang khas adalah membuat Loggingsifat, lalu menambahkannya ke kelas yang perlu dicatat:

class MyController with Logging

Sayangnya, ini berarti Anda harus beralih bahasa. Kecuali Anda menggunakan Java 8, dalam hal ini Anda dapat membuat Loggingantarmuka dengan "metode default":

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

Sekarang, dalam kode kelas Anda, Anda dapat menggunakannya

getLogger().debug("blah blah blah");

Meskipun mudah, ini memiliki beberapa kelemahan. Untuk satu hal, itu mencemari antarmuka setiap kelas yang menggunakannya, karena semua metode antarmuka bersifat publik. Mungkin tidak terlalu buruk jika Anda menggunakannya hanya untuk kelas yang dipakai dan disuntikkan oleh Spring, terutama jika Anda mengikuti pemisahan antarmuka / implementasi.

Masalah yang lebih besar adalah bahwa ia harus mencari contoh logger yang sebenarnya pada setiap panggilan. Yang cepat, tetapi tidak perlu.

Dan Anda masih membutuhkan pernyataan impor.

Pindahkan logger ke superclass

Saya akan ulangi: Saya tidak menemukan definisi logger berulang-ulang, tetapi jika Anda melakukannya, saya pikir ini pendekatan terbaik untuk menghilangkannya.

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Sekarang kelas pengontrol Anda mewarisi dari AbstractController, dan mereka memiliki akses ke loggervariabel. Ingatlah bahwa Anda harus meletakkan @Controlleranotasi di kelas yang konkret.

Beberapa orang akan menemukan ini penyimpangan warisan. Saya telah mencoba meredakan mereka dengan memberi nama kelas AbstractControllerdaripada AbstractProjectClass. Anda dapat memutuskan sendiri apakah ada hubungan is-a .

Orang lain akan keberatan dengan penggunaan variabel instan daripada variabel statis. Logger statis IMO rentan terhadap kesalahan salin-tempel, karena Anda harus secara eksplisit merujuk nama kelas; getClass()memastikan bahwa logger Anda selalu benar.

kdgregory
sumber
Saya pikir Anda harus berhati-hati tentang getClass () di kelas pewarisan, MethodHandles.lookup (). LookupClass () lebih baik setelah jdk 7
yuxh
@luxh - apakah Anda memiliki penjelasan untuk itu?
kdgregory
periksa di sini: stackoverflow.com/a/6653577/4652536
yuxh
@yuxh - satu-satunya penyebutan MethodHandles dalam pertanyaan itu (yang tidak ada dalam jawaban yang Anda tautkan), adalah pernyataan yang tidak didukung serupa dengan komentar yang meminta klarifikasi. Apakah Anda memiliki dukungan otoritatif untuk pernyataan yang MethodHandles.lookup().lookupClass()lebih baik daripada Object.getClass()?
kdgregory
MethodHandles.lookup().lookupClass()dapat digunakan untuk variabel statis, tidak rentan terhadap kesalahan salin-tempel dan cepat: stackoverflow.com/a/47112323/898747 Itu berarti inport tambahan, tetapi saya cukup suka logger saya menjadi statis sehingga setidaknya layak sebutkan :)
FableBlaze
0

Untuk memperluas jawaban yang diberikan oleh @kdgregory, Groovy menyediakan @Slf4j( groovy.util.logging.Slf4j) di luar kotak sebagai anotasi yang melakukan transformasi AST pada kelas untuk menangani dengan logger yang mengasumsikan nama variabel logsecara default jika dibiarkan tidak ditentukan.

Daniel Paul Anzaldo
sumber