Cách chính xác để xử lý đầu ra gỡ lỗi trong Java là gì?


32

Khi các dự án Java hiện tại của tôi ngày càng lớn hơn, tôi cảm thấy một nhu cầu ngày càng tăng để chèn đầu ra gỡ lỗi vào một số điểm trong mã của tôi.

Để bật hoặc tắt tính năng này một cách thích hợp, tùy thuộc vào việc mở hoặc đóng các phiên kiểm tra, tôi thường đặt private static final boolean DEBUG = falseở đầu các lớp mà các bài kiểm tra của tôi đang kiểm tra và sử dụng nó một cách tầm thường (ví dụ):

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

và như thế.

Nhưng điều đó không làm tôi vui, vì tất nhiên nó hoạt động nhưng có thể có quá nhiều lớp để đặt DEBUG thành đúng, nếu bạn không nhìn chằm chằm vào một vài trong số chúng.

Ngược lại, tôi (như - tôi nghĩ - nhiều người khác) sẽ không thích đưa toàn bộ ứng dụng vào chế độ gỡ lỗi, vì số lượng văn bản được xuất ra có thể áp đảo.

Vì vậy, có một cách chính xác để xử lý một cách kiến ​​trúc tình huống như vậy hay cách chính xác nhất là sử dụng thành viên lớp DEBUG?


14
trong Java, cách chính xác là KHÔNG sử dụng mã homebrew để ghi nhật ký. Chọn một khung đã được thiết lập, không phát minh lại bánh xe
gnat

Tôi sử dụng DEBUG boolean trong một số lớp phức tạp hơn của tôi, với cùng lý do như bạn đã nêu. Tôi thường không muốn gỡ lỗi toàn bộ ứng dụng, chỉ là lớp cho tôi vấn đề. Thói quen xuất phát từ những ngày COBOL của tôi, trong đó các câu lệnh HIỂN THỊ là hình thức gỡ lỗi duy nhất có sẵn.
Gilbert Le Blanc

1
Tôi cũng khuyên bạn nên dựa nhiều hơn vào trình gỡ lỗi khi có thể và không xả rác mã của bạn bằng các câu lệnh gỡ lỗi.
Andrew T Finnell

1
Bạn có thực hành Test Driven Development (TDD) với các bài kiểm tra đơn vị không? Khi tôi bắt đầu làm điều đó, tôi nhận thấy sự giảm đáng kể trong 'mã gỡ lỗi'.
JW01

Câu trả lời:


52

Bạn muốn xem xét một khung đăng nhập và có thể tại một khung mặt tiền đăng nhập.

Có nhiều khung ghi nhật ký ngoài kia, thường có các chức năng chồng chéo, đến mức theo thời gian, nhiều người đã tiến hóa dựa vào API chung hoặc đã được sử dụng thông qua khung mặt tiền để trừu tượng hóa việc sử dụng của họ và cho phép chúng được hoán đổi tại chỗ Nếu cần thiết.

Khung

Một số khung đăng nhập

  • Khung ghi nhật ký Java (một phần của JDK),
  • Apache Log4J (hơi cũ, nhưng vẫn mạnh mẽ và được duy trì tích cực),
  • LogBack (được tạo để cung cấp một cách tiếp cận hiện đại hơn Log4J, bởi một trong những người tạo ra Log4J ).

Một số mặt tiền đăng nhập

Sử dụng

Ví dụ cơ bản

Hầu hết các khung này sẽ cho phép bạn viết một cái gì đó có dạng (ở đây sử dụng slf4j-apilogback-core):

package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}

Lưu ý việc sử dụng lớp hiện tại để tạo một trình ghi nhật ký chuyên dụng, điều này sẽ cho phép SLF4J / LogBack định dạng đầu ra và cho biết thông điệp đăng nhập đến từ đâu.

Như đã lưu ý trong hướng dẫn SLF4J , một kiểu sử dụng điển hình trong một lớp thường là:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}

Nhưng trên thực tế, việc khai báo logger với biểu mẫu: thậm chí còn phổ biến hơn nữa.

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

