การบันทึกแบบกำหนดพารามิเตอร์
จัดรูปแบบข้อความอย่างมีประสิทธิภาพ
การบันทึกแบบกำหนดพารามิเตอร์ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดจึงควรใช้การบันทึกแบบมีโครงสร้าง
- ส่วนหน้าของ SLF4J และตัวบันทึก
- การบันทึกแบบกำหนดพารามิเตอร์
- การกำหนดค่า Logback