Tại sao thông báo ghi nhật ký Level.FINE không hiển thị?


110

Các JavaDocs chojava.util.logging.Level nhà nước:


Các mức theo thứ tự giảm dần là:

  • SEVERE (giá trị cao nhất)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (giá trị thấp nhất)

Nguồn

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));
        }
    }
}

Đầu ra

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 . . .

Báo cáo vấn đề

Ví dụ của tôi đặt Levelthành FINER, vì vậy tôi đã mong đợi thấy 2 thông báo cho mỗi vòng lặp. Thay vào đó, tôi thấy một thông báo duy nhất cho mỗi vòng lặp (các Level.FINEthông báo bị thiếu).

Câu hỏi

Điều gì cần thay đổi để xem FINE( FINERhoặc FINEST) đầu ra?

Cập nhật (giải pháp)

Cảm ơn câu trả lời của Vineet Reynolds , phiên bản này hoạt động theo mong đợi của tôi. Nó hiển thị 3 x INFOtin nhắn và 3 x FINEtin nhắn.

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));
        }
    }
}

10
Có vẻ như với tôi rằng bạn sẽ có các thông báo được in hai lần trên bảng điều khiển cho INFO trở lên: đầu tiên bởi trình ghi ẩn danh, sau đó là trình ghi nhật ký chung của nó, trình ghi toàn cầu cũng có ConsoleHandler được đặt thành INFO theo mặc định. Để tắt trình ghi nhật ký chung, bạn cần thêm dòng mã này: logger.setUseParentHandlers (false);
phút

Tôi chỉ muốn xác nhận vài phút bình luận về trận đấu đôi. bạn sẽ nhận được hai đầu ra trừ khi bạn sử dụng .setUseParentHandlers (false);
xpagesbeast

Câu trả lời:


124

Người ghi nhật ký chỉ ghi nhật ký thông báo, tức là họ tạo các bản ghi nhật ký (hoặc yêu cầu ghi nhật ký). Họ không xuất bản tin nhắn đến các điểm đến, do Người xử lý đảm nhận. Đặt cấp độ của trình ghi nhật ký, chỉ khiến nó tạo ra các bản ghi nhật ký phù hợp với cấp độ đó hoặc cao hơn.

Bạn có thể đang sử dụng ConsoleHandler(tôi không thể suy ra đầu ra của bạn là System.err hay một tệp, nhưng tôi sẽ giả định rằng đó là trước đây), mặc định xuất bản các bản ghi nhật ký của cấp độ Level.INFO. Bạn sẽ phải cấu hình trình xử lý này, để xuất bản các bản ghi nhật ký ở cấp độ Level.FINERvà cao hơn, cho kết quả mong muốn.

Tôi khuyên bạn nên đọc hướng dẫn Tổng quan về ghi nhật ký Java để hiểu thiết kế cơ bản. Hướng dẫn bao gồm sự khác biệt giữa khái niệm Người ghi nhật ký và Người xử lý.

Chỉnh sửa cấp trình xử lý

1. Sử dụng tệp cấu hình

Tệp thuộc tính java.util.logging (theo mặc định, đây là logging.propertiestệp trong JRE_HOME/lib) có thể được sửa đổi để thay đổi mức mặc định của ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Tạo trình xử lý trong thời gian chạy

Điều này không được khuyến nghị, vì nó sẽ dẫn đến việc ghi đè cấu hình chung. Sử dụng điều này trong toàn bộ cơ sở mã của bạn sẽ dẫn đến cấu hình trình ghi nhật ký có thể không quản lý được.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);

2
Cảm ơn. Đó là một mẹo nhỏ. Bây giờ tôi nhận ra tại sao tôi ban đầu rất bối rối. Trước đây tôi đã làm việc với các cấp độ ghi nhật ký, nhưng việc triển khai của tôi tại thời điểm đó chỉ đơn giản là bỏ mọi thông báo đã ghi nhật ký vào một danh sách được hiển thị mà không liên quan đến Handler.
Andrew Thompson

3
Không có gì. Và yeah, việc thiết kế không có được cho bạn, nếu ai đã được viết logger mà chỉ đơn giản đổ dây vào một tập tin, an ủi, vv
Vineet Reynolds

7
Và việc thay đổi logging.properties trong thư viện JRE toàn cầu có thể quản lý được không?
James Anderson

2
Xin lưu ý rằng bản thân cấp của trình ghi nhật ký phải ít hạn chế hơn cấp của trình xử lý. Trước khi ghi nhật ký, Java kiểm tra mức tối đa giữa trình ghi và trình xử lý. Vì vậy, nếu bạn chỉ đặt handler.setLevel (Level.FINER); bạn sẽ không thấy gì vì mức ghi nhật ký mặc định là Level.INFO. Bạn phải đặt nó thành FINER, FINEST hoặc ALL. Nói logger.setLevel (Level.ALL);
Jeff_Alieffson

Tôi đoán đây sẽ giải quyết vấn đề như một lớp lót, ngay cả khi bạn sử dụng nặc danh và tên người khai thác gỗ trong một môi trường mặc định:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Đánh dấu Jeronimus

