0Pricing
Java Academy · บทเรียน

การบันทึกแบบกำหนดพารามิเตอร์

จัดรูปแบบข้อความอย่างมีประสิทธิภาพ

การบันทึกแบบกำหนดพารามิเตอร์ เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

ปัญหาของการต่อสตริง

การต่อข้อความล็อกจะสร้างสตริงทั้งหมด ก่อน การเรียกใช้ แม้ว่าระดับนั้นจะปิดใช้งานอยู่ก็ตาม

log.debug("id=" + id + " name=" + name) ทำให้เสียการประมวลผลของ CPU เมื่อ DEBUG ปิดอยู่ เพราะสตริงจะถูกสร้างขึ้นไม่ว่าอย่างไรก็ตาม

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        String name = "Ada"; int id = 7;
        // Bad: string is built even if DEBUG is disabled
        log.debug("id=" + id + " name=" + name);
    }
}

ตัวยึดตำแหน่งด้วย {}

SLF4J ใช้ {} เป็นตัวยึดตำแหน่ง ให้ส่งค่าเป็นอาร์กิวเมนต์เพิ่มเติม

ข้อความจะถูกประกอบขึ้นเฉพาะ เมื่อระดับนั้นเปิดใช้งาน ดังนั้นล็อกที่ปิดใช้งานจึงแทบไม่ใช้ทรัพยากร

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        String name = "Ada"; int id = 7;
        // Good: no string built unless DEBUG is on
        log.debug("id={} name={}", id, name);
    }
}

ตัวยึดตำแหน่งหลายตัว

ตัวยึดตำแหน่งจะถูกเติมค่า ตามลำดับ {} ตัวแรกจะรับอาร์กิวเมนต์ตัวแรก ตัวที่สองจะรับตัวที่สอง และเป็นเช่นนี้ต่อไป

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("order {} for user {} totaling {} cents", "A-1", 42, 1599);
    }
}

เหตุผลที่มีประสิทธิภาพ

เฟรมเวิร์กจะตรวจสอบระดับก่อน หากปิดใช้งานอยู่ เฟรมเวิร์กจะส่งคืนทันทีโดยไม่เรียก toString() กับอาร์กิวเมนต์ใด ๆ

ส่วนที่ใช้ทรัพยากรมากอย่างการจัดรูปแบบจะเกิดขึ้นแบบล่าช้า เฉพาะเมื่อข้อความจะถูกส่งออกจริงเท่านั้น

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    static String describe(Object o) { System.out.println("toString called"); return o.toString(); }
    public static void main(String[] args) {
        // Note: passing describe(...) still evaluates eagerly; pass the raw object instead
        log.trace("value={}", new int[]{1, 2, 3});
    }
}

ส่งออบเจ็กต์ดิบ ไม่ใช่สตริงที่จัดรูปแบบไว้ล่วงหน้า

เพื่อให้ได้ประโยชน์จากการทำงานแบบล่าช้า ให้ส่ง ออบเจ็กต์ดิบ ไม่ใช่การเรียกเมธอดที่จัดรูปแบบออบเจ็กต์นั้น

หากคุณเขียน log.debug("{}", buildReport()) buildReport() จะยังคงทำงานทันที ให้ส่งอาร์กิวเมนต์ที่ตัวบันทึกสามารถจัดรูปแบบได้เอง

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        Object user = new Object();
        // Logger calls user.toString() only if DEBUG is enabled
        log.debug("current user: {}", user);
    }
}

ซัพพลายเออร์แบบล่าช้าสำหรับงานที่ใช้ทรัพยากรมาก

เมื่อการจัดรูปแบบใช้ทรัพยากรมากจริง ๆ ให้ใช้ ส่วนติดต่อแบบต่อเนื่องของ SLF4J 2.x ร่วมกับ Supplier เพื่อเลื่อนการทำงานออกไป

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    static String heavyReport() { return "...expensive..."; }
    public static void main(String[] args) {
        log.atDebug()
           .setMessage("report: {}")
           .addArgument(() -> heavyReport())
           .log();
    }
}

การหลีกอักขระวงเล็บปีกกาตามตัวอักษร

