Logger.getLogger (MyClass.class) เป็นวิธีที่ดีที่สุดในการเริ่มต้นตัวบันทึก log4j หรือไม่


13

บทช่วยสอน Mkyong นี้แสดงให้เห็นถึงตัวบันทึกที่ไม่น่าสนใจด้วยวิธีนี้:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

ตอนนี้สมมุติว่าคลาสอื่น ๆ ที่คุณใช้มีตัวบันทึกจะเริ่มต้นตัวบันทึกได้ในลักษณะเดียวกัน

คำถามของฉันคือ - นี่เป็นวิธีที่ดีที่สุดหรือไม่ ดูเหมือน ... ซ้ำแล้วซ้ำอีก


2
สิ่งใดที่คุณพบเกี่ยวกับเรื่องนี้อย่างละเอียด (ตั้งค่าไวยากรณ์ของ Java) คุณต้องสร้างตัวแปรเพื่อเก็บ logger และคุณต้องบอกgetLogger()ชื่อของ logger ให้ได้
kdgregory

2
@ kdgregory ความจริงที่ว่าฉันกำลังทำสิ่งเดียวกันในทุกชั้น
dwjohnston


3
เก่าเกินไปที่จะโยกย้าย แต่คำถามนี้อยู่นอกหัวข้อที่นี่และเหมาะสมกว่าสำหรับ StackOverflow
Andres F.

1
@AndresF ฉันคิดว่าฉันใส่ไว้ที่นี่เพราะมันมีคำถามเกี่ยวกับรูปแบบโค้ด / รูปแบบการออกแบบมากกว่าปัญหาทางเทคนิค
dwjohnston

คำตอบ:


16

ความคิดเห็นของคุณบอกว่า "verbose" หมายถึงความจำเป็นในการทำซ้ำบรรทัดของรหัสนี้ในทุกชั้น คำตอบแรกของฉันคือในภาพรวมการเพิ่มโค้ดสองบรรทัด (คำจำกัดความแปรปรวนพร้อมคำสั่งนำเข้า) ในทุกชั้นเรียนนั้นไม่ได้เป็นเรื่องใหญ่ โดยเฉพาะอย่างยิ่งเมื่อคุณต้องการเพิ่มลงในคลาสที่มีพฤติกรรมดังนั้นจึงต้องทำการบันทึก ที่กล่าวว่ารหัสบรรทัดที่คุณใช้นั้นมีแนวโน้มที่จะเกิดข้อผิดพลาดในการคัดลอกวาง (เพิ่มเติมในภายหลัง)

แต่เนื่องจากคุณต้องการทางเลือกนี่คือเหตุผลบางประการที่คุณอาจต้องการหรือไม่ต้องการใช้

ใช้ตัวบันทึกคนเดียวสำหรับทั้งแอพ

หากคุณไม่สนใจว่าคลาสใดที่กำลังรายงานหรือเต็มใจที่จะใส่บริบทที่จำเป็นทั้งหมดไว้ในข้อความกว่าคนตัดไม้ซิงเกิลธรรมดาจะทำงาน:

LoggerSingleton.getInstance().debug("MyController is running")

ในความคิดของฉันประโยชน์ที่สำคัญอย่างหนึ่งของกรอบการบันทึกคือการให้บริบทโดยอินสแตนซ์ของคนตัดไม้แยกต่างหาก - ถ้าเพียงเพื่อส่งข้อความบันทึกไปยังปลายทางที่แตกต่างกัน ฉันจะไม่ยอมแพ้เพียงเพื่อบันทึกโค้ดหนึ่งบรรทัด (คุณยังต้องนำเข้า)

นอกจากนี้ยังเพิ่มการใช้คำฟุ่มเฟื่อยในจุดที่ใช้ซึ่งจะจบลงด้วยการกดแป้นมากขึ้น

สร้างตัวบันทึกของคุณ ณ จุดใช้งาน

ฉันจะโยนอันนี้ออกเพียงเพราะมันกำจัดตัวแปร ฉันไม่คิดว่าฉันต้องการแสดงความคิดเห็น แม้ว่ามันจะแสดงเทคนิคที่ฉันต้องการสำหรับรับอินสแตนซ์คนตัดไม้

Logger.getLogger(getClass()).debug("blah blah blah");

ใช้ bean post-processor เพื่อฉีด logger

ตัวอย่างของคุณใช้ Spring และ Spring ให้คุณเชื่อมต่อกับรหัสการเริ่มต้นถั่ว คุณสามารถสร้างโพสต์โปรเซสเซอร์ที่ตรวจสอบ bean สำหรับloggerตัวแปรสมาชิกและสร้างLoggerอินสแตนซ์เมื่อพบหนึ่ง

ในขณะที่โพสต์โปรเซสเซอร์นั้นมีโค้ดเพียงไม่กี่โหล แต่เป็นอีกส่วนหนึ่งที่มีการเคลื่อนไหวในแอปพลิเคชันของคุณ ฉันชอบที่จะมีไม่กี่คนที่เป็นไปได้

ใช้มิกซ์อิน

