التسجيل المعلَّم بالمعلمات
تنسيق فعّال للرسائل
التسجيل المعلَّم بالمعلمات درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
مشكلة دمج السلاسل النصية
يؤدي دمج رسالة السجل إلى إنشاء السلسلة النصية كاملةً قبل الاستدعاء، حتى إذا كان المستوى معطّلًا.
يهدر log.debug("id=" + id + " name=" + name) موارد وحدة المعالجة المركزية عندما يكون 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);
}
}المزوّدات الكسولة للعمل الثقيل
عندما يكون التنسيق مكلفًا فعلًا، استخدموا واجهة fluent API في 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");
}
}الأسئلة الشائعة
هل درس «التسجيل المعلَّم بالمعلمات» مجاني؟
نعم — نص درس «التسجيل المعلَّم بالمعلمات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «التسجيل المعلَّم بالمعلمات»؟
تنسيق فعّال للرسائل تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «التسجيل المعلَّم بالمعلمات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- لماذا التسجيل المنظّم
- واجهة SLF4J والمسجلات
- التسجيل المعلَّم بالمعلمات
- تهيئة Logback