Lần đầu tiên trong đời tôi thấy mình ở một vị trí mà tôi đang viết một API Java sẽ được mở nguồn. Hy vọng sẽ được đưa vào nhiều dự án khác.
Để đăng nhập tôi (và thực sự những người tôi làm việc cùng) đã luôn sử dụng JUL (java.util.logging) và không bao giờ có bất kỳ vấn đề nào với nó. Tuy nhiên bây giờ tôi cần hiểu chi tiết hơn những gì tôi nên làm để phát triển API. Tôi đã thực hiện một số nghiên cứu về điều này và với thông tin tôi có được, tôi càng bối rối hơn. Do đó bài này.
Vì tôi đến từ JUL nên tôi thiên vị về điều đó. Kiến thức của tôi về phần còn lại không phải là lớn.
Từ nghiên cứu tôi đã thực hiện, tôi đã đưa ra những lý do tại sao mọi người không thích JUL:
"Tôi đã bắt đầu phát triển Java từ lâu trước khi Sun phát hành JUL và việc tiếp tục với log-framework-X dễ dàng hơn là học một cái gì đó mới" . Hừm. Tôi không đùa, đây thực sự là những gì mọi người nói. Với lập luận này, tất cả chúng ta có thể thực hiện COBOL. (tuy nhiên tôi chắc chắn có thể liên quan đến việc đây là một anh chàng lười biếng)
"Tôi không thích tên của các cấp ghi nhật ký trong JUL" . Ok, nghiêm túc, điều này không đủ lý do để giới thiệu một phụ thuộc mới.
"Tôi không thích định dạng chuẩn của đầu ra từ JUL" . Hừm. Đây chỉ là cấu hình. Bạn thậm chí không phải làm bất cứ điều gì khôn ngoan. (đúng, trở lại ngày xưa, bạn có thể phải tạo lớp Formatter của riêng mình để làm cho đúng).
"Tôi sử dụng các thư viện khác cũng sử dụng log-framework-X vì vậy tôi nghĩ việc sử dụng thư viện đó dễ dàng hơn" . Đây là một đối số tròn, phải không? Tại sao 'mọi người' sử dụng log-framework-X mà không phải JUL?
"Mọi người khác đang sử dụng log-framework-X" . Điều này với tôi chỉ là một trường hợp đặc biệt ở trên. Đa số không phải lúc nào cũng đúng.
Vì vậy, câu hỏi lớn thực sự là tại sao không JUL? . Tôi đã bỏ lỡ điều gì? Cơ sở cho việc ghi nhật ký (SLF4J, JCL) là nhiều triển khai khai thác đã tồn tại trong lịch sử và lý do cho điều đó thực sự quay trở lại thời đại trước JUL như tôi thấy. Nếu JUL hoàn hảo thì mặt tiền đăng nhập sẽ không tồn tại, hay sao? Để làm cho vấn đề trở nên khó hiểu hơn JUL là ở một mức độ nào đó, chính mặt tiền, cho phép Người xử lý, Trình định dạng và thậm chí Trình quản lý Log được hoán đổi.
Thay vì chấp nhận nhiều cách để làm cùng một việc (ghi nhật ký), chúng ta không nên đặt câu hỏi tại sao chúng lại cần thiết ngay từ đầu? (và xem nếu những lý do đó vẫn còn tồn tại)
Ok, nghiên cứu của tôi cho đến nay đã dẫn đến một vài điều mà tôi có thể thấy có thể là vấn đề thực sự với JUL:
Hiệu suất . Một số người nói rằng hiệu suất trong SLF4J là vượt trội so với phần còn lại. Đây dường như là một trường hợp tối ưu hóa sớm. Nếu bạn cần đăng nhập hàng trăm megabyte mỗi giây thì tôi không chắc bạn đang đi đúng hướng. JUL cũng đã phát triển và các thử nghiệm bạn đã thực hiện trên Java 1.4 có thể không còn đúng nữa. Bạn có thể đọc về nó ở đây và cách khắc phục này đã biến nó thành Java 7. Nhiều người cũng nói về chi phí của việc nối chuỗi trong các phương thức ghi nhật ký. Tuy nhiên, việc ghi nhật ký dựa trên mẫu sẽ tránh được chi phí này và nó cũng tồn tại trong JUL. Cá nhân tôi không bao giờ thực sự viết mẫu dựa trên đăng nhập. Quá lười biếng cho điều đó. Ví dụ: nếu tôi làm điều này với JUL:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
IDE của tôi sẽ cảnh báo tôi và xin phép rằng nó sẽ thay đổi nó thành:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. mà tôi tất nhiên sẽ chấp nhận. Đã được cho phép ! Cảm ơn sự giúp đỡ của bạn.
Vì vậy, tôi thực sự không viết những tuyên bố như vậy, được thực hiện bởi IDE.
Để kết luận về vấn đề hiệu suất, tôi không tìm thấy bất cứ điều gì cho thấy hiệu suất của JUL không ổn so với đối thủ.
Cấu hình từ classpath . JUL ngoài hộp không thể tải tệp cấu hình từ đường dẫn lớp. Đó là một vài dòng mã để làm cho nó làm như vậy. Tôi có thể thấy tại sao điều này có thể gây phiền nhiễu nhưng giải pháp ngắn và đơn giản.
Sẵn có của xử lý đầu ra . JUL đi kèm với 5 trình xử lý đầu ra bên ngoài: bảng điều khiển, luồng tệp, ổ cắm và bộ nhớ. Đây có thể được mở rộng hoặc những cái mới có thể được viết. Ví dụ, điều này có thể được ghi vào UNIX / Linux Syslog và Windows Event Log. Cá nhân tôi chưa bao giờ có yêu cầu này và tôi chưa thấy nó được sử dụng nhưng tôi chắc chắn có thể liên quan đến lý do tại sao nó có thể là một tính năng hữu ích. Logback đi kèm với một ứng dụng cho Syslog chẳng hạn. Tôi vẫn sẽ tranh luận rằng
- 99,5% nhu cầu cho các điểm đến đầu ra được bảo đảm bởi những gì có sẵn trong JUL.
- Nhu cầu đặc biệt có thể được phục vụ cho những người xử lý tùy chỉnh trên đầu JUL chứ không phải trên một thứ khác. Không có gì với tôi cho thấy rằng cần nhiều thời gian hơn để viết trình xử lý đầu ra Syslog cho JUL so với khung công tác ghi nhật ký khác.
Tôi thực sự lo ngại rằng có một cái gì đó tôi đã bỏ qua. Việc sử dụng các mặt tiền ghi nhật ký và triển khai ghi nhật ký khác với JUL rất phổ biến đến nỗi tôi phải đi đến kết luận rằng đó là tôi, người không hiểu. Đó không phải là lần đầu tiên, tôi sợ. :-)
Vậy tôi nên làm gì với API của mình? Tôi muốn nó trở nên thành công. Tất nhiên tôi có thể chỉ "đi theo dòng chảy" và thực hiện SLF4J (có vẻ phổ biến nhất hiện nay) nhưng vì lợi ích của riêng tôi, tôi vẫn cần hiểu chính xác những gì sai với JUL của ngày hôm nay để đảm bảo tất cả các fuzz? Tôi sẽ phá hoại bản thân mình bằng cách chọn JUL cho thư viện của mình chứ?
Kiểm tra hiệu suất
(phần được thêm bởi nolan600 vào ngày 07 tháng 7 năm 2012)
Có một tài liệu tham khảo dưới đây từ Ceki về tham số của SLF4J nhanh hơn 10 lần so với JUL. Vì vậy, tôi đã bắt đầu làm một số thử nghiệm đơn giản. Thoạt nhìn yêu sách chắc chắn là đúng. Dưới đây là kết quả sơ bộ (nhưng đọc tiếp!):
- Thời gian thực hiện SLF4J, phụ trợ Đăng nhập lại: 1515
- Thời gian thực hiện SLF4J, phụ trợ JUL: 12938
- Thời gian thực hiện JUL: 16911
Những con số trên là msecs nên ít hơn là tốt hơn. Vì vậy, 10 lần khác biệt hiệu suất là lần đầu tiên thực sự khá gần. Phản ứng ban đầu của tôi: Đó là rất nhiều!
Đây là cốt lõi của bài kiểm tra. Như có thể thấy một số nguyên và một chuỗi được hiểu trong một vòng lặp sau đó được sử dụng trong câu lệnh log:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(Tôi muốn câu lệnh log có cả kiểu dữ liệu nguyên thủy (trong trường hợp này là int) và kiểu dữ liệu phức tạp hơn (trong trường hợp này là String). Không chắc nó có vấn đề nhưng bạn có nó.)
Báo cáo nhật ký cho SLF4J:
logger.info("Logging {} and {} ", i, someString);
Báo cáo nhật ký cho JUL:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
JVM đã được 'làm nóng' với cùng một thử nghiệm được thực hiện một lần trước khi thực hiện phép đo thực tế. Java 1.7.03 đã được sử dụng trên Windows 7. Phiên bản mới nhất của SLF4J (v1.6.6) và Logback (v1.0.6) đã được sử dụng. Stdout và stderr đã được chuyển hướng đến thiết bị null.
Tuy nhiên, bây giờ hãy cẩn thận, hóa ra JUL đang dành phần lớn thời gian của mình getSourceClassName()
vì JUL mặc định in tên lớp nguồn trong đầu ra, trong khi Logback thì không. Vì vậy, chúng tôi đang so sánh táo và cam. Tôi phải làm lại bài kiểm tra và cấu hình các cài đặt ghi nhật ký theo cách tương tự để chúng thực sự xuất ra cùng một thứ. Tuy nhiên, tôi nghi ngờ rằng SLF4J + Logback vẫn sẽ đứng đầu nhưng khác xa với những con số ban đầu như đã nêu ở trên. Giữ nguyên.
Btw: Thử nghiệm là lần đầu tiên tôi thực sự làm việc với SLF4J hoặc Logback. Một trải nghiệm thú vị. JUL chắc chắn là ít chào đón hơn khi bạn bắt đầu.
Hiệu suất thử nghiệm (phần 2)
(phần được thêm bởi nolan600 vào ngày 08 tháng 8 năm 2012)
Vì hóa ra nó không thực sự quan trọng đối với hiệu suất như thế nào khi bạn định cấu hình mẫu của mình trong JUL, tức là nó có bao gồm tên nguồn hay không. Tôi đã thử với một mẫu rất đơn giản:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
và điều đó đã không thay đổi thời gian trên. Trình hồ sơ của tôi tiết lộ rằng trình ghi nhật ký vẫn dành nhiều thời gian cho các cuộc gọi đến getSourceClassName()
ngay cả khi đây không phải là một phần trong mẫu của tôi. Các mô hình không quan trọng.
Do đó, tôi kết luận về vấn đề hiệu suất rằng ít nhất là đối với câu lệnh nhật ký dựa trên mẫu được kiểm tra dường như có khoảng 10 yếu tố khác biệt về hiệu suất thực giữa JUL (chậm) và SLF4J + Logback (nhanh). Đúng như Ceki nói.
Tôi cũng có thể thấy một điều khác là getLogger()
cuộc gọi của SLF4J đắt hơn rất nhiều so với cuộc gọi của JUL. (95 ms so với 0,3 ms nếu hồ sơ của tôi là chính xác). Điều này thật ý nghĩa. SLF4J phải thực hiện một số thời gian để ràng buộc việc thực hiện ghi nhật ký cơ bản. Điều này không làm tôi sợ. Các cuộc gọi này sẽ hơi hiếm trong vòng đời của một ứng dụng. Độ bền phải có trong các cuộc gọi nhật ký thực tế.
Kết luận cuối cùng
(phần được thêm bởi nolan600 vào ngày 08 tháng 8 năm 2012)
Cảm ơn bạn cho tất cả các câu trả lời của bạn. Trái với những gì tôi nghĩ ban đầu tôi đã quyết định sử dụng SLF4J cho API của mình. Điều này dựa trên một số điều và đầu vào của bạn:
Nó cho phép linh hoạt để chọn thực hiện nhật ký tại thời điểm triển khai.
Các vấn đề thiếu linh hoạt về cấu hình của JUL khi chạy bên trong máy chủ ứng dụng.
SLF4J chắc chắn nhanh hơn rất nhiều như chi tiết ở trên, đặc biệt nếu bạn kết hợp nó với Logback. Ngay cả khi đây chỉ là một thử nghiệm sơ bộ, tôi vẫn có lý do để tin rằng đã có nhiều nỗ lực hơn để tối ưu hóa trên SLF4J + Logback so với JUL.
Tài liệu. Tài liệu về SLF4J đơn giản là toàn diện và chính xác hơn rất nhiều.
Hoa văn linh hoạt. Khi tôi thực hiện các bài kiểm tra, tôi đã đặt ra để JUL bắt chước mẫu mặc định từ Logback. Mẫu này bao gồm tên của chủ đề. Hóa ra JUL không thể làm điều này ra khỏi hộp. Ok, tôi đã không bỏ lỡ nó cho đến bây giờ, nhưng tôi không nghĩ rằng đó là một điều nên thiếu trong khung đăng nhập. Giai đoạn = Stage!
Hầu hết (hoặc nhiều) các dự án Java ngày nay sử dụng Maven, vì vậy việc thêm một phụ thuộc không phải là vấn đề lớn đặc biệt là nếu sự phụ thuộc đó khá ổn định, tức là không liên tục thay đổi API của nó. Điều này dường như đúng với SLF4J. Ngoài ra bình SLF4J và bạn bè có kích thước nhỏ.
Vì vậy, điều kỳ lạ đã xảy ra là tôi thực sự cảm thấy khó chịu với JUL sau khi đã làm việc một chút với SLF4J. Tôi vẫn hối tiếc rằng nó phải theo cách này với JUL. JUL là xa hoàn hảo nhưng loại công việc. Chỉ là không đủ tốt. Điều tương tự cũng có thể nói về Properties
một ví dụ nhưng chúng tôi không nghĩ về việc trừu tượng hóa để mọi người có thể cắm vào thư viện cấu hình của riêng họ và những gì có bạn. Tôi nghĩ lý do là Properties
ở ngay phía trên quán bar trong khi điều ngược lại là đúng với JUL của ngày hôm nay ... và trong quá khứ, nó ở mức 0 vì nó không tồn tại.
java.lang.System.Logger
, đó là một giao diện , có thể được chuyển hướng đến bất kỳ khung ghi nhật ký thực tế nào bạn muốn, miễn là khung đó bắt kịp và cung cấp triển khai giao diện đó. Kết hợp với mô đun hóa, bạn thậm chí có thể triển khai một ứng dụng với JRE đi kèm không chứa java.util.logging
, nếu bạn thích một khung khác.