2009-11-20 46 views
64

Tôi quyết định sử dụng khung ghi nhật ký Log4J cho một dự án Java mới. Tôi tự hỏi tôi nên sử dụng chiến lược nào để tạo/quản lý các bản sao của Logger và tại sao?Log4J: Các chiến lược để tạo các bản sao Logger

  • một thể hiện của Trình ghi nhật ký mỗi lớp ví dụ:

    class Foo { 
        private static final Logger log = Logger.getLogger(Foo.class); 
    } 
    
  • một ví dụ của Logger cho mỗi thread
  • một ví dụ của Logger cho mỗi ứng dụng
  • cắt ngang: một ví dụ của Logger trong mỗi lớp của một ứng dụng (ví dụ lớp xem, lớp điều khiển và bền bỉ lớp)
  • cắt dọc: một ví dụ của Logger trong phân vùng chức năng của ứng dụng

Lưu ý: vấn đề này đã được coi là một mức độ nào trong những bài viết này:

Whats the overhead of creating a Log4j Logger

+0

Lưu ý: Cách "cuối cùng" rất có thể là điều tra ngăn xếp cuộc gọi để nhận người gọi. Theo kinh nghiệm của tôi, các chương trình Java sẽ mạnh mẽ hơn nếu bạn viết mã một cách rõ ràng, thay vì dựa vào nó một cách kỳ diệu. –

+0

Lỗi trong JRE sẽ khiến việc ghi nhật ký không chính xác. –

Trả lời

41

Thông thường, bạn sẽ phải cài đặt logger mỗi lớp vì đó là một thành phần logic tốt đẹp. Các luồng đã là một phần của các thông điệp tường trình (nếu bộ lọc của bạn hiển thị chúng) vì vậy việc cắt logger theo cách đó có lẽ là thừa.

Về trình đăng nhập dựa trên ứng dụng hoặc lớp, vấn đề là bạn phải tìm một nơi để gắn đối tượng Logger đó. Không phải là một thỏa thuận thực sự lớn. Vấn đề lớn hơn là một số lớp có thể được sử dụng ở nhiều cấp độ từ nhiều ứng dụng ... rất khó để có được trình ghi nhật ký của bạn đúng. Hoặc ít nhất là khó khăn.

... và điều cuối cùng bạn muốn là giả định xấu trong thiết lập ghi nhật ký của bạn.

Nếu bạn quan tâm đến các ứng dụng và lớp và có các điểm tách dễ dàng, NDC là cách để đi. Mã đôi khi có thể hơi quá nhiều nhưng tôi không biết bao nhiêu lần tôi đã được lưu bởi một chồng ngữ cảnh chính xác cho thấy rằng Foo.bar() được gọi từ ứng dụng X trong lớp Y.

+0

Làm thế nào về nếu tôi có một lớp singelton trong đó có một trường hợp tĩnh của logger? và làm cho tất cả các lớp học truy cập thành viên này của lớp singelton? – Jack

30

Chiến lược được sử dụng nhiều nhất là tạo trình ghi nhật ký cho mỗi lớp. Nếu bạn tạo chủ đề mới, hãy cung cấp cho họ một tên hữu ích, vì vậy việc ghi nhật ký của họ có thể dễ dàng phân biệt được.

Tạo logger mỗi lớp có những lợi ích của việc có thể bật/tắt ghi trong cấu trúc gói của các lớp học của bạn:

log4j.logger.org.apache = INFO 
log4j.logger.com.example = DEBUG 
log4j.logger.com.example.verbose = ERROR 

Trên đây sẽ thiết lập tất cả các mã thư viện apache để INFO mức, chuyển đổi đăng nhập từ mã của riêng bạn ở mức DEBUG ngoại trừ gói tiết.

+1

Tôi thấy lợi ích nếu bạn muốn kiểm soát mức độ đăng nhập theo cấu trúc gói. Có cách nào tốt hơn để đạt được điều này hơn là sao chép 'private static Logger logger = Logger.getLogger (MyClass.class);' vào mỗi lớp? –

