2011-12-09 44 views
24

Giúp tôi tham gia một cuộc tranh luận ở đây .. :)Ngay cả với slf4j, bạn có nên bảo vệ quá trình ghi nhật ký không?

Trang web slf4j ở đây http://www.slf4j.org/faq.html#logging_performance cho biết rằng do ghi nhật ký tham số, bảo vệ ghi nhật ký là không cần thiết. I E. thay vì viết:

if(logger.isDebugEnabled()) { 
    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 
} 

Bạn có thể nhận được ngay với:

Object entry = new SomeObject(); 
logger.debug("The entry is {}.", entry); 

Đây có phải là thực sự không sao, hay nó phải chịu sự (mặc dù thấp hơn) chi phí của việc tạo ra chuỗi tĩnh đã vượt qua phương pháp dấu vết ..?

Trả lời

33

tôi sẽ cố gắng đưa hai xu của tôi từ một góc độ

gì chính xác là vì lợi ích của khai thác gỗ parametrized?

Bạn chỉ cần hoãn toString() gọinối chuỗi cho đến khi thực sự cần thiết, đó là khi bạn thực sự cần phải đăng nhập vào tin nhắn. Điều này tối ưu hóa hiệu suất khi hoạt động ghi nhật ký cụ thể đó bị tắt. Kiểm tra source code for SLF4J nếu không chắc chắn.

Ghi nhật ký tham số có giúp bảo vệ vô dụng trong mọi trường hợp không?

số

Trong đó trường hợp sẽ bảo vệ khai thác gỗ được sử dụng?

Khi có các hoạt động đắt tiền tiềm năng khác.

Ví dụ (trong trường hợp hoạt động khai thác gỗ đặc biệt này bị vô hiệu hóa), nếu chúng ta có không bảo vệ khai thác gỗ

logger.debug("User: {}", getUserService().getCurrentUser()); 
  1. Chúng tôi sẽ trả chi phí từ obj = getUserService().getCurrentUser()
  2. Chúng tôi sẽ tiết kiệm chi phí từ "User name: " + obj.toString()

Nếu chúng tôi sử dụng khai thác gỗ gác:

if (logger.isDebugEnabled()) { 
    logger.debug("User: {}", getUserService().getCurrentUser()); 
} 
  1. Chúng tôi sẽ trả chi phí logger.isDebugEnabled()
  2. Chúng tôi sẽ lưu chi phí từ obj = getUserService().getCurrentUser()
  3. Chúng tôi sẽ tiết kiệm chi phí từ "User name: " + obj.toString()

Trong c sau ase, chúng tôi sẽ tiết kiệm cả chi phí ở mức giá kiểm tra isDebugEnabled() hai lần khi hoạt động ghi nhật ký cụ thể này được bật.

LƯU Ý: Đây chỉ là một ví dụ, không cố gắng tranh luận các thực tiễn tốt/xấu ở đây.

+0

Cảm ơn fgelz, đây là giải thích rõ ràng nhất vì vậy tôi đã thay đổi quyết định của mình. Làm cho bạn nghĩ rằng mặc dù tất cả các khuôn khổ khác nhau, vẫn còn chỗ cho sự đổi mới trong thế giới khai thác, về mặt trì hoãn các hoạt động tốn kém .. có thể với các đại biểu –

+0

@MarkD JDK 8 Dự án Lambda sẽ giúp đổi mới (http: // openjdk .java.net/projects/lambda /) – fglez

+0

Bạn cũng nên lưu ý rằng việc gọi một phương thức varargs (như logger.debug (String, Object ..) thực sự đang thực hiện dưới bìa logger.debug (String, new Object [. Vì vậy, nếu bạn không bọc các cuộc gọi, bạn đang tạo ra (sau đó xử lý) một mảng Object mới mỗi lần bạn đi qua phương thức này, ngay cả khi bạn không bao giờ viết nội dung ra ngoài. hoặc kinh nghiệm áp lực bộ nhớ sau đó điều này có thể xây dựng lên theo thời gian, và các cuộc gọi logger.isDebugEnabled() sẽ được lót bởi một JIT phong nha nếu được sử dụng rất nhiều SLF4J chuyên này cho một và hai đối số, nhưng để biết thêm bạn sẽ nhận được – AlBlue

2

Xin đừng sử dụng câu lệnh if, bởi vì mỗi khi tôi nhìn vào mã như thế này

if (logger.isDebug()) { 
    logger.debug("Of course it's debug {}", sadFace); 
} 

tôi khóc.

Tôi hy vọng rằng chi phí tạo chuỗi tĩnh quá thấp đến mức không đáng kể cho 99% người dùng.

+1

slf4j không sử dụng cấu trúc '{0}'. –

+0

Nếu bạn đang sử dụng rất nhiều bản ghi gỡ lỗi, mặc dù, mà hầu hết các ứng dụng lớn làm, nó có thể được rất nhiều chi phí. Bạn đang tạo thêm rác cho bộ thu gom rác để thu thập và một số hệ thống đã có đủ áp lực trong khu vực đó. Nếu bạn không thực hiện bất kỳ cuộc gọi nào để nhận thông số của mình và bạn chỉ sử dụng chuỗi không đổi cho chuỗi param, thì tốt cho đến khi có ai đó đến và thay đổi nó để làm điều gì đó ít tầm thường hơn. Nó dễ dàng hơn từ quan điểm 'chuẩn mã hóa' để chỉ yêu cầu câu lệnh if. – Dogs

15

Viết và đọc tất cả các số if(logger.isDebugEnabled()) {} có thể sẽ tốn nhiều thời gian khi chúng tiết kiệm cho bạn.

Tất nhiên, gọi phương thức nhật ký không miễn phí nhưng điều này cũng đúng cho việc gọi isDebugEnabled(). Vì vậy, bạn đang trả tiền nhiều hơn cho mỗi câu lệnh đăng nhập đang hoạt động nếu bạn sử dụng mẫu này (vì khung ghi nhật ký sẽ kiểm tra mức hai lần).

Nó cũng cắt mã.

Trong thực tế, tôi đã không tìm thấy hình phạt hiệu suất đủ lớn để làm phiền.

Nếu ghi nhật ký quá chậm đối với bạn, hãy viết ứng dụng không chặn để đẩy sự kiện nhật ký vào hàng đợi mà không chỉ một vài kiểm tra và sử dụng chuỗi nền để xử lý chúng.

Bối cảnh: Các ứng dụng chuẩn đều được đồng bộ hóa để đăng nhập vào ứng dụng đa luồng có thể gây ra rất nhiều tạm dừng nhỏ trong đó tất cả các chuỗi chờ thông điệp tường trình được ghi vào một tệp.

+0

Cảm ơn đó là những gì tôi nghĩ là tốt .. sẽ chờ đợi để xem những gì conscensus chung mặc dù trước khi tôi cung cấp cho bạn một cộng :) Tôi thích tip về các appenders mà cũng có thể là một vấn đề đối với chúng tôi. –

+0

Cả hai "gọi phương thức đăng nhập" và "gọi làDebugEnabled()" có thể ** thực sự miễn phí ** với một số tối ưu hóa JIT mà tôi nghĩ. Tôi không chắc chắn, nhưng nếu có thể là người cung cấp thực thi hiệu năng cao thực sự cho sl4jf, tôi sẽ đặt một triển khai với phương thức no-op tại thời điểm rutime cho các mức bị vô hiệu hóa, để JIT có thể loại bỏ cuộc gọi. –

4

Vấn đề với một tuyên bố khai thác gỗ như thế này:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 

là nó sẽ làm được rất nhiều việc phải nối giá trị vào một String, sau đó được không bao giờ sử dụng nếu debug logging tắt. Vì vậy, trong trường hợp đó, nó là có lợi để kiểm tra xem gỡ lỗi đăng nhập là trên trước khi thực hiện dòng này. Khi bạn chỉ chuyển các tham số:

logger.debug("The entry is {}.", entry); 

thì không cần thiết phải xây dựng String không bao giờ được sử dụng và séc là không cần thiết; chỉ cần chuyển các đối số cho một phương thức không có chi phí rất cao.

Lưu ý rằng nếu bạn có biểu thức tương đối đắt đối với các đối số trong câu lệnh ghi nhật ký, thì có thể vẫn có lợi khi kiểm tra mức ghi nhật ký trước.

+0

Bạn có thể xử lý các biểu thức đắt tiền bằng cách sử dụng chúng trong phương thức toString() trong một lớp ẩn danh để chúng không được đánh giá cho đến khi thực sự cần thiết. –

+0

@ ThorbjørnRavnAndersen Có, nhưng điều đó sẽ làm cho đoạn mã khá dài dòng. – Jesper

5

Bộ bảo vệ không được sử dụng do tạo chuỗi.

Thay vào đó, nó thường được sử dụng để tránh một biểu thức đối số có khả năng tốn kém, chẳng hạn như entry[i].retrieveExtendedDebugInformation().formatNicely(). Đối với điều đó, logback đảm bảo đối số được đánh giá chỉ khi thông điệp tường trình được in thực sự, trong khi log4j luôn luôn đánh giá đối số trước khi gọi debug().

Ở đây, ứng cử viên duy nhất là String.valueOf(entry[i]), không đắt lắm, vì vậy bạn có thể đưa ra lý lẽ rằng bảo vệ này hoàn toàn không cần thiết.

+1

okay - điều này có ý nghĩa và là một quan điểm khác về mọi thứ. Vì vậy, theo ý kiến ​​của bạn đó là một cuộc gọi phán quyết về việc có nên bảo vệ, và bảo vệ chỉ nên được sử dụng trên các hoạt động tương đối hiếm (đắt tiền) không? .. giới thiệu các tham số giảm số lượng của các hoạt động đó .. –

+0

Có, và tùy thuộc vào mức độ đắt tiền hoạt động là bảo vệ thường chỉ có ý nghĩa để truy tìm các câu lệnh bên trong vòng lặp. Người ta thậm chí có thể lập luận rằng nếu các câu lệnh debug của bạn quá đắt đến mức chúng làm chậm lớp ngay cả khi bạn đang ở trên một mức log cao hơn, thì bạn không sử dụng quyền log.debug, đặc biệt, bạn có thể đang làm 'while (điều kiện) {log.debug (complexExpression); doSomething();} 'trong khi bạn nên làm' log.debug (complexExpression); while (condition) {log.trace (simpleExpression); doSomething();} '. Nhưng tôi không phải là người cuồng nhiệt, tôi thừa nhận rằng tôi thường viết một người bảo vệ. – wallenborn

+0

"Không phải là rất tốn kém" vẫn còn đủ tốn kém để tạo sự khác biệt cho một chương trình có nhiều nhật ký. –

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