หากต้องการใส่ {} ตามตัวอักษรในข้อความ ให้หลีกอักขระด้วยแบ็กสแลช: \\{}

กรณีนี้พบไม่บ่อย แต่มีประโยชน์เมื่อบันทึกข้อความลักษณะคล้าย JSON

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        // Logs a literal {} then the value
        log.info("empty object is \\{} and id is {}", 7);
    }
}

ข้อยกเว้นพร้อมตัวยึดตำแหน่ง

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        int id = 99;
        try {
            throw new RuntimeException("db down");
        } catch (Exception e) {
            log.error("failed processing record {}", id, e);
        }
    }
}

หลีกเลี่ยงอาร์กิวเมนต์จำนวนมากเกินไป

หากจำนวนตัวยึดตำแหน่งไม่ตรงกับจำนวนอาร์กิวเมนต์ SLF4J จะพยายามทำให้ดีที่สุด แต่ผลลัพธ์อาจดูไม่ถูกต้อง

ให้จำนวนทั้งสองสอดคล้องกัน หากมีค่ามากกว่าสองสามค่า ให้พิจารณาใช้บริบทแบบมีโครงสร้าง (MDC) แทน

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        // Two placeholders, two arguments: correct
        log.info("user {} did {}", "ada", "login");
    }
}

การเพิ่มบริบทด้วย MDC

สำหรับค่าที่ใช้ร่วมกันในหลายบรรทัด เช่น รหัสคำขอ ให้ใส่ค่าเหล่านั้นใน MDC แทนการใส่ไว้ในทุกข้อความ

จากนั้นรูปแบบการจัดวางจะพิมพ์ค่า MDC ในทุกบรรทัดให้โดยอัตโนมัติ

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        MDC.put("requestId", "req-1234");
        try {
            log.info("handling request");
            log.info("request done");
        } finally {
            MDC.clear();
        }
    }
}

สรุปแนวทางปฏิบัติที่ดี

กฎสำหรับการบันทึกล็อกอย่างมีประสิทธิภาพ:

  • ใช้ตัวยึดตำแหน่ง {} เสมอ และอย่าใช้การต่อด้วย +
  • ส่งออบเจ็กต์ดิบ ไม่ใช่สตริงที่สร้างไว้ล่วงหน้า
  • ใช้ซัพพลายเออร์หรือ isXEnabled() สำหรับงานที่ใช้ทรัพยากรมากจริง ๆ
  • ใช้ MDC สำหรับบริบทที่เกี่ยวข้องกับหลายส่วนของระบบ
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("processed {} items in {} ms", 1000, 42);
    }
}

ตรวจสอบความเข้าใจ

ทดสอบความรู้เกี่ยวกับการบันทึกล็อกแบบมีพารามิเตอร์

ทบทวน

คุณได้เรียนรู้การบันทึกล็อกแบบมีพารามิเตอร์:

  • ใช้ตัวยึดตำแหน่ง {} ร่วมกับอาร์กิวเมนต์เพิ่มเติม
  • ข้อความจะถูกสร้างแบบ ล่าช้า เฉพาะเมื่อเปิดระดับนั้นอยู่
  • ส่งออบเจ็กต์ดิบ และใช้ซัพพลายเออร์สำหรับงานที่ใช้ทรัพยากรมาก
  • ใช้ MDC สำหรับค่าบริบทที่ใช้ร่วมกัน

ถัดไปคือการกำหนดค่าระบบเบื้องหลัง Logback

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("Parameterized logging recap: {}", "done");
    }
}

คำถามที่พบบ่อย

บทเรียน “การบันทึกแบบกำหนดพารามิเตอร์” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การบันทึกแบบกำหนดพารามิเตอร์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การบันทึกแบบกำหนดพารามิเตอร์”

จัดรูปแบบข้อความอย่างมีประสิทธิภาพ คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การบันทึกแบบกำหนดพารามิเตอร์” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุใดจึงควรใช้การบันทึกแบบมีโครงสร้าง
  2. ส่วนหน้าของ SLF4J และตัวบันทึก
  3. การบันทึกแบบกำหนดพารามิเตอร์
  4. การกำหนดค่า Logback
← กลับไปที่ Java Academy