Điều này cũng cho phép logger được sử dụng từ bên trong các phương thức tĩnh và nó được chia sẻ giữa tất cả các thể hiện của lớp. Đây hoàn toàn có khả năng là hình thức ưa thích của bạn. Tuy nhiên, như Brendan Long đã lưu ý trong các bình luận, bạn muốn chắc chắn hiểu được ý nghĩa và quyết định phù hợp (điều này áp dụng cho tất cả các khung đăng nhập theo các thành ngữ này).

Chẳng hạn, có nhiều cách khác để tạo logger, bằng cách sử dụng tham số chuỗi để tạo logger có tên:

Logger logger = LoggerFactory.getLogger("MyModuleName");

Mức gỡ lỗi

Mức độ gỡ lỗi thay đổi từ khung này sang khung khác, nhưng các mức phổ biến là (theo thứ tự quan trọng, từ lành tính đến dơi xấu, và có lẽ rất phổ biến đến hy vọng rất hiếm):

  • TRACE Thông tin rất chi tiết. Chỉ nên viết vào nhật ký. Chỉ được sử dụng để theo dõi lưu lượng của chương trình tại các điểm kiểm tra.

  • DEBUG Thông tin chi tiết. Chỉ nên viết vào nhật ký.

  • INFO Sự kiện thời gian chạy đáng chú ý. Nên hiển thị ngay lập tức trên bàn điều khiển, vì vậy hãy sử dụng một cách tiết kiệm.

  • WARNING Thời gian chạy kỳ lạ và lỗi có thể phục hồi.

  • ERROR Lỗi thời gian chạy khác hoặc điều kiện bất ngờ.

  • FATAL Lỗi nghiêm trọng gây ra chấm dứt sớm.

Khối và vệ binh

Bây giờ, giả sử bạn có một phần mã nơi bạn sắp viết một số câu lệnh gỡ lỗi. Điều này có thể nhanh chóng ảnh hưởng đến hiệu suất của bạn, cả do tác động của việc ghi nhật ký và tạo ra bất kỳ tham số nào bạn có thể chuyển sang phương thức ghi nhật ký.

Để tránh loại vấn đề này, bạn thường muốn viết một cái gì đó có dạng:

if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}

Nếu bạn đã không sử dụng bảo vệ này trước khối lệnh gỡ lỗi của mình, mặc dù các thông báo có thể không được xuất ra (ví dụ, nếu logger của bạn hiện được cấu hình để chỉ in những thứ ở trên INFOmức), heavyComputation()phương thức vẫn sẽ được thực thi .

Cấu hình

Cấu hình khá phụ thuộc vào khung đăng nhập của bạn, nhưng chúng cung cấp hầu hết các kỹ thuật tương tự cho việc này:

  • cấu hình lập trình (tại thời gian chạy, thông qua API - cho phép thay đổi thời gian chạy ),
  • cấu hình khai báo tĩnh (tại thời điểm bắt đầu, thường thông qua tệp XML hoặc tệp thuộc tính - có thể là thứ bạn cần lúc đầu ).

Họ cũng cung cấp hầu hết các khả năng tương tự:

  • cấu hình định dạng của thông báo đầu ra (dấu thời gian, điểm đánh dấu, v.v ...),
  • cấu hình của các mức đầu ra,
  • cấu hình của các bộ lọc hạt mịn (ví dụ để bao gồm / loại trừ các gói hoặc lớp),
  • cấu hình của các appender để xác định nơi đăng nhập (vào bảng điều khiển, tệp, dịch vụ web ...) và có thể phải làm gì với các bản ghi cũ hơn (ví dụ: với các tệp tự động cuộn).

Đây là một ví dụ phổ biến về cấu hình khai báo, sử dụng logback.xmltệp.

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Như đã đề cập, điều này phụ thuộc vào khung của bạn và có thể có các lựa chọn thay thế khác (ví dụ, LogBack cũng cho phép sử dụng tập lệnh Groovy). Định dạng cấu hình XML cũng có thể thay đổi từ cách triển khai này sang cách khác.

Để biết thêm ví dụ về cấu hình, vui lòng tham khảo (trong số những người khác) để:

Một số niềm vui lịch sử

Hãy lưu ý rằng Log4J đang chứng kiến một bản cập nhật lớn vào lúc này, chuyển từ phiên bản 1.x để 2.x . Bạn có thể muốn xem cả hai để có thêm niềm vui hoặc sự nhầm lẫn lịch sử và nếu bạn chọn Log4J có thể thích đi với phiên bản 2.x.

