Mengapa pesan pencatatan Level.FINE tidak muncul?

110

The JavaDocs untukjava.util.logging.Level negara:


Level-level dalam urutan menurun adalah:

  • SEVERE (nilai tertinggi)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (nilai terendah)

Sumber

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(Level.FINER);
        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}

Keluaran

Logging level is: FINER
Jun 11, 2011 9:39:23 PM LoggingLevelsBlunder main
INFO: 0 0
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 1 1
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 2 4
Press any key to continue . . .

Pernyataan masalah

Contoh saya menyetel Levelke FINER, jadi saya mengharapkan untuk melihat 2 pesan untuk setiap loop. Sebaliknya saya melihat satu pesan untuk setiap loop ( Level.FINEpesan hilang).

Pertanyaan

Apa yang perlu diubah untuk melihat keluaran FINE(, FINERatau FINEST)?

Perbarui (solusi)

Berkat jawaban Vineet Reynolds , versi ini bekerja sesuai ekspektasi saya. Ini menampilkan 3 x INFOpesan, & 3 x FINEpesan.

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        // LOG this level to the log
        logger.setLevel(Level.FINER);

        ConsoleHandler handler = new ConsoleHandler();
        // PUBLISH this level
        handler.setLevel(Level.FINER);
        logger.addHandler(handler);

        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}
Andrew Thompson
sumber
10
Tampak bagi saya bahwa Anda akan memiliki pesan yang dicetak dua kali pada konsol untuk INFO dan di atasnya: pertama oleh pencatat anonim, kemudian oleh induknya, pencatat global yang juga memiliki ConsoleHandler yang disetel ke INFO secara default. Untuk menonaktifkan logger global, Anda perlu menambahkan baris kode ini: logger.setUseParentHandlers (false);
menit
Saya hanya ingin mengkonfirmasi komentar menit tentang ganda. Anda akan mendapatkan dua keluaran kecuali Anda menggunakan .setUseParentHandlers (false);
xpagesbeast

Jawaban:

124

Penebang hanya mencatat pesan, yaitu mereka membuat catatan log (atau permintaan logging). Mereka tidak mempublikasikan pesan ke tujuan, yang diurus oleh Penangan. Menyetel level logger, hanya menyebabkannya membuat catatan log yang cocok dengan level itu atau lebih tinggi.

Anda mungkin menggunakan ConsoleHandler(saya tidak bisa menyimpulkan di mana output Anda adalah System.err atau file, tetapi saya akan berasumsi bahwa itu adalah yang pertama), yang secara default menerbitkan catatan log dari level tersebut Level.INFO. Anda harus mengkonfigurasi penangan ini, untuk menerbitkan catatan log tingkat Level.FINERdan lebih tinggi, untuk hasil yang diinginkan.

Saya akan merekomendasikan membaca panduan Ikhtisar Logging Java , untuk memahami desain yang mendasarinya. Panduan ini mencakup perbedaan antara konsep Logger dan Handler.

Mengedit level penangan

1. Menggunakan file Konfigurasi

File properti java.util.logging (secara default, ini adalah logging.propertiesfile di JRE_HOME/lib) dapat dimodifikasi untuk mengubah tingkat default ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Membuat penangan saat runtime

Ini tidak disarankan, karena akan mengakibatkan penggantian konfigurasi global. Menggunakan ini di seluruh basis kode Anda akan menghasilkan konfigurasi logger yang mungkin tidak dapat dikelola.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
sumber
2
Terima kasih. Itu berhasil. Saya sekarang menyadari mengapa saya awalnya sangat bingung. Saya sebelumnya bekerja dengan level logging, tetapi implementasi saya pada saat itu hanya menjatuhkan setiap pesan yang dicatat ke dalam daftar yang ditampilkan tanpa memperhatikan Handler.
Andrew Thompson
3
Sama-sama. Dan ya, desainnya sesuai untuk Anda, jika seseorang telah menulis penebang yang hanya membuang string ke file, konsol, dll.
Vineet Reynolds
7
Dan mengubah properti logging.properties di perpustakaan JRE global dapat dikelola?
James Anderson
2
Harap dicatat, bahwa level logger itu sendiri harus kurang ketat, dari level handler. Sebelum logging Java memeriksa level maksimum antara logger dan handler. Jadi jika Anda hanya menyetel handler.setLevel (Level.FINER); Anda tidak akan melihat apa-apa karena level logger default adalah Level.INFO. Anda harus mengaturnya ke FINER, FINEST atau ALL. Ucapkan logger.setLevel (Level.ALL);
Jeff_Alieffson
Saya kira ini akan menyelesaikan masalah sebagai satu baris, bahkan ketika Anda menggunakan penebang anonim dan bernama di lingkungan default:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

Mengapa

java.util.logging memiliki root logger sebagai default Level.INFO, dan ConsoleHandler yang terpasang padanya yang juga default ke Level.INFO. FINElebih rendah dari INFO, jadi pesan halus tidak ditampilkan secara default.


Solusi 1