+0

Cho rằng bạn cần một số cách để cung cấp một cá thể logger cả package- và classname của bạn, tôi không thấy một sự thay thế để tạo một lớp cho mỗi lớp. Nếu bạn sẽ tạo logger cho mỗi gói, bạn vẫn cần lấy một tham chiếu tới cá thể của trình ghi nhật ký để sử dụng nó. – rsp

+0

Phương thức khởi tạo 'Logger' nào bạn sử dụng để tạo một' Logger' cho một gói? Có phải 'getLogger (String name)' trong đó name chỉ là một String xác định gói (ví dụ: "a.b.c")? Nếu bạn làm điều đó, bạn có thể cấu hình mức ghi nhật ký khi bạn đề cập đến một 'log4j.logger.a.b.c = INFO'? –

0

Quy ước chung là "một lớp trình ghi nhật ký và sử dụng tên lớp làm tên của nó". Đây là lời khuyên tốt.

Kinh nghiệm cá nhân của tôi là biến logger này KHÔNG được khai báo tĩnh mà là biến mẫu được truy xuất cho mỗi biến mới. Điều này cho phép khung ghi nhật ký xử lý hai cuộc gọi khác nhau tùy thuộc vào nơi họ đến. Một biến tĩnh là giống nhau cho tất cả các cá thể của lớp đó (trong trình nạp lớp đó).

Ngoài ra, bạn nên tìm hiểu tất cả các khả năng với phụ trợ ghi nhật ký của mình.Bạn có thể có khả năng bạn không mong đợi có thể.

+0

Chỉ cần một lưu ý, ít nhất là đối với log4j, nếu bạn đang sử dụng tên lớp như là tìm kiếm thì nó sẽ là cùng một bản ghi nhật ký mỗi lần. Không có lý do để lấy nó một lần nữa và một lần nữa cho mỗi trường hợp lớp. Bạn cũng có thể giữ nó tĩnh. – PSpeed

+1

Có những ưu điểm và nhược điểm đối với việc ghi nhật ký tĩnh. Chúng có thể có vấn đề trong các thùng chứa được quản lý và chúng không nên được sử dụng ở tất cả trong mã nhằm mục đích chạy bên trong OSGi. Xem ưu và khuyết điểm của SLF4J: http://www.slf4j.org/faq.html#declared_static – SteveD

10

Như đã nói bởi những người khác, tôi sẽ tạo ra một Logger mỗi lớp:

private final static Logger LOGGER = Logger.getLogger(Foo.class); 

hoặc

private final Logger logger = Logger.getLogger(this.getClass()); 

Tuy nhiên, tôi đã tìm thấy nó hữu ích trong quá khứ để có thông tin khác trong logger. Ví dụ, nếu bạn có một trang web, bạn có thể bao gồm ID người dùng trong mỗi thông điệp tường trình. Bằng cách đó ,, bạn có thể theo dõi mọi thứ mà người dùng đang làm (rất hữu ích để gỡ lỗi các vấn đề, v.v.).

Cách dễ nhất để thực hiện việc này là sử dụng MDC, nhưng bạn có thể sử dụng Trình ghi nhật ký được tạo cho từng phiên bản của lớp với tên bao gồm ID người dùng.

Một ưu điểm khác của việc sử dụng MDC là nếu bạn sử dụng SL4J, bạn có thể thay đổi cài đặt tùy thuộc vào các giá trị trong MDC của bạn. Vì vậy, nếu bạn muốn đăng nhập tất cả hoạt động cho một người dùng cụ thể ở cấp DEBUG và để tất cả những người dùng khác tại ERROR, bạn có thể. Bạn cũng có thể chuyển hướng đầu ra khác nhau đến các địa điểm khác nhau tùy thuộc vào MDC của bạn.

Một số liên kết hữu ích:

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html

+0

+1 Để đề cập đến SLF4J – Tim