Scala และ Groovy ให้คุณสมบัติซึ่งช่วยให้คุณสามารถห่อหุ้มพฤติกรรม รูปแบบสกาล่าทั่วไปคือการสร้างLoggingลักษณะแล้วเพิ่มลงในคลาสที่ต้องมีการบันทึก:

class MyController with Logging

น่าเสียดายนั่นหมายความว่าคุณต้องเปลี่ยนภาษา นอกจากว่าคุณใช้ Java 8 อยู่ในกรณีนี้คุณสามารถสร้างLoggingส่วนต่อประสานด้วย "default method":

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

ตอนนี้ในรหัสชั้นเรียนของคุณคุณสามารถใช้

getLogger().debug("blah blah blah");

ในขณะที่ง่ายสิ่งนี้มีข้อเสียสองประการ สำหรับสิ่งหนึ่งมันเป็นมลภาวะอินเตอร์เฟสของทุกคลาสที่ใช้เพราะวิธีการอินเตอร์เฟสทั้งหมดเป็นแบบสาธารณะ อาจไม่เลวถ้าคุณใช้เฉพาะคลาสที่อินสแตนซ์และฉีดโดย Spring โดยเฉพาะถ้าคุณทำตามการแยกอินเทอร์เฟซ / การใช้งาน

ปัญหาที่ใหญ่กว่าคือการค้นหาอินสแตนซ์ตัวบันทึกจริงในการโทรทุกครั้ง ซึ่งเร็ว แต่ไม่จำเป็น

และคุณยังต้องการใบแจ้งยอดการนำเข้า

ย้ายตัวบันทึกไปที่ซูเปอร์คลาส

ฉันจะทำซ้ำ: ฉันไม่พบคำจำกัดความตัวทำซ้ำที่ชัดเจน แต่ถ้าคุณทำฉันคิดว่านี่เป็นวิธีที่ดีที่สุดในการกำจัดพวกเขา

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

ตอนนี้คลาสคอนโทรลเลอร์ของคุณสืบทอดมาAbstractControllerแล้วและพวกเขาก็สามารถเข้าถึงloggerตัวแปรได้ จำไว้ว่าคุณต้องใส่@Controllerคำอธิบายประกอบลงในคลาสที่เป็นรูปธรรม

บางคนจะพบว่าสิ่งนี้เป็นการบิดเบือนมรดก ผมได้พยายามที่จะระงับโทสะพวกเขาโดยการตั้งชื่อชั้นเรียนมากกว่าAbstractController AbstractProjectClassคุณสามารถตัดสินใจด้วยตัวเองหรือไม่ว่ามีความเป็นแบบความสัมพันธ์

คนอื่นจะคัดค้านการใช้ตัวแปรอินสแตนซ์แทนที่จะเป็นตัวแปรสแตติก ตัวบันทึกแบบคงที่IMOมีแนวโน้มที่จะเกิดข้อผิดพลาดในการคัดลอกวางเนื่องจากคุณต้องอ้างอิงชื่อคลาสอย่างชัดเจน getClass()ตรวจสอบให้แน่ใจว่าคนตัดไม้ของคุณถูกต้องเสมอ


ฉันคิดว่าคุณควรระวังเกี่ยวกับ getClass () ในคลาสที่สืบทอด, MethodHandles.lookup (). lookupClass () ดีกว่าหลังจาก jdk 7
yuxh

@luxh - คุณมีคำอธิบายเกี่ยวกับเรื่องนี้หรือไม่?
kdgregory

ตรวจสอบที่นี่: stackoverflow.com/a/6653577/4652536
yuxh

@yuxh - การอ้างถึงเพียงวิธีเดียวของ MethodHandles ในคำถามนั้น (ซึ่งไม่ได้อยู่ในคำตอบที่คุณเชื่อมโยง) เป็นการยืนยันที่ไม่ได้รับการสนับสนุนที่คล้ายกันพร้อมกับความคิดเห็นที่ขอความกระจ่าง คุณมีการสนับสนุนที่เชื่อถือได้สำหรับการยืนยันที่MethodHandles.lookup().lookupClass()ดีกว่าObject.getClass()หรือไม่
kdgregory

MethodHandles.lookup().lookupClass()สามารถใช้สำหรับตัวแปรแบบคงที่, ไม่น่าจะเกิดข้อผิดพลาดในการคัดลอกและวางอย่างรวดเร็ว: stackoverflow.com/a/47112323/898747มันหมายถึงการเพิ่มพอร์ต แต่ฉันค่อนข้างชอบ loggers ของฉันเมื่ออยู่ในรูปแบบคงที่อย่างน้อยก็คุ้มค่า พูดถึง :)
FableBlaze

0

หากต้องการขยายคำตอบที่ให้โดย @kdgregory Groovy จะจัดเตรียม@Slf4j( groovy.util.logging.Slf4j) ออกจากกล่องเพื่อเป็นการเพิ่มความคิดเห็นที่ดำเนินการแปลง AST ในชั้นเรียนเพื่อแก้ปัญหาด้วยตัวบันทึกซึ่งถือว่าชื่อตัวแปรเป็นlogค่าเริ่มต้นหากไม่ได้ระบุไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.