Điều đáng chú ý, như Mike Partridge đã đề cập trong các bình luận, rằng LogBack được tạo bởi một cựu thành viên nhóm Log4J. Được tạo ra để giải quyết các thiếu sót của khung công tác Ghi nhật ký Java. Và phiên bản Log4J 2.x chính sắp ra mắt hiện đang tích hợp một vài tính năng được lấy từ LogBack.

sự giới thiệu

Tóm lại, hãy tách rời càng nhiều càng tốt, chơi xung quanh với một vài người, và xem những gì phù hợp nhất với bạn. Cuối cùng, nó chỉ là một khung đăng nhập . Ngoại trừ nếu bạn có một lý do rất cụ thể, ngoài việc dễ sử dụng và sở thích cá nhân, bất kỳ điều nào trong số này sẽ làm tốt hơn vì vậy không có lý do gì để treo lên nó. Hầu hết trong số họ cũng có thể được mở rộng theo nhu cầu của bạn.

Tuy nhiên, nếu tôi phải chọn một sự kết hợp ngày hôm nay, tôi sẽ đi với LogBack + SLF4J. Nhưng nếu bạn đã hỏi tôi vài năm sau, tôi đã đề xuất Log4J với Nhật ký Apache Commons, vì vậy hãy theo dõi sự phụ thuộc của bạn và phát triển cùng với họ.


1
SLF4J và LogBack được viết bởi người đầu tiên viết Log4J.
Mike Partridge

4
Đối với những người có thể lo lắng về tác động hiệu suất của việc đăng nhập: slf4j.org/faq.html#logging_performance
Mike Partridge

2
Điều đáng nói là không nên rõ ràng liệu bạn có nên tạo logger hay không static, vì nó tiết kiệm một lượng bộ nhớ nhỏ, nhưng gây ra sự cố trong một số trường hợp nhất định: slf4j.org/faq.html#declared_static
Phục hồi lại

1
@MikePartridge: Tôi biết nội dung của liên kết, nhưng nó vẫn không ngăn chặn việc đánh giá tham số chẳng hạn. Lý do ghi nhật ký tham số được thực hiện nhiều hơn là vì việc xử lý thông điệp tường trình sẽ không xảy ra (chuỗi đáng chú ý là chuỗi). Tuy nhiên, bất kỳ lệnh gọi phương thức nào cũng sẽ được thực thi nếu nó được truyền dưới dạng tham số. Vì vậy, tùy thuộc vào trường hợp sử dụng của bạn, các khối có thể hữu ích. Và như đã đề cập trong bài đăng, chúng cũng có thể hữu ích cho bạn chỉ để nhóm các hoạt động liên quan đến gỡ lỗi khác (không chỉ ghi nhật ký) xảy ra khi mức DEBUG được bật.
haylem

1
@haylem - Đó là sự thật, sai lầm của tôi.
Mike Partridge

2

sử dụng khung đăng nhập

hầu hết thời gian có một phương pháp nhà máy tĩnh

private static final Logger logger = Logger.create("classname");

sau đó bạn có thể xuất mã đăng nhập của mình với các cấp độ khác nhau:

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

sau đó sẽ có một tệp duy nhất nơi bạn có thể xác định thông điệp nào cho mỗi lớp sẽ được ghi vào đầu ra nhật ký (tệp hoặc stderr)


1

Đây chính xác là những khung đăng nhập như log4j hoặc slf4j mới hơn có nghĩa là gì. Chúng cho phép bạn kiểm soát việc đăng nhập rất chi tiết và định cấu hình nó ngay cả khi ứng dụng đang chạy.


0

Một khung đăng nhập chắc chắn là con đường để đi. Tuy nhiên, bạn cũng phải có một bộ kiểm tra tốt. Phạm vi kiểm tra tốt thường có thể loại bỏ sự cần thiết của đầu ra gỡ lỗi cùng nhau.


Nếu bạn sử dụng khung đăng nhập và có sẵn bản ghi gỡ lỗi - sẽ đến lúc điều này sẽ giúp bạn tránh khỏi một ngày thực sự tồi tệ.
Fortyrunner

1
Tôi không nói bạn không nên đăng nhập. Tôi nói bạn cần phải có xét nghiệm đầu tiên.
Dima
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.