27

Tại sao

java.util.logging có một trình ghi gốc mặc định Level.INFOvà một ConsoleHandler gắn liền với nó cũng mặc định Level.INFO. FINEthấp hơn INFO, vì vậy các thông báo tốt không được hiển thị theo mặc định.


Giải pháp 1

Tạo một trình ghi nhật ký cho toàn bộ ứng dụng của bạn, ví dụ từ tên gói hoặc mục đích sử dụng Logger.getGlobal()của bạn, và kết nối ConsoleLogger của riêng bạn với nó. Sau đó, yêu cầu trình ghi gốc đóng cửa (để tránh trùng lặp đầu ra của các thông báo cấp cao hơn) hoặc yêu cầu trình ghi của bạn không chuyển tiếp nhật ký đến thư mục gốc.

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

Giải pháp 2

Ngoài ra, bạn có thể hạ thấp thanh của trình ghi gốc.

Bạn có thể đặt chúng bằng mã:

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

Hoặc với tệp cấu hình ghi nhật ký, nếu bạn đang sử dụng nó :

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

Bằng cách hạ thấp cấp độ toàn cầu, bạn có thể bắt đầu thấy thông báo từ các thư viện lõi, chẳng hạn như từ một số thành phần Swing hoặc JavaFX. Trong trường hợp này, bạn có thể đặt Bộ lọc trên trình ghi gốc để lọc ra các thư không phải từ chương trình của bạn.


applog.setUseParentHandlers (false) những mã này giúp tôi, cảm ơn rất nhiều.
aolphn

4

tại sao ghi nhật ký java của tôi không hoạt động

cung cấp một tệp jar sẽ giúp bạn tìm ra lý do tại sao việc đăng nhập của bạn không hoạt động như mong đợi. Nó cung cấp cho bạn một kết xuất hoàn chỉnh về những trình ghi và trình xử lý nào đã được cài đặt và những cấp nào được đặt và cấp nào trong hệ thống phân cấp ghi nhật ký.


Đây là một bình luận, không phải là một câu trả lời.
hfontanez

4

TẠI SAO

Như đã đề cập bởi @Sheepy, lý do tại sao nó không hoạt động là java.util.logging.Loggercó trình ghi gốc mặc định Level.INFOvà tệp ConsoleHandlerđính kèm với trình ghi gốc đó cũng mặc định Level.INFO. Vì vậy, để thấy được FINE(, FINERhoặc FINEST) đầu ra, bạn cần phải thiết lập giá trị mặc định của logger gốc và nó ConsoleHandlerđến Level.FINEnhư sau:

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


Sự cố cập nhật của bạn (giải pháp)

Như đã đề cập bởi @mins, bạn sẽ có các thông báo được in hai lần trên bảng điều khiển cho INFOvà ở trên: đầu tiên bởi trình ghi ẩn danh, sau đó là trình cha mẹ của nó, trình ghi gốc cũng có ConsoleHandlerthiết lập INFOtheo mặc định. Để vô hiệu hóa trình ghi gốc, bạn cần thêm dòng mã sau:logger.setUseParentHandlers(false);

Có những cách khác để ngăn nhật ký được xử lý bởi trình xử lý Console mặc định của trình ghi gốc được đề cập bởi @Sheepy, ví dụ:

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

Nhưng Logger.getLogger("").setLevel( Level.OFF );sẽ không hoạt động vì nó chỉ chặn thông báo được truyền trực tiếp đến trình ghi gốc, chứ không phải thông báo đến từ trình ghi con. Để minh họa cách thức Logger Hierarchyhoạt động, tôi vẽ sơ đồ sau:

nhập mô tả hình ảnh ở đây

public void setLevel(Level newLevel)thiết lập mức nhật ký xác định mức thông báo nào sẽ được ghi bởi trình ghi này. Các cấp thông báo thấp hơn giá trị này sẽ bị loại bỏ. Giá trị mức Level.OFF có thể được sử dụng để tắt ghi nhật ký. Nếu cấp mới là null, có nghĩa là nút này sẽ kế thừa cấp của nó từ tổ tiên gần nhất của nó với một giá trị cấp cụ thể (không phải null).


0

Tôi đã tìm thấy vấn đề thực sự của mình và nó không được đề cập trong bất kỳ câu trả lời nào: một số bài kiểm tra đơn vị của tôi đã khiến mã khởi tạo ghi nhật ký được chạy nhiều lần trong cùng một bộ kiểm tra, gây rối cho việc ghi nhật ký trong các bài kiểm tra sau.


0

Đã thử các biến thể khác, điều này có thể phù hợp

    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");

0

Giải pháp này có vẻ tốt hơn đối với tôi, liên quan đến khả năng bảo trì và thiết kế để thay đổi:

  1. Tạo tệp thuộc tính ghi nhật ký nhúng nó vào thư mục dự án tài nguyên, được đưa vào tệp jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Tải tệp thuộc tính từ mã:

    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) {
          // ...
       }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.