Trình ghi nhật ký slf4j lợi thế của định dạng với {} thay vì nối chuỗi


100

Có lợi thế nào của việc sử dụng {}thay vì nối chuỗi không?

Một ví dụ từ slf4j

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

thay vì

logger.debug("Temperature set to"+ t + ". Old temperature was " + oldT);

Tôi nghĩ đó là về tối ưu hóa tốc độ vì đánh giá tham số (và nối chuỗi) có thể tránh được trong thời gian chạy tùy thuộc vào tệp cấu hình. Nhưng chỉ có thể có hai tham số, thì đôi khi không có sự lựa chọn nào khác hơn là nối chuỗi. Cần quan điểm về vấn đề này.

Câu trả lời:


74

Đó về hiệu suất nối chuỗi. Nó có khả năng quan trọng nếu bạn có báo cáo ghi nhật ký dày đặc.

(Trước SLF4J 1.7) Nhưng chỉ có thể có hai tham số

Bởi vì phần lớn các câu lệnh ghi nhật ký có 2 hoặc ít tham số hơn, vì vậy SLF4J API lên đến phiên bản 1.6 bao gồm (chỉ) phần lớn các trường hợp sử dụng. Các nhà thiết kế API đã cung cấp các phương thức quá tải với các tham số varargs kể từ phiên bản API 1.7.

Đối với những trường hợp bạn cần nhiều hơn 2 và bạn bị mắc kẹt với SLF4J trước 1.7, thì chỉ cần sử dụng nối chuỗi hoặc new Object[] { param1, param2, param3, ... }. Sẽ có ít trong số chúng mà hiệu suất không quan trọng bằng.


2
Nên tránh nối chuỗi không sử dụng (tức là các câu lệnh gỡ lỗi). Sử dụng kiểm tra mức nhật ký (quá dài dòng nhưng hiệu quả) hoặc tham số mảng đối tượng (mỏng hơn, nhưng có thể là chi phí nhỏ). (Tôi thích cái sau hơn, tất cả mọi thứ đều bình đẳng.) Thật khó để nói rằng chuỗi concat sẽ không quan trọng / sẽ không ảnh hưởng đến hiệu suất. Về lý thuyết, việc tạo mảng đối tượng có thể được nội tuyến hóa và tối ưu hóa và "thực sự" không tạo ra sự khác biệt (so với mơ tưởng). (Nó không phải là tối ưu hóa quá sớm, nó chỉ đơn giản là vấn đề làm một cái gì đó đúng / tốt hơn trong lần đầu tiên.)
michael

tại sao sửa đổi quá tải không được thực hiện cho System.out.println () để tuân theo tương tự như trình ghi của slf4j, để nó tránh nối chuỗi?
a3.14_Infinity

44

Phiên bản ngắn: Có, nó nhanh hơn, với ít mã hơn!

Nối chuỗi thực hiện rất nhiều công việc mà không cần biết nó có cần thiết hay không (kiểm tra truyền thống "đang bật gỡ lỗi" được biết đến từ log4j) và nên tránh nếu có thể, vì {} cho phép trì hoãn lệnh gọi toString () và xây dựng chuỗi sau khi nó đã được quyết định xem sự kiện có cần chụp hay không. Bởi có những định dạng logger một đơn chuỗi mã trở nên sạch hơn trong quan điểm của tôi.

Bạn có thể cung cấp bất kỳ số lượng đối số nào. Lưu ý rằng nếu bạn sử dụng phiên bản cũ của sljf4j và bạn có nhiều hơn hai đối số {}, bạn phải sử dụng new Object[]{a,b,c,d}cú pháp để chuyển một mảng. Xem ví dụ: http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object []) .

Về tốc độ: Ceki đã đăng một điểm chuẩn cách đây một thời gian trên một trong những danh sách.


6
lưu ý: javadoc mới nhất hiển thị cú pháp var-arg mới hơn , debug(String format, Object... arguments). Xem slf4j.org/faq.html#logging_performance
michael

Được ủng hộ vì đề cập đến đánh giá .toString () ngoài hiệu suất nối. Đây là điều gì đó xảy ra bên trong trình ghi nhật ký và trình ghi nhật ký có thể quyết định xem có cần thiết phải gọi phương thức đó hay không. Nó không xảy ra nếu thanh mức ghi nhật ký không được đáp ứng.
Chetan Narsude

6

Bởi vì, Chuỗi là bất biến trong Java, vì vậy Chuỗi bên trái và bên phải phải được sao chép vào Chuỗi mới cho mỗi cặp nối. Vì vậy, tốt hơn hãy sử dụng trình giữ chỗ.


2
Điều này đúng nếu có một cặp duy nhất, nhưng nhìn chung không chính xác, vì trình biên dịch biến nối thành các lệnh gọi trình tạo chuỗi, dẫn đến mã nhanh hơn nhiều mà không phân bổ nhiều.
cdeszaq

3

Một thay thế khác là String.format(). Chúng tôi đang sử dụng nó trong jcabi-log (trình bao bọc tiện ích tĩnh xung quanh slf4j).

Logger.debug(this, "some variable = %s", value);

Nó có thể bảo trì và mở rộng hơn nhiều. Bên cạnh đó, rất dễ dàng để dịch.


3
Tôi không nghĩ rằng nó dễ bảo trì hơn. nếu loại valuethay đổi, bạn cũng phải quay lại và thay đổi câu lệnh ghi nhật ký. Một cái gì đó mà IDE sẽ không giúp bạn. Người ghi nhật ký nên hỗ trợ gỡ lỗi và không gặp khó khăn. :-)
Chetan Narsude 31/03/16

3
@ChetanNarsude IntelliJ 2016 ít nhất cho tôi biết khi nào chuỗi định dạng không phù hợp với các đối số định dạng. Ví dụ: String.format("%d", "Test")tạo ra cảnh báo IntelliJ Argument type 'String' does not match the type of the format specifier '%d'.. Mặc dù vậy, tôi không chắc nó vẫn có thể cung cấp phản hồi thông minh này khi làm việc với giải pháp trên.
nghiền nát

tốc độ của cái này là bao nhiêu?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen nó bên trong khá thô sơ, nhưng tất nhiên nó chậm hơn so với một tĩnh logger
yegor256

Gói slf4j? điều đó không đánh bại mục đích của việc sử dụng slf4j? Ngoài ra, tôi đã thấy nhiều người lạm dụng String.format để chuỗi được định dạng trước khi mức nhật ký được đánh giá, như: logger.info (String.format ("hello% s", tên người dùng)).
Juan Bustamante

2

Tôi nghĩ theo quan điểm của tác giả, lý do chính là để giảm chi phí cho việc nối chuỗi. Chỉ cần đọc tài liệu của trình ghi nhật ký, bạn có thể tìm thấy những từ sau:

/**
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the DEBUG level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before 
  invoking the method,
* even if this logger is disabled for DEBUG. The variants taking
* {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two}
* arguments exist solely in order to avoid this hidden cost.</p>
*/
*
 * @param format    the format string
 * @param arguments a list of 3 or more arguments
 */
public void debug(String format, Object... arguments);
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.