Buat logger untuk seluruh aplikasi Anda, misalnya dari nama paket atau penggunaan Anda Logger.getGlobal(), dan kaitkan ConsoleLogger Anda sendiri ke sana. Kemudian minta logger root untuk diam (untuk menghindari output duplikat dari pesan tingkat yang lebih tinggi), atau minta logger Anda untuk tidak meneruskan log ke root.

public static final Logger applog = Logger.getGlobal();
...

// Create and set handler
Handler systemOut = new ConsoleHandler();
systemOut.setLevel( Level.ALL );
applog.addHandler( systemOut );
applog.setLevel( Level.ALL );

// Prevent logs from processed by default Console handler.
applog.setUseParentHandlers( false ); // Solution 1
Logger.getLogger("").setLevel( Level.OFF ); // Solution 2

Solusi 2

Atau, Anda dapat menurunkan bilah logger root.

Anda dapat mengaturnya dengan kode:

Logger rootLog = Logger.getLogger("");
rootLog.setLevel( Level.FINE );
rootLog.getHandlers()[0].setLevel( Level.FINE ); // Default console handler

Atau dengan file konfigurasi logging, jika Anda menggunakannya :

.level = FINE
java.util.logging.ConsoleHandler.level = FINE

Dengan menurunkan level global, Anda mungkin mulai melihat pesan dari pustaka inti, seperti dari beberapa komponen Swing atau JavaFX. Dalam hal ini Anda dapat mengatur Filter pada root logger untuk menyaring pesan yang bukan dari program Anda.

Sheepy
sumber
applog.setUseParentHandlers (false) kode ini membantu saya, terima kasih banyak.
aolphn
4

mengapa java logging saya tidak berfungsi

menyediakan file jar yang akan membantu Anda mengetahui mengapa proses masuk Anda tidak berfungsi seperti yang diharapkan. Ini memberi Anda dump lengkap dari logger dan penangan apa yang telah diinstal dan level apa yang ditetapkan dan pada level mana dalam hierarki logging.

matthew
sumber
Ini adalah komentar, bukan jawaban.
hfontanez
4

MENGAPA

Seperti yang disebutkan oleh @Sheepy, alasan mengapa ini tidak berfungsi adalah karena java.util.logging.Loggermemiliki logger root sebagai default Level.INFO, dan ConsoleHandlerlampiran ke logger root juga default Level.INFO. Oleh karena itu, untuk melihat keluaran FINE(, FINERatau FINEST), Anda perlu menyetel nilai default dari logger root dan ConsoleHandlerke Level.FINEsebagai berikut:

Logger.getLogger("").setLevel(Level.FINE);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);


Masalah Pembaruan Anda (solusi)

Seperti yang disebutkan oleh @mins, Anda akan memiliki pesan yang dicetak dua kali pada konsol untuk INFOdan yang lebih tinggi: pertama oleh pencatat anonim, kemudian oleh induknya, pencatat akar yang juga telah ConsoleHandlerdisetel ke INFOsecara default. Untuk menonaktifkan root logger, Anda perlu menambahkan baris kode ini:logger.setUseParentHandlers(false);

Ada cara lain untuk mencegah log diproses oleh pengendali Konsol default dari logger root yang disebutkan oleh @Sheepy, misalnya:

Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF );

Tetapi Logger.getLogger("").setLevel( Level.OFF );tidak akan berfungsi karena hanya memblokir pesan yang diteruskan langsung ke root logger, bukan pesan yang berasal dari logger anak. Untuk mengilustrasikan cara Logger Hierarchykerjanya, saya menggambar diagram berikut:

masukkan deskripsi gambar di sini

public void setLevel(Level newLevel)setel level log yang menentukan level pesan mana yang akan dicatat oleh logger ini. Level pesan yang lebih rendah dari nilai ini akan dibuang. Nilai level Level.OFF dapat digunakan untuk mematikan logging. Jika level baru adalah null, itu berarti node ini harus mewarisi levelnya dari leluhur terdekatnya dengan nilai level tertentu (bukan null).

Joe Yichong
sumber
0

Saya menemukan masalah saya yang sebenarnya dan tidak disebutkan dalam jawaban apa pun: beberapa pengujian unit saya menyebabkan kode inisialisasi logging dijalankan beberapa kali dalam rangkaian pengujian yang sama, mengacaukan logging pada pengujian selanjutnya.

Alex R
sumber
0

Mencoba varian lain, ini mungkin tepat

    Logger logger = Logger.getLogger(MyClass.class.getName());        
    Level level = Level.ALL;
    for(Handler h : java.util.logging.Logger.getLogger("").getHandlers())    
        h.setLevel(level);
    logger.setLevel(level);
// this must be shown
    logger.fine("fine");
    logger.info("info");
zhen_khokh
sumber
0

Solusi ini tampak lebih baik bagi saya, berkenaan dengan pemeliharaan dan desain untuk perubahan:

  1. Buat file properti logging, sematkan di folder proyek sumber daya, untuk dimasukkan ke dalam file jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
    
  2. Muat file properti dari kode:

    public static java.net.URL retrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    public synchronized void initializeLogger() {
       try (InputStream is = retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
    
pengguna2627331
sumber