+0

Tôi thích phần 'this.getClass()'. Bằng cách này, sẽ không có bất kỳ lỗi sao chép/dán nào liên quan đến tên trình ghi nhật ký. – winklerrr

3
  • Tạo một logger mỗi lớp.
  • Nếu bạn có các phụ thuộc yêu cầu Ghi nhật ký Commons (rất có thể) hãy sử dụng số bridge của slf4j để ghi nhật ký. Khởi tạo trình ghi nhật ký của bạn (mỗi lớp) bằng giao diện Ghi nhật ký Commons: private static final Log log = LogFactory.getLog(MyClass.class);
  • Hiển thị mẫu này trong IDE bằng phím tắt. Tôi sử dụng số live templates của IDEA cho mục đích này.
  • Cung cấp thông tin theo ngữ cảnh cho chủ đề bằng cách sử dụng NDC (chuỗi chuỗi cục bộ chuỗi) hoặc MDC (bản đồ cục bộ của chuỗi →?).

Ví dụ đối với các mẫu:

private static final Log log = LogFactory.getLog($class$.class); // live template 'log' 

if (log.isDebugEnabled()) 
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ... 
0

Khi triển khai nhiều tai/Wars, nó có thể là tốt hơn để đóng gói log4j.jar cao hơn trong hệ thống phân cấp classloader.
tức là không có trong WAR hoặc EAR, nhưng trong trình nạp lớp Hệ thống của vùng chứa của bạn, nếu không nhiều phiên bản Log4J sẽ ghi vào cùng một tệp đồng thời dẫn đến hành vi lạ.

13

Tôi chắc chắn đây không phải là phương pháp hay nhất, nhưng tôi đã sa thải một số thời gian khởi động trên các ứng dụng trước đây để lưu các dòng mã. Cụ thể, khi dán trong:

Logger logger = Logger.getLogger(MyClass.class); 

... nhà phát triển thường quên thay đổi "MyClass" thành tên lớp hiện tại và một số nhật ký luôn hướng gió ở vị trí không chính xác. Thật tệ.

Tôi đã thỉnh thoảng viết:

static Logger logger = LogUtil.getInstance(); 

Và:

class LogUtil { 
    public Logger getInstance() { 
     String callingClassName = 
     Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName(); 
     return Logger.getLogger(callingClassName); 
    } 
} 

Các "2" trong mã đó có thể sai, nhưng thực chất là có; có một hiệu suất hit (trên lớp tải, như là một biến tĩnh) tìm tên lớp, để một nhà phát triển không thực sự có một cách để loại bỏ điều này hoặc giới thiệu bất kỳ lỗi nào.

Tôi thường không vui mừng khi mất hiệu suất để ngăn lỗi nhà phát triển trong thời gian chạy, nhưng nếu nó xảy ra như một singleton, một lần? Thường thì âm thanh như một giao dịch tốt với tôi.

+0

Tôi đã cập nhật câu trả lời của bạn - nó không biên dịch và tôi đã sử dụng getClass(). GetCanonicalName() thay vì chỉ getClassName(). Oh, và 2 thực sự là số ma thuật. – ripper234

+0

Tôi đã viết một lớp tương tự vì những lý do bạn đã nêu. Vì vậy, tất cả là cần thiết là: tĩnh cuối cùng ClassLogger log = new ClassLogger(); Tôi đã kết hợp slf4j để cho phép nhiều triển khai nhật ký. Để biết chi tiết, hãy xem org.sormula.log.ClassLogger tìm thấy tại www.sormula.org. –

+1

Sử dụng getStackTrace() [2] .getClass(). GetCanonicalName(), bạn sẽ luôn nhận được "java.lang.StackTraceElement". Tôi đã nhận được kết quả tôi muốn bằng cách sử dụng 'callingClassName = Thread.currentThread(). GetStackTrace() [2] .getClassName()'. Tôi đã bình chọn câu trả lời nhưng không chỉnh sửa câu trả lời. Cảm ơn vì tiền hỗ trợ. – bigleftie

3

Tùy chọn khác: Bạn có thể thử AspectJ cắt chéo khi ghi nhật ký. Kiểm tra here : Simplify Your Logging. (Nếu bạn không muốn sử dụng AOP, bạn có thể nhìn slf4j)

//Without AOP 

    Class A{ 
     methodx(){ 
     logger.info("INFO"); 
     } 
    } 

    Class B{ 
     methody(){ 
     logger.info("INFO"); 
     } 
    } 

//With AOP 

    Class A{ 
     methodx(){ 
     ...... 
     } 
    } 

    Class B{ 
     methody(){ 
     ...... 
     } 
    } 

    Class LoggingInterceptor{ 

     //Catched defined method before process 
     public void before(...xyz){ 
     logger.info("INFO" + ...xyz); 
     } 

     //Catched defined method after processed   
     public void after(...xyz){ 
     logger.info("INFO" + ...xyz); 
     } 
     ..... 

    } 

Tái bút:AOP sẽ tốt hơn, nó là DRY(Don't Repeat Yourself) cách.

2

phương pháp tốt nhất và dễ nhất để tạo logger tùy chỉnh, notlinked cho bất kỳ classname là:

// create logger 
Logger customLogger = Logger.getLogger("myCustomLogName"); 

// create log file, where messages will be sent, 
// you can also use console appender 
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
              "/home/user/some.log"); 

// sometimes you can call this if you reuse this logger 
// to avoid useless traces 
customLogger.removeAllAppenders(); 

// tell to logger where to write 
customLogger.addAppender(fileAppender); 

// send message (of type :: info, you can use also error, warn, etc) 
customLogger.info("Hello! message from custom logger"); 

bây giờ, nếu bạn cần một logger trong cùng một lớp, không có vấn đề :) chỉ cần tạo mới một

// create logger 
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName"); 

bây giờ nhìn thấy đoạn code trên và tạo fileappender mới nên đầu ra của bạn sẽ được gửi trong file khác

này rất hữu ích cho (ít nhất) 2 tình huống

  • khi bạn muốn lỗi riêng biệt từ thông tin và cảnh báo

  • khi bạn quản lý nhiều quy trình và bạn cần đầu ra từ mỗi quá trình

ps. có một vài câu hỏi ? cảm thấy tự do để yêu cầu! :)

+1

Cách tiếp cận này là tốt nếu bạn không có mã thông thường, nếu bạn có 2 tệp nhật ký khác nhau cho 2 quy trình khác nhau và cả hai quy trình đều có một số mã phổ biến thì đó là một vấn đề. –

-1

Nếu ứng dụng của bạn đang theo nguyên tắc SOA, cho mỗi dịch vụ A bạn sẽ có các thành phần sau:

  1. Một điều khiển
  2. Một Thực hiện Dịch vụ
  3. Một Executor
  4. Một kiên trì

Vì vậy, nó giúp cuộc sống dễ dàng hơn để có một số aController.log aService.log aExecutor.đăng nhập và aPersistance.log

Đây là một tách lớp dựa vì vậy tất cả các lớp học Remoting/REST/SOAP của bạn sẽ ghi vào aController.log

Tất cả các cơ chế lập kế hoạch, dịch vụ phụ trợ của bạn vv sẽ viết thư cho aService.log

Và tất cả các thao tác tác vụ được ghi vào aExecutor.log, v.v.

Nếu bạn có trình xử lý đa luồng, bạn có thể phải sử dụng trình tích lũy nhật ký hoặc một kỹ thuật khác để căn chỉnh đúng thông điệp tường trình cho nhiều chuỗi. Bằng cách này bạn sẽ luôn luôn có 4 tập tin đăng nhập mà không phải là rất nhiều và không quá ít, tôi nói với bạn từ kinh nghiệm này làm cho cuộc sống thực sự dễ dàng hơn.

Các vấn đề liên quan