2011-10-02 3 views
18

Tôi hiện đang viết một dự án lớn trong java, với nhiều lớp, một số lớp nhỏ gọn, chỉ đơn giản là đại diện cho các đối tượng chỉ với vài phương thức. Tôi có một bộ ghi nhật ký trong lớp chính của mình và nó hoạt động tốt. Tôi muốn chỉ có thể sử dụng một trình ghi (với một bộ điều hợp) với tất cả các lớp. Tôi đã cố gắng chuyển một tham chiếu đến trình ghi nhật ký tới các lớp khác nhau, nhưng nó không có vẻ đúng. bên cạnh đó, đôi khi tôi chạy thử nghiệm trên các lớp mà không cần chạy chính và do đó trình ghi nhật ký không được khởi tạo cho các lớp khác.cách sử dụng log4j với nhiều lớp học?

Cách tiếp cận tốt nhất để thực hiện điều đó, ý tôi là, cách đăng nhập từ các lớp khác nhau sang một nhật ký, không có sự phụ thuộc cứng giữa các lớp và khả năng sử dụng nhật ký độc lập với mỗi lớp?

Trả lời

16

Nếu tôi hiểu đúng, những gì bạn có ít phút là:

public class Main { 
    public static final Logger LOGGER = Logger.getLogger(Main.class); 
} 

public class AnotherClass { 
    public void doSomething() { 
     Main.LOGGER.debug("value=" + value); 
    } 
} 

hay, bạn vượt qua tham chiếu đến một logger vào các nhà thầu của lớp.

Thứ nhất, bạn có thể sử dụng một logger toàn cầu bằng cách đơn giản bằng cách sử dụng cùng một giá trị truyền cho Logger.getLogger, như:

public class Main { 
    private static final Logger LOGGER = Logger.getLogger("GLOBAL"); 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger("GLOBAL"); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 

này sử dụng chính xác các logger cùng, Logger.getLogger trả về cùng một đối tượng trong cả hai cuộc gọi . Bạn không còn có một sự phụ thuộc giữa các lớp, và điều này sẽ làm việc.

Điều khác mà tôi thu thập từ nhận xét của bạn là bạn đang định cấu hình bằng tay (sử dụng BasicConfigurator.configure. Hầu hết thời gian này không cần thiết và bạn nên thực hiện cấu hình của mình bằng cách thêm log4j.properties hoặc log4j. Trong Eclipse, điều này được thực hiện bằng cách thêm nó vào src/(hoặc src/main/resources nếu bạn đang sử dụng maven) .Nếu bạn đang sử dụng junit, sau đó thêm nó vào thư mục test/source (hoặc src/test/resources with maven) Đây là cách tốt hơn để cấu hình log4j, vì bạn không phải truyền thông tin giữa các lớp.

Ngoài ra, cách được khuyến nghị sử dụng nhật ký là chuyển lớp học tới Logger.getLogger(). Bằng cách này bạn có thể lọc đầu ra của bạn dựa trên tên lớp, mà thường là nhiều hơn nữa hữu ích hơn là chỉ có một logger toàn cầu:

public class Main { 
    private static final Logger LOGGER = Logger.getLogger(Main.class); 
    public static final main(String[] args) { 
     LOGGER.debug("started"); 
    } 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger(this.getClass()); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 

Sau đó, trong log4j.properties, bạn có thể cấu hình một appender đơn vào một tập tin .

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Cuối cùng, không nhất thiết phải khai báo tất cả nhật ký của bạn là tĩnh. Điều này chỉ tạo ra sự khác biệt đáng chú ý nếu bạn đang thực hiện lô hàng [*] của việc tạo đối tượng. Khai báo các logger của bạn là các trường không tĩnh cho phép bạn sử dụng Logger.getLogger(this.getClass()); trong trường hợp thêm một logger vào một lớp sẽ trở thành một vết cắt và dán của một dòng đơn. Xem Should I declare Log references static or not? (tiếc là liên kết đến trang wiki bị hỏng), nhưng slf4j page cũng chứa một lời giải thích tốt. Vì vậy, sử dụng các lĩnh vực không tĩnh trừ khi bạn có một lý do rất tốt không.

Cameron đúng khi anh ấy nói rằng bạn nên thử và sử dụng slf4j nếu có thể, nó có một tính năng sát thủ, bạn có thể sử dụng nhiều khung công tác ghi nhật ký với nó.

[*] và ý tôi là rất nhiều.

+0

Tôi đã làm đúng như bạn đã đề cập, nhưng chỉ các nhật ký trong lớp Chính được in, nhật ký trong các phương pháp khác không được in? cách khắc phục –

5

Your logger instances should typically be private, static and final. Bằng cách đó, mỗi lớp sẽ có cá thể logger riêng (được tạo khi lớp được nạp), để bạn có thể xác định lớp mà bản ghi nhật ký được tạo ra, và bạn cũng không còn cần truyền các cá thể logger trên các lớp.

+0

Tôi đặt "BasicConfigurator.configure();" ở đâu? Trong một lớp học không có phương pháp chính? – stdcall

+0

@Mellowcandle, bạn không cần phải gọi 'BasicConfigurator.configure()' trong mỗi lớp. Làm điều đó, chỉ trong phương pháp chính của bạn của lớp khởi tạo ứng dụng của bạn, và chỉ khi bạn có. Chỉnh sửa: ['BasicConfigurator.configure'] (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/BasicConfigurator.html#configure%28%29) được sử dụng để tạo một thiết lập log4j, khi bạn thiếu tệp log4j.properties chức năng được định cấu hình với trình cắm và bố cục. –

+0

Bạn có ý nghĩa gì? Tôi cần chạy các bài kiểm tra đơn vị trên nhiều lớp. khi nào tôi phải chạy nó? – stdcall

4

Cách tốt nhất để làm là yêu cầu mỗi lớp có trình ghi nhật ký riêng (được đặt tên sau lớp), sau đó thiết lập cấu hình để tất cả đều nối vào cùng một người gắn thêm.

Ví dụ:

class A { 
    private static final Logger log = Logger.getLogger(A.class); 
} 

class B { 
    private static final Logger log = Logger.getLogger(B.class); 
} 

Sau đó log4j.properties của bạn có thể trông giống như ví dụ trong tài liệu log4j:

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Cả AB sẽ đăng nhập vào logger gốc và do đó đến cùng một appender (trong trường hợp này là console).

Điều này sẽ cung cấp cho bạn những gì bạn muốn: mỗi lớp là độc lập nhưng tất cả chúng đều ghi vào cùng một nhật ký. Bạn cũng có được tính năng tiền thưởng mà bạn có thể thay đổi mức độ đăng nhập cho mỗi lớp trong cấu hình log4j.

Để sang một bên, bạn có thể cân nhắc chuyển sang slf4j nếu dự án vẫn còn trong giai đoạn phát triển sớm. slf4j có một số cải tiến so với log4j giúp làm việc dễ dàng hơn một chút.

+0

Tôi đặt "BasicConfigurator.configure() ở đâu" Trong một lớp học không có phương pháp chính? – stdcall

+2

@Mellowcandle: Không. Sử dụng tệp 'log4j.properties'.Bạn chỉ cần đặt nó trên classpath và log4j sẽ sử dụng nó. –

+0

@CameronSkinner Tôi đã làm đúng như bạn đã đề cập, nhưng chỉ các nhật ký trong lớp Chính được in trong bảng điều khiển, nhật ký trong các phương thức khác không được in, cách khắc phục? –

1

Lý do bạn có nhiều bản ghi nhật ký là vì bạn muốn chúng hoạt động khác nhau khi ghi nhật ký (thường bằng cách in ra tên lớp mà chúng được định cấu hình). Nếu bạn không quan tâm về điều đó, bạn chỉ có thể tạo một cá thể logger tĩnh duy nhất trong một lớp và sử dụng tất cả các nơi đó.

Để tạo logger đơn, bạn chỉ cần tạo một lớp ghi nhật ký tiện ích tĩnh là logger điểm duy nhất, vì vậy nếu cần thay đổi gói logger, bạn sẽ chỉ cập nhật lớp này.

final public class Logger { 
    private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("Log"); 

    enum Level {Error, Warn, Fatal, Info, Debug} 

    private Logger() {/* do nothing */}; 

    public static void logError(Class clazz, String msg) { 
     log(Level.Error, clazz, msg, null); 
    } 

    public static void logWarn(Class clazz, String msg) { 
     log(Level.Warn, clazz, msg, null); 
    } 

    public static void logFatal(Class clazz, String msg) { 
     log(Level.Fatal, clazz, msg, null); 
    } 

    public static void logInfo(Class clazz, String msg) { 
     log(Level.Info, clazz, msg, null); 
    } 

    public static void logDebug(Class clazz, String msg) { 
     log(Level.Debug, clazz, msg, null); 
    } 


    public static void logError(Class clazz, String msg, Throwable throwable) { 
     log(Level.Error, clazz, msg, throwable); 
    } 


    public static void logWarn(Class clazz, String msg, Throwable throwable) { 
     log(Level.Warn, clazz, msg, throwable); 
    } 

    public static void logFatal(Class clazz, String msg, Throwable throwable) { 
     log(Level.Fatal, clazz, msg, throwable); 
    } 

    public static void logInfo(Class clazz, String msg, Throwable throwable) { 
     log(Level.Info, clazz, msg, throwable); 
    } 

    public static void logDebug(Class clazz, String msg, Throwable throwable) { 
     log(Level.Debug, clazz, msg, throwable); 
    } 

    private static void log(Level level, Class clazz, String msg, Throwable throwable) { 
     String message = String.format("[%s] : %s", clazz, msg); 
     switch (level) { 
      case Info: 
       logger.info(message, throwable); 
       break; 
      case Warn: 
       logger.warn(message, throwable); 
       break; 
      case Error: 
       logger.error(message, throwable); 
       break; 
      case Fatal: 
       logger.fatal(message, throwable); 
       break; 
      default: 
      case Debug: 
       logger.debug(message, throwable); 
     } 
    } 

}