เป็นครั้งแรกในชีวิตของฉันฉันพบว่าตัวเองอยู่ในตำแหน่งที่ฉันเขียน Java API ที่จะเปิดแหล่งที่มา หวังว่าจะรวมอยู่ในโครงการอื่น ๆ อีกมากมาย
สำหรับการบันทึกฉัน (และแน่นอนคนที่ฉันทำงานด้วย) มักจะใช้ JUL (java.util.logging) และไม่เคยมีปัญหาใด ๆ อย่างไรก็ตามตอนนี้ฉันต้องเข้าใจในรายละเอียดเพิ่มเติมสิ่งที่ฉันควรทำเพื่อการพัฒนา API ของฉัน ฉันได้ทำการวิจัยเกี่ยวกับเรื่องนี้และข้อมูลที่ฉันได้รับฉันเพิ่งสับสนมากขึ้น ดังนั้นโพสต์นี้
ตั้งแต่ฉันมาจาก JUL ฉันลำเอียง ความรู้ที่เหลือของฉันไม่ใหญ่มาก
จากการวิจัยที่ฉันทำฉันได้พบกับเหตุผลเหล่านี้ว่าทำไมคนถึงไม่ชอบ JUL:
"ผมเริ่มพัฒนาในชวานานก่อนที่ดวงอาทิตย์ปล่อยออกกรกฎาคมและมันก็เป็นเพียงแค่ง่ายสำหรับผมที่จะดำเนินการกับการเข้าสู่ระบบกรอบ-X มากกว่าที่จะเรียนรู้สิ่งใหม่" อืมมม ฉันไม่ได้ล้อเล่นนี่เป็นสิ่งที่ผู้คนพูด ด้วยเหตุผลนี้เราทุกคนสามารถทำได้ COBOL (อย่างไรก็ตามฉันสามารถเกี่ยวข้องกับการเป็นเพื่อนขี้เกียจอย่างแน่นอน)
"ผมไม่ชอบชื่อของระดับการเข้าสู่ระบบในกรกฎาคมว่า" ตกลงอย่างจริงจังนี่เป็นเพียงเหตุผลไม่เพียงพอที่จะแนะนำการพึ่งพาใหม่
"ผมไม่ชอบรูปแบบมาตรฐานของการส่งออกจากกรกฎาคม" อืมมม นี่เป็นเพียงการกำหนดค่า คุณไม่จำเป็นต้องทำอะไรกับโค้ดที่ชาญฉลาด (จริงแล้วย้อนกลับไปในสมัยก่อนคุณอาจต้องสร้างคลาส Formatter ของคุณเองเพื่อทำให้ถูกต้อง)
"ผมใช้ห้องสมุดอื่น ๆ ที่ยังใช้การบันทึกกรอบ-X ดังนั้นฉันคิดว่ามันง่ายขึ้นเพียงเพื่อการใช้งานที่หนึ่ง" นี่คือการโต้แย้งแบบวงกลมใช่ไหม? เหตุใด 'ทุกคน' จึงใช้การบันทึกเฟรมเวิร์ก X และไม่ใช่ JUL
"ทุกคนอื่นใช้การบันทึกกรอบ-X" สำหรับฉันนี่เป็นกรณีพิเศษที่กล่าวมา ส่วนใหญ่ไม่ถูกต้องเสมอไป
ดังนั้นคำถามใหญ่ที่แท้จริงคือทำไมไม่ JUL . ฉันพลาดอะไรไป raison d'êtreสำหรับการบันทึกหน้าไม้ (SLF4J, JCL) คือการใช้งานการบันทึกหลายครั้งมีอยู่ในอดีตและเหตุผลที่ย้อนกลับไปสู่ยุคก่อน JUL อย่างที่เห็น ถ้า JUL นั้นสมบูรณ์แบบแล้วการบันทึกด้านหน้าจะไม่มีอยู่จริง ในการทำให้เรื่องที่สับสนมากขึ้น JUL นั้นจะต้องมีด้านหน้าเป็นส่วนหนึ่งซึ่งอนุญาตให้ตัวจัดการรูปแบบและแม้แต่ LogManager เปลี่ยนได้
แทนที่จะสวมกอดหลายวิธีในการทำสิ่งเดียวกัน (บันทึก) เราไม่ควรตั้งคำถามว่าทำไมพวกเขาจึงจำเป็นในตอนแรก? (และดูว่าเหตุผลเหล่านั้นยังคงมีอยู่)
ตกลงการวิจัยของฉันได้นำไปสู่บางสิ่งที่ฉันเห็นอาจเป็นปัญหาจริงกับ JUL:
การปฏิบัติ บางคนบอกว่าประสิทธิภาพใน SLF4J นั้นเหนือกว่าที่เหลือ นี่ดูเหมือนว่าฉันจะเป็นกรณีของการเพิ่มประสิทธิภาพก่อนวัยอันควร หากคุณต้องการบันทึกหลายร้อยเมกะไบต์ต่อวินาทีแล้วฉันไม่แน่ใจว่าคุณอยู่ในเส้นทางที่ถูกต้องแล้ว JUL พัฒนาขึ้นด้วยและการทดสอบที่คุณทำบน Java 1.4 อาจไม่เป็นจริงอีกต่อไป คุณสามารถอ่านได้ที่นี่และการแก้ไขนี้ทำให้มันเป็น Java 7 หลายคนยังพูดคุยเกี่ยวกับค่าใช้จ่ายของการต่อสตริงในวิธีการบันทึก อย่างไรก็ตามการบันทึกตามเทมเพลตหลีกเลี่ยงค่าใช้จ่ายนี้และมีอยู่ใน JUL ด้วย ส่วนตัวฉันไม่เคยเขียนบันทึกตามแม่แบบจริงๆ ขี้เกียจเกินไปสำหรับสิ่งนั้น เช่นถ้าฉันทำกับ JUL:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
IDE ของฉันจะเตือนฉันและขออนุญาตว่าควรเปลี่ยนเป็น:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. ซึ่งแน่นอนฉันจะยอมรับ ได้รับอนุญาต ! ขอขอบคุณสำหรับความช่วยเหลือของคุณ.
ดังนั้นฉันไม่ได้เขียนข้อความเหล่านี้ด้วยตัวเองที่ทำโดย IDE
โดยสรุปเกี่ยวกับปัญหาของการแสดงฉันไม่พบสิ่งใดที่จะแนะนำว่าผลการดำเนินงานของ JUL นั้นไม่โอเคเมื่อเทียบกับการแข่งขัน
กำหนดค่าจาก classpath J-Out-of-the-box JUL ไม่สามารถโหลดไฟล์การกำหนดค่าจาก classpath มันเป็นโค้ดสองสามบรรทัดเพื่อให้มันทำ ฉันเห็นว่าทำไมสิ่งนี้ถึงน่ารำคาญ แต่วิธีแก้ปัญหานั้นสั้นและง่าย
ความพร้อมของรถขนเอาท์พุท JUL มาพร้อมกับตัวจัดการเอาต์พุต 5 ตัวนอก: คอนโซลไฟล์สตรีมซ็อกเก็ตและหน่วยความจำ สามารถขยายได้หรือเขียนใหม่ได้ ตัวอย่างนี้อาจจะเขียนไปยัง UNIX / Linux Syslog และ Windows Event Log ฉันไม่เคยมีข้อกำหนดนี้มาก่อนและไม่เคยเห็นมันใช้มาก่อน แต่ฉันสามารถบอกได้ว่าทำไมมันถึงมีประโยชน์ Logback มาพร้อมกับ appender สำหรับ Syslog เป็นต้น ยังฉันจะเถียงว่า
- 99.5% ของความต้องการปลายทางปลายทางจะได้รับการคุ้มครองโดยสิ่งที่อยู่ใน JUL นอกกรอบ
- ความต้องการพิเศษสามารถรองรับโดยตัวจัดการแบบกำหนดเองที่ด้านบนของ JUL แทนที่จะเป็นด้านบน ไม่มีอะไรสำหรับฉันที่แนะนำว่าต้องใช้เวลาในการเขียนตัวจัดการเอาต์พุต Syslog สำหรับ JUL มากกว่าที่จะทำกับเฟรมเวิร์กการบันทึกอื่น
ฉันกังวลจริงๆว่ามีบางอย่างที่ฉันมองข้ามไป การใช้ซุ้มการบันทึกและการปรับใช้การบันทึกอื่นนอกเหนือจาก JUL นั้นแพร่หลายมากจนฉันต้องมาสรุปว่าเป็นฉันที่เพิ่งไม่เข้าใจ นั่นคงไม่ใช่ครั้งแรกที่ฉันกลัว :-)
ดังนั้นฉันควรทำอย่างไรกับ API ของฉัน ฉันต้องการให้มันประสบความสำเร็จ แน่นอนว่าฉันสามารถ "ไปตามกระแส" และใช้ SLF4J (ซึ่งดูเหมือนว่าจะเป็นที่นิยมมากที่สุดในวันนี้) แต่เพื่อประโยชน์ของตัวเองฉันยังต้องเข้าใจว่า JUL ในวันนี้มีอะไรผิดปกติหรือไม่? ฉันจะก่อวินาศกรรมตัวเองโดยเลือก JUL สำหรับห้องสมุดของฉัน?
การทดสอบประสิทธิภาพ
(ส่วนเพิ่มโดย nolan600 เมื่อวันที่ 07 -JUL-2012)
มีการอ้างอิงด้านล่างจาก Ceki เกี่ยวกับการ parametrization ของ SLF4J ซึ่งเร็วกว่า JUL 10 เท่าหรือมากกว่า ดังนั้นฉันจึงเริ่มทำการทดสอบง่ายๆ ได้อย่างรวดเร็วก่อนการเรียกร้องถูกต้องแน่นอน นี่คือผลการทดสอบเบื้องต้น (แต่อ่านต่อ!):
- เวลาดำเนินการ SLF4J แบ็กเอนด์แบ็กเอนด์: 1515
- เวลาดำเนินการ SLF4J แบ็กเอนด์ JUL: 12938
- เวลาดำเนินการ JUL: 16911
ตัวเลขข้างต้นเป็นมิลลิวินาทีจึงน้อยกว่าดีกว่า ดังนั้นความแตกต่างด้านประสิทธิภาพถึง 10 เท่าโดยตอนแรกก็ใกล้กัน ปฏิกิริยาเริ่มต้นของฉัน: นั่นเยอะมาก!
นี่คือแก่นของการทดสอบ ตามที่สามารถเห็นจำนวนเต็มและสตริงถูก จำกัด ในลูปซึ่งจะใช้ในคำสั่งบันทึก:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(ฉันต้องการให้คำสั่งบันทึกมีทั้งชนิดข้อมูลดั้งเดิม (ในกรณีนี้คือ int) และชนิดข้อมูลที่ซับซ้อนมากขึ้น (ในกรณีนี้คือสตริง) ไม่แน่ใจว่ามันสำคัญ แต่มีอยู่แล้วที่นั่น)
คำสั่งบันทึกสำหรับ SLF4J:
logger.info("Logging {} and {} ", i, someString);
คำสั่งบันทึกสำหรับ JUL:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
JVM คือ 'อุ่นเครื่อง' ด้วยการทดสอบเดียวกันดำเนินการหนึ่งครั้งก่อนที่การวัดจริงจะเสร็จสิ้น ใช้ Java 1.7.03 บน Windows 7 SLF4J (v1.6.6) รุ่นล่าสุดและใช้ Logback (v1.0.6) Stdout และ stderr ถูกเปลี่ยนเส้นทางไปยังอุปกรณ์ null
อย่างไรก็ตามอย่างระมัดระวังตอนนี้ปรากฎว่า JUL ใช้เวลาส่วนใหญ่ในการทำงานgetSourceClassName()
เนื่องจาก JUL โดยค่าเริ่มต้นจะพิมพ์ชื่อคลาสต้นทางในเอาต์พุตขณะที่ Logback ไม่ ดังนั้นเรากำลังเปรียบเทียบแอปเปิ้ลและส้ม ฉันต้องทำการทดสอบอีกครั้งและกำหนดค่าการใช้งานการบันทึกในลักษณะที่คล้ายกันเพื่อให้พวกเขาออกสิ่งเดียวกันจริง ๆ อย่างไรก็ตามฉันสงสัยว่า SLF4J + Logback จะยังคงอยู่ด้านบน แต่ไกลจากตัวเลขเริ่มต้นตามที่ระบุข้างต้น คอยติดตาม.
Btw: การทดสอบครั้งแรกที่ฉันได้ทำงานกับ SLF4J หรือ Logback เป็นประสบการณ์ที่น่ารื่นรมย์ JUL ต้อนรับอย่างอบอุ่นน้อยลงเมื่อคุณเริ่มต้น
ประสิทธิภาพการทดสอบ (ตอนที่ 2)
(ส่วนเพิ่มโดย nolan600 เมื่อวันที่ 08-JUL-2012)
ตามที่ปรากฎมันไม่สำคัญสำหรับประสิทธิภาพที่คุณกำหนดค่ารูปแบบของคุณใน JUL หรือไม่รวมถึงชื่อแหล่งที่มาหรือไม่ ฉันลองด้วยรูปแบบที่เรียบง่ายมาก:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
และนั่นไม่ได้เปลี่ยนการกำหนดเวลาข้างต้นเลย ผู้สร้างโปรไฟล์ของฉันเปิดเผยว่าตัวบันทึกยังคงใช้เวลาในการโทรถึงgetSourceClassName()
แม้ว่านี่ไม่ใช่ส่วนหนึ่งของรูปแบบของฉัน รูปแบบไม่สำคัญ
ฉันจึงสรุปประเด็นของประสิทธิภาพการทำงานว่าอย่างน้อยสำหรับคำสั่งบันทึกที่ใช้เทมเพลตที่ทดสอบแล้วดูเหมือนว่าจะมีปัจจัยประมาณ 10 ในความแตกต่างของประสิทธิภาพที่แท้จริงระหว่าง JUL (ช้า) และ SLF4J + Logback (ด่วน) เหมือนที่เซกิพูด
ฉันยังสามารถเห็นอีกสิ่งหนึ่งคือการgetLogger()
โทรของ SLF4J นั้นแพงกว่า ditto ของ JUL มาก (95 ms เทียบกับ 0.3 ms ถ้า profiler ของฉันถูกต้อง) มันสมเหตุสมผลแล้ว SLF4J ต้องทำบางครั้งในการเชื่อมโยงของการใช้งานการบันทึกพื้นฐาน มันไม่ทำให้ฉันกลัว การโทรเหล่านี้ควรจะค่อนข้างหายากในช่วงชีวิตของแอปพลิเคชัน ความคงทนควรอยู่ในการเรียกบันทึกจริง
ข้อสรุปสุดท้าย
(ส่วนเพิ่มโดย nolan600 เมื่อวันที่ 08-JUL-2012)
ขอบคุณสำหรับคำตอบทั้งหมดของคุณ ตรงกันข้ามกับสิ่งที่ฉันคิดในตอนแรกว่าฉันตัดสินใจใช้ SLF4J สำหรับ API ของฉัน สิ่งนี้ขึ้นอยู่กับหลาย ๆ สิ่งและการป้อนข้อมูลของคุณ:
มันให้ความยืดหยุ่นในการเลือกการใช้งานบันทึกเวลาใช้งาน
ปัญหาเกี่ยวกับการขาดความยืดหยุ่นในการกำหนดค่าของ JUL เมื่อทำงานภายในแอปพลิเคชันเซิร์ฟเวอร์
SLF4J นั้นเร็วกว่ามากโดยเฉพาะถ้าคุณจับคู่กับ Logback แม้ว่านี่เป็นเพียงการทดสอบคร่าวๆ แต่ฉันก็มีเหตุผลที่เชื่อได้ว่ามีความพยายามมากขึ้นในการปรับแต่ง SLF4J + Logback มากกว่า JUL
เอกสาร เอกสารสำหรับ SLF4J นั้นครอบคลุมและแม่นยำกว่ามาก
ความยืดหยุ่นของรูปแบบ ในขณะที่ฉันทำการทดสอบฉันได้กำหนดให้ JUL เลียนแบบรูปแบบเริ่มต้นจาก Logback รูปแบบนี้มีชื่อของเธรด ปรากฎว่า JUL ไม่สามารถทำสิ่งนี้ได้นอกกรอบ ตกลงฉันยังไม่ได้พลาดจนถึงตอนนี้ แต่ฉันไม่คิดว่ามันเป็นสิ่งที่ควรพลาดจากกรอบการทำงานของบันทึก ระยะเวลา!
โครงการ Java ส่วนใหญ่ (หรือหลายโครงการ) ในปัจจุบันใช้ Maven ดังนั้นการเพิ่มการพึ่งพาไม่ได้เป็นเรื่องใหญ่โดยเฉพาะอย่างยิ่งหากการพึ่งพานั้นค่อนข้างคงที่นั่นคือไม่เปลี่ยนแปลง API ตลอดเวลา นี่น่าจะเป็นจริงสำหรับ SLF4J นอกจากนี้โถ SLF4J และเพื่อน ๆ ยังมีขนาดเล็ก
ดังนั้นสิ่งที่แปลกประหลาดที่เกิดขึ้นก็คือฉันรู้สึกไม่พอใจกับ JUL หลังจากทำงานกับ SLF4J มาบ้าง ฉันยังคงเสียใจที่ต้องใช้วิธีนี้กับ JUL JUL ห่างไกลจากความสมบูรณ์แบบ แต่เป็นงานที่ทำ แค่ค่อนข้างไม่ดีพอ ตัวอย่างเดียวกันสามารถพูดเกี่ยวกับProperties
เป็นตัวอย่าง แต่เราไม่คิดเกี่ยวกับนามธรรมที่เพื่อให้ผู้คนสามารถเชื่อมต่อในไลบรารีการกำหนดค่าของตัวเองและสิ่งที่คุณมี ฉันคิดว่าเหตุผลนั้นProperties
มาจากแถบเหนือในขณะที่ตรงกันข้ามกับ JUL ของวันนี้ ... และในอดีตมันมาที่ศูนย์เพราะมันไม่ได้มีอยู่
java.lang.System.Logger
ซึ่งเป็นอินเตอร์เฟสซึ่งสามารถเปลี่ยนเส้นทางไปยังเฟรมเวิร์กการบันทึกที่แท้จริงที่คุณต้องการตราบใดที่เฟรมเวิร์กติดตั้งและให้การใช้อินเตอร์เฟสนั้น เมื่อรวมกับการทำให้เป็นโมดูลคุณสามารถปรับใช้แอ็พพลิเคชันด้วยบันเดิล JRE ที่ไม่มีอยู่java.util.logging
หากคุณต้องการเฟรมเวิร์กอื่น