مرحبًا بكم في دليل مثال Apache Log4j2. إذا سألت مطورًا خبيرًا عن أكثر الأشياء إزعاجًا في التطبيق، فقد يكون الجواب متعلقًا بالتسجيلات. إذا لم يكن هناك تسجيل مناسب في التطبيق، ستكون الصيانة كابوسًا. يمر معظم التطبيقات بمراحل اختبار التطوير، واختبار الوحدات، واختبار الدمج. ولكن عندما يتعلق الأمر بالإنتاج، ستواجه دائمًا سيناريوهات فريدة واستثنائية. لذا، الطريقة الوحيدة لمعرفة ما حدث في حالة معينة هي تتبع التسجيلات. توفر العديد من الأطر الأساسية طريقة للتسجيل الافتراضي، لكن الأفضل دائمًا الاعتماد على آلية تسجيل قياسية في الصناعة. Apache Log4j هو واحد من أشهر الأطر لتسجيل الحوادث. وApache Log4j 2 هو الإصدار التالي، الذي يعتبر أفضل بكثير من Log4j.
دليل مثال Log4j
في هذا الدليل المثالي لـ Log4j2، ستتعلم كيفية البدء مع Apache Log4j2. كما سنستكشف أيضًا بنية Log4j2، تكوين log4j2، مستويات تسجيل log4j2، وملحقات التسجيل والمرشحات والمزيد.
- نظرة عامة على Log4j2
- بنية Log4j2
- تكوين Log4j2
- مستويات Log4j2
- بحث Log4j2
- ملحقات Log4j2
- مرشحات Log4j2
- تخطيطات Log4j2
- أي مستوى Log4j2 يجب أن تستخدمه
- ملخص درس Log4j2
نظرة عامة على Log4j2
استخدام واجهة برمجة التطبيقات للتسجيل في التطبيق ليس رفاهية، بل ضرورة لا غنى عنها. Log4j هو مكتبة مفتوحة المصدر تم نشرها وترخيصها بموجب برمجيات أباتشي. يمكنك تصحيح تطبيق باستخدام تصحيح Eclipse أو أدوات أخرى، ولكن هذا ليس كافيًا وغير عملي في بيئة الإنتاج. ستوفر لك آلية التسجيل العديد من الفوائد التي لن تجدها في التصحيح العادي.
Category / Operation (Debugging, Logging) | Debugging | Logging |
---|---|---|
Human Intervention | There’s a need for human intervention | No need for human intervention |
Persistent Medium | Can’t be integrated with persistent storage | Can be integrated with persistent storage (Files, Database, NoSQL database, etc.) |
May used for Auditing | Can’t be used for achieving auditing | Can be used for achieving auditing if it’s used efficiently |
Sufficient for complicated structure and flow | Not sufficient; you may get lost with flow. | Sufficient |
Productivity | Less productive | More productive |
كما يمكنك رؤية أعلاه، سيكون استخدام آلية التسجيل أكثر كفاءة مع تكلفة صيانة أقل. Apache Log4j هو الأداة الرائدة لتسجيل التطبيقات في لغة Java، لذا يجب عليك استخدامه.
هندسة معمارية Log4j2
قبل أن نبدأ في تعليمة مثال Log4j، من الجيد أن نلقي نظرة على الهندسة المعمارية لـ Log4j2. الصورة أدناه توضح الفئات المهمة في واجهة برمجة التطبيقات Log4j2. فيما يلي الشرح المفصل للهندسة المعمارية الموضحة أعلاه:
-
سيطلب التطبيقات من
LogManager
مُحددًاLogger
باسم محدد. -
LogManager
سيقوم بتحديدLoggerContext
المناسبة ثم الحصول علىLogger
منها. -
إذا لم يتم إنشاء المسجل بعد ، سيتم إنشاؤه وربطه بـتكوين المسجل وفقًا لثلاث خيارات أدناه:
- سيتم إنشاء مثيل المسجل وربطه بـ تكوين المسجل الذي يحمل نفس الاسم. على سبيل المثال ، سيتم تقييم App.class في
getLogger(App.class)
ليكون سلسلة نصيةcom.journaldev.App
. اسم تكوين المسجل متطابق مع اسم الفئة الكامل المؤهل (مكون البرمجيات). - سيتم إنشاء مثيل المسجل وربطه بـ تكوين المسجل الذي ينتمي إلى نفس حزمة المسجلات الأم. على سبيل المثال
com.journaldev
فيgetLogger("com.journaldev")
- سيتم إنشاء مثيل المسجل وربطه بـ تكوين المسجل الجذري. سيتم استخدام تكوين المسجل الجذري عند عدم وجود ملف تكوين أو عند الحصول على مسجل باسم غير محدد في تعريفات المسجل.
- سيتم إنشاء مثيل المسجل وربطه بـ تكوين المسجل الذي يحمل نفس الاسم. على سبيل المثال ، سيتم تقييم App.class في
-
تتم إنشاء كائنات
LoggerConfig
من تصريح المسجل في ملف التكوين. يتم استخدام LoggerConfig أيضًا للتعامل معLogEvents
وتفويضها لمُرسِلي الـ Log4j2 Appenders المُحددة لها. -
المسجل الجذر هو حالة استثنائية، من حيث وجوده. فهو دائمًا موجود وفي القمة لأي تسلسل مسجل.
-
يمكنك الحصول على المسجل الجذر باستخدام البيانات التالية:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); Logger logger = LogManager.getRootLogger();
-
أسماء مسجلات log4j2 حساسة لحالة الأحرف.
- باستثناء المسجل الجذر، يمكن الحص
-
سياق السجل هو نقطة صوتية لنظام السجل كما يمكن أن يكون لديك عدة سياقات للسجل داخل تطبيقك. يجب تعيين تكوين نشط لكل سياق سجل.
-
تحتوي تكوين Log4j2 على جميع أصول نظام السجل؛ LoggerConfig(s)، Appender(s)، Filter(s) وغيرها الكثير.
-
سيتم دائمًا إرجاع المرجع لنفس مثيل سجل معين عند استدعاء LogManager.getLogger() من خلال تمرير نفس الاسم.
-
يتم عادةً تكوين نظام السجل خلال تهيئة التطبيق. يمكن أن يكون هذا بأشكال مختلفة؛ برمجيًا أو عن طريق قراءة ملف تكوين log4j2.
كل سجل مرتبط بكائن LoggerConfig ، ومجموعة من كائنات LoggerConfig تشكل تسلسلًا للسجلات. يُعرف هذا المفهوم باسم تسلسل السجل. تسلسل السجل مكون من مجموعة من كائنات LoggerConfig مع علاقة أب وابن. العنصر الأعلى في كل تسلسل للسجلات هو سجل الجذر. إذا لم يجد Log4j2 ملف التكوين ، فسيتم استخدام سجل الجذر فقط لتسجيل الأخطاء مع مستوى تسجيل الأخطاء. تُظهر الصورة أدناه رسالة تحذير ستحصل عليها في هذه الحالة. Error StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. الجدول أدناه يوضح العلاقة بين الأب والابن في تسلسل السجلات.
LoggerConfig (Is A) | Root | com | com.journaldev | com.journaldev.logging |
---|---|---|---|---|
Root | X | Child | descendant | descendant |
com | Parent | X | Child | descendant |
com.journaldev | Ancestor | Parent | X | Child |
com.journaldev.logging | Ancestor | Ancestor | Parent | X |
لتوضيح العلاقة بين الأب والابن ، يجب قراءة الجدول أعلاه على النحو التالي:
- الجذر هو أب لـ com.
- الجذر هو جد لـ com.journaldev.
- الجذر هو جد لـ com.journaldev.logging.
- com هو ابن للجذر.
- com هو أب لـ com.journaldev.
- com هو جد لـ com.journaldev.logging.
- com.journaldev.logging هو ابن لـ com.journaldev وهكذا.
تُعتبر مثيلًا لـ LoggerConfig أجدادًا لآخر إذا كان اسمه متبوعًا بنقطة بادئة لاسم الابن. تُعتبر مثيلًا لـ LoggerConfig والدًا لآخر إذا لم يكن هناك أسماء متداخلة بين كلاهما.
تكوين Log4j2
هناك العديد من الطرق لاستخدام تكوين Log4j2 في تطبيقك.
- من خلال ملف تكوين مكتوب بتنسيق XML أو JSON أو YAML أو ملف خصائص.
- برمجيًا، عن طريق إنشاء مصنع تكوين وتنفيذ تكوين.
- برمجيًا، عن طريق استدعاء واجهة تكوين المكشوفة في واجهة التكوين.
- برمجيًا، عن طريق استدعاء الطرق على فئة السجل الداخلية.
سنركز بشكل أساسي على ملف التكوين. ومع ذلك، فمن الجيد معرفة النهج البرمجي أيضًا، في حال كنت ترغب في تكوين استراتيجية تسجيل معينة لبعض السجلات النمطية. أولًا وقبل كل شيء، دعونا ننظر في الحالة التي لم تقدم فيها ملف تكوين. يفترض تطبيق Log4j2 أن يكون هناك متغير نظام يُسمى log4j.configurationFile ليشير إلى موقع ملف تكوين log4j2.
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
public static void main( String[] args ) {
Logger logger = LogManager.getRootLogger();
logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
}
}
A simple log4j2 configuration file will look like below. configuration.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
وإليك شرح مفصل للشفرة المذكورة أعلاه:
- قام التطبيق بالإشارة إلى سجل الجذر عن طريق استدعاء طريقة LogManager
getRootLogger
. - بدأت إشارة المسجل من LogManager في تشغيل نظام Log4j.
- سيقوم Log4j بتفتيش خاصية النظام log4j.configurationFile لتحديد ملف تكوين log4j2. يمكن كتابة تكوين Log4j بتنسيق JSON و YAML و XML. يمكننا تعيين خاصية النظام log4j.configurationFile عبر
System.setProperties("log4j.configurationFile","FILE_PATH")
أو عن طريق تمريرها كمعلمة للآلة الظاهرة في الشكل أدناه. لاحظ أيضًا بادئة بروتوكول الملف.
- في حال عدم تعريف خاصية النظام، يأخذ ترتيب التكوين الأولوية التالية:
- سيبحث Property ConfigurationFactory عن
log4j2-test.properties
في مسار الفئة. - سيبحث YAML ConfigurationFactory عن log4j2-test.yaml أو log4j2-test.yml في مسار الفئة.
- سيبحث JSON ConfigurationFactory عن log4j2-test.jsn أو log4j2-test.json في مسار الفئة.
- سيبحث XML ConfigurationFactory عن log4j2-test.xml في مسار الفئة.
- سيبحث Property ConfigurationFactory عن
log4j2.properties
في مسار الفئة - سيبحث YAML ConfigurationFactory عن log4j2.yml أو log4j2.yaml في مسار الفئة.
- سيبحث JSON ConfigurationFactory عن log4j2.jsn أو log4j2.json في مسار الفئة.
- سيبحث XML ConfigurationFactory عن log4j2.xml في مسار الفئة.
- إذا لم يتم توفير ملف تكوين، سيتم استخدام DefaultConfiguration وسيؤدي ذلك إلى مجموعة من السلوكيات الافتراضية:
- سيتم استخدام جهاز تسجيل الجذر.
- سيتم تعيين مستوى جهاز تسجيل الجذر إلى ERROR.
- سيقوم جهاز تسجيل الجذر بنقل رسائل التسجيل إلى وحدة التحكم.
- سيتم تعيين PatternLayout ليكون
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
- سيبحث Property ConfigurationFactory عن
استخدام ملف تكوين log4j2 يجعل تكوين log4j2 بسيطًا للغاية، ولكن دعنا نرى كيف يمكننا تكوينه بشكل برمجي. هذا كل شيء عن استخدام ConfigurationFactory.
package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
public static void main( String[] args ) throws FileNotFoundException, IOException {
// احصل على مثيل لمصنع التكوين؛ الخيارات المتاحة لديك هي ConfigurationFactory الافتراضية، XMLConfigurationFactory، YamlConfigurationFactory و JsonConfigurationFactory
// حدد مصدر هذا التكوين، يحتوي الملف الموجود على مكون غير حقيقي يحتوي فقط على علامة تكوين فارغة
ConfigurationFactory factory = XmlConfigurationFactory.getInstance();
// احصل على مرجع من التكوين
ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
// قم بإنشاء مدخل تطبيق وحدة التحكم الافتراضي
Configuration configuration = factory.getConfiguration(configurationSource);
// أضف مدخل تطبيق وحدة التحكم إلى التكوين
ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
// قم بإنشاء LoggerConfig
configuration.addAppender(appender);
// أضف مدخل التطبيق
LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
// أضف سجل وقم بربطه بمثيل LoggerConfig
loggerConfig.addAppender(appender,null,null);
// احصل على مثيل السياق
configuration.addLogger("com", loggerConfig);
// ابدأ نظام تسجيل الأحداث
LoggerContext context = new LoggerContext("JournalDevLoggerContext");
// احصل على مرجع للمسجل
context.start(configuration);
// حدث تسجيل DEBUG
Logger logger = context.getLogger("com");
// حدث تسجيل لرسالة الخطأ للمسجل المكون على أنه FATAL
logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
// حدث تسجيل لرسالة الخطأ التي ستتم معالجتها بواسطة الجذر
logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
يمكنك استخدام أي من ConfigurationFactory المقدمة من قبل Log4j2 أو استخدام الافتراضي.
logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
}
}
- لقد استخدمنا
XMLConfigurationFactory
للحصول على مثيل من ConfigurationFactory. - سيقدم لك المصنع مثيلًا من المرجع المطلوب للتكوين عن طريق تمرير ملف التكوين المقابل.
- سيتم استخدام مثيل التكوين بالتزامن مع LoggerContext لبدء نظام تسجيل الأحداث.
- A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
- تم إنشاء مثيل LoggerConfig باستخدام الاسم والمستوى المقدمين ودون استخدام مرشح. سيتم تعيين مدخل السجل الذي تم إنشاؤه لهذا المثيل من LoggerConfig.
- تمت إضافة مثيل LoggerConfig إلى مثيل التكوين.
- A new instance of LoggerContext is created with defined name.
- تم تمرير مثيل التكوين لمثيل LoggerContext وتم استدعاء start على الأخير.
- A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
- تم إطلاق ثلاثة أحداث من مثيل Logger التي سيتم شرحها في قسم مستويات Log4j2.
- تم تكوين مسجل com لطباعة رسائل تحمل مستويات FATAL.
- بشكل افتراضي، يتم تكوين مسجل Root لطباعة رسائل تحمل مستوى ERROR.
- لن يتم تسجيل رسائل ERROR بواسطة مسجل ‘com’ لأن مستواها هو FATAL.
يمكن تكوين نفس التكوين عن طريق استخدام ملف YAML أو JSON أو الخصائص. ومع ذلك، فإن تكوين ملف خصائص log4j2 مختلف عن ملف خصائص log4j، لذلك تأكد من عدم محاولة استخدام تكوين ملف خصائص log4j مع log4j2. سيتم إلقاء الخطأ التالي؛
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
أثناء معالجة الكود أعلاه، سيتم إعطاءك الناتج التالي:
Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages
السطر الأول من السجلات هو من مسجل com والثاني من مسجل Root. رسالة خطأ مسجل com لم يتم طباعتها لأن مستواها هو Fatal.
مستويات Log4j2
يمكنك رؤية في الأمثلة البرمجية أعلاه أنه في كل مرة نقوم فيها بتعريف LoggerConfig ، نقدم أيضًا مستوى التسجيل. بشكل افتراضي ، يكون التسجيل في log4j2 جمعيًا. يعني ذلك أن جميع المسجلات الأم سيتم استخدامها أيضًا عند استخدام مسجل محدد. توضح الصورة أدناه هذا الوضع. وهنا نقاط توضيح لذلك:
- كما ذكرنا سابقًا ، لكل مسجل يتم ربطه بمثيل LoggerConfig. تم تعريف هذا LoggerConfig في نطاق التكوين.
- يمكن تحديد مستوى التسجيل في نطاق LoggerConfig.
- يمكنك الحصول على المسجل بواسطة اسمه ، أو الحزمة الأصلية ، أو عن طريق توجيه المسجل الجذري نفسه.
- المسجل الجذري هو العقدة على مستوى أعلى لهرم كل تسلسل LoggerConfig.
- بمجرد الحصول على المسجل com.journaldev وبدء logEvent للتسجيل ، سيقوم loggerConfig (net.journaldev) بتسجيل الرسالة وسيتم نقل الرسالة أيضًا لأعلى في الهرم دون أي احترام لمستويات تسجيل الآباء. ستتم نقل حدث التسجيل إلى المسجلين com و Root وسيقومونا بتسجيل الرسالة على التوالي وفقًا للمستويات المحددة.
- بمجرد الحصول على المسجل com وبدء logEvent للتسجيل ، سيقوم loggerConfig (com) بتسجيل الرسالة وسيتم نقل الرسالة أيضًا لأعلى في الهرم دون أي احترام لمستويات تسجيل الآباء. وبمعنى آخر ، سيتم نقل حدث التسجيل إلى المسجل الجذري وسيقوم أيضًا بتسجيل الرسالة.
- نفس الحالة بالنسبة لتسلسل net.journaldev.
- الأقسام التالية، ستضيف مزيدًا من التوضيح لمفهوم الإضافة.
- هناك فرصة للوالدين بتجاهل الرسالة باستخدام مفهوم الفلتر أو عن طريق ضبط المؤشر الإضافي على القيمة الخاطئة، لذا لن يتم نقل أحداث السجل إلى الوالدين.
- هناك فرصة للمسجّل بتجاهل الرسالة إذا كان مستوى تكوين المسجّل الخاص به أكبر من مستوى أحداث السجل.
الآن، دعونا نرى المثال المرتبط بمفهوم الإضافة المشروح أعلاه:
import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
public static void main(String [] args){
new ComApp();
new ComJournalDevApp();
new NetApp();
new NetJournalDevApp();
}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
public ComJournalDevApp(){
Logger logger = LogManager.getLogger(ComJournalDevApp.class);
logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
public NetApp(){
Logger logger = LogManager.getLogger(NetApp.class);
logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
public NetJournalDevApp(){
Logger logger = LogManager.getLogger(NetJournalDevApp.class);
logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
}
}
بينما يبدو ملف تكوين log4j2 كما يلي:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
إذا قمت بتنفيذ الفئة الرئيسية ستجد النتائج التالية:
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
فيما يلي شرح مفصل للشيفرة المدرجة أعلاه:
- يحتوي ملف التكوين على خمس حالات تكوين مسجل محددة وهي الجذر، com، com.journaldev، net & net.journaldev. تمامًا كما هو موضح في التسلسل الهرمي للمسجلات أعلاه.
- تم تكوين مستوى الجذر ليكون ERROR وهذه هي القيمة الافتراضية فعلًا.
- تم تكوين مستويات com و com.journaldev لتكون TRACE.
- تم تكوين مستويات net و net.journaldev لتكون ERROR.
- قد لاحظت أن رسائل المسجلات ComAPP و ComJournalDevApp قد تم عرضها مرتين وثلاث مرات على التوالي. يتم عرض هذه الرسائل وفقًا لتسلسل المسجلات لـ ComApp & ComJournalDevApp حيث يكونون في حزم com & com.journalDev على التوالي. لدينا حالة مماثلة مع فئات NetApp & NetJournalDevApp.
- يتم نقل الوالدين كمؤشر إضافي مضبوط على القيمة الصحيحة بشكل افتراضي.
تأخذ مساحة التسجيل في الاعتبار مستويات الأحداث في السجل ومستوى loggerConfig بالإضافة إلى التسلسل الهرمي للسجل.
فماذا لو قمنا بتغيير LoggerConfig لـ com ليكون INFO وتركنا البرنامج كما هو:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="INFO">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
ثم سيكون النتيجة كما يلي:
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
- بالتأكيد قد لاحظت أن حدث السجل ComAPP تم تجاهله وذلك بسبب المستوى المحدد لـ loggerConfig لحزمة com. مستوى INFO (400) أقل من مستوى حدث السجل الذي هو هنا TRACE(600). لذلك، لن يتم عرض رسالة ComApp بعد الآن ولكي يتم عرضها، يجب عليك تعديل مستوى LoggerConfig لـ com ليكون TRACE(600) أو ALL(Integer.MAX_VALUE).
للتأكد من عرض حدث السجل، يجب أن يكون مستوى LoggerConfig أكبر من أو يساوي مستوى حدث السجل.
الجدول أدناه يظهر مستويات log4j2 والوزن الخاص بها:
LEVEL | Weight |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | Integer.MAX_VALUE |
بالطبع يوضح الجدول أعلاه أكثر من الكلمات ويعطيك السبب الرئيسي لعدم عرض حدث السجل TRACE أثناء كون مستوى LoggerConfig هو INFO.
لاحظ أن انتشار الأحداث في التسلسل الهرمي للسجل يتجاوز هذا الحساب ويتجاهل المستويات.
ولكن ماذا يحدث إذا قمنا بإزالة LoggerConfig لـ com.journaldev من التكوين وأضفنا واحدة جديدة لـ com.journaldev.logging لجعل ملف التكوين يبدو كما يلي:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
قد تجد الشكل أدناه أكثر ملاءمة لفهم ما حدث في تكوين log4j2 أعلاه. هنا بعض التوضيحات للشكل المعروض أعلاه وكيف يمكن أن يؤثر ذلك على سلوك أحداث تسجيل الدخول:
- عندما تقوم أحداث تسجيل الدخول بالإلقاء بواسطة Logger المسمى com.journaldev.logging، تم استخدام LoggerConfig المرتبط بهذا الاسم (أي com.journaldev.logging) للتعامل معه وطباعة الرسالة.
- بما أن السمة الإضافية لـ com.journaldev.logging LoggerConfig مضبوطة افتراضيًا على true، تم نقل حدث تسجيل الدخول للأب الذي يشير في هذه الحالة إلى com.journaldev.
- بما أن com.journaldev LoggerConfig غير معرف في التكوين، لا يحدث أي عمل وسيتم نقل حدث تسجيل الدخول إلى com ثم إلى Root LoggerConfig instances.
- سيتلقى Com و Root حدث تسجيل الدخول وطباعته بغض النظر عن المستوى الذي تم إرساله به.
نتيجة للنقاط المذكورة، سترى النتائج التالية:
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
وقد تلاحظ ما يلي:
- تم عرض حدث تسجيل الدخول في حزمة com مرتين. مرة واحدة لـ com والثانية لـ Root.
- تم عرض حدث السجل في com.journaldev مرتين. الأولى لـ com والثانية لـ Root. على الرغم من أنه كان ثلاث مرات من قبل، لكن الآن LoggerConfig لـ com.journaldev غير موجود ولذلك قد لا يكون هناك تسجيل في حزمة com.journaldev وسيتم نقل الحدث إلى com و Root.
- تم عرض حدث السجل في com.journaldev.logging ثلاث مرات، واحدة لحزمة com.journaldev.logging والثانية لـ com والثالثة لـ Root. وفقًا لانتشار تسلسل Logger Hierarchy، يجب عرضها أربع مرات، ولكن بسبب عدم وجود LoggerConfig لـ com.journaldev، يتم عرضها ثلاث مرات.
في حال قمت بتعريف مثيل LoggerConfig لـ com.journaldev دون تحديد مستوى، سيتم وراثة المستوى من والديها.
ولكن ماذا لو قمت بتعريف مثيل LoggerConfig لـ com.journaldev في ملف التكوين الخاص بك وتفويت تحديد مستوى LoggerConfig؟ لحسن الحظ، سيوفر لك مفهوم Logger Hierarchy هنا وسيورث com.journaldev قيمة مستواه من والديه. فيما يلي ملف تكوين عينة يتبعه جدول لمستوى تسجيل كل مثيل لتكوين السجل.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
Root | Root | ERROR | ERROR |
com | com | TRACE | TRACE |
com.journaldev | com | TRACE | TRACE |
com.journaldev.logging | com.journaldev.logging | TRACE | TRACE |
- تم ربط حزمة com.journaldev.logging بالفعل بـ مثيل LoggerConfig بمستوى تسجيل TRACE.
- تم ربط حزمة com.journaldev بمثيل LoggerConfig بدون تحديد مستوى تسجيل، لذا ستورث مستوى تسجيل والديها وبالتأكيد ستكون القيمة TRACE لحزمة com.
- تم ربط حزمة com بمثيل LoggerConfig بمستوى تسجيل TRACE بشكل افتراضي.
- بشكل افتراضي، يكون لـ Root مستوى تسجيل ERROR.
- في حالة عدم تعريف حزمة com، ستورث مثيل LoggerConfig لـ com.journaldev مستوى تسجيل Root.
أدناه نتيجة تنفيذ com.journaldev يرث com مستوى سجل:
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
وأدناه ستكون النتيجة إذا قمت بإزالة تعريف LoggerConfig لحزمة com:
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
قد تلاحظ أنه لم يتم تسجيل أي رسائل لـ com و com.journaldev، وأدناه الأسباب.
- سيؤدي حذف LoggerConfig المرتبط بحزمة com إلى تجاهل جميع أحداث السجل المذكورة في تلك الحزمة.
- نظرًا لعدم تحديد LoggerConfig المعرف لحزمة com في التكوين، سيورث LoggerConfig المرتبط بـ com.journaldev مستوى السجل من والديه. لم يتم تحديد Com، وتم الوصول إلى التسلسل الهرمي للسجل الرئيسي ويشير الآن إلى الجذر. مستوى السجل الجذر هو ERROR(200) ومستوى حدث السجل في com.journaldev هو TRACE(600) – انظر ComJournalDevApp – ووفقًا للمعادلة المحددة سابقًا، يجب أن يكون مستوى LoggerConfig أكبر من أو يساوي مستوى حدث السجل وهذا غير صحيح، لذلك لن يتم عرض أي رسائل هنا لـ com.journaldev.
وأخيرًا، يُظهر الجدول أدناه جميع السيناريوهات الممكنة لتسجيل الأحداث التي قد تواجهها عند استخدام نظام السجل:
X (N/A) | LoggerConfig Level | OFF(0) | FATAL(100) | ERROR(200) | WARN(300) | INFO(400) | DEBUG(500) | TRACE(600) | ALL(MAX) |
---|---|---|---|---|---|---|---|---|---|
Event Level | X | X | X | X | X | X | X | X | X |
OFF(0) | X | YES | NO | NO | NO | NO | NO | NO | NO |
FATAL(100) | X | NO | YES | YES | YES | YES | YES | YES | YES |
ERROR(200) | X | NO | NO | YES | YES | YES | YES | YES | YES |
WARN(300) | X | NO | NO | NO | YES | YES | YES | YES | YES |
INFO(400) | X | NO | NO | NO | NO | YES | YES | YES | YES |
DEBUG(500) | X | NO | NO | NO | NO | NO | YES | YES | YES |
TRACE(600) | X | NO | NO | NO | NO | NO | NO | YES | YES |
ALL(MAX) | X | NO | NO | NO | NO | NO | NO | NO | YES |
- لا يوجد طريقة مباشرة يمكن استخدامها لرمي أحداث سجل OFF/ALL.
- عمومًا، لرمي أحداث سجل OFF/ALL يمكنك استخدام logger.log(Level.OFF, “رسالة”) أو logger.log(LEVEL.ALL,”رسالة”) على التوالي.
- تتولى طريقة السجل معالجة حدث السجل وفقًا للمعادلة المذكورة.
تقول المعادلة التالي: إذا كان مستوى مستوى LoggerConfig أكبر من أو يساوي مستوى حدث السجل، فسيتم قبول الحدث لمعالجة إضافية.
سيتم قبول حدث السجل للمعالجة اللاحقة – هذا مهم جدًا لأن لديك القدرة على منع بعض الأحداث من القيام بالمعالجة حتى لو تم قبولها باستخدام مرشحات Log4j2. يمكنك ضبط الخاصية الإضافية على القيمة الباطلة لتجنب انتشار حدث السجل إلى مسجلات السجل الأصلية. وفيما يلي نفس المثال الذي رأيته من قبل ولكن هذه المرة مع سمة الإضافية، حتى تلاحظ الفرق.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
وسيكون نتيجة التنفيذ كما يلي:
17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
وقد تلاحظ عدم انتشار أحداث السجل إلى مسجلاتها الأصلية.
Log4j2 البحث
في الأفضلية، يمكنك تعريف البحث على أنه الطريقة التي يمكنك من خلالها تمرير القيم لملف تكوين السجل الخاص بك. يوفر Log4j2 مجموعة مختلفة من البحث التي يمكن استخدامها بشكل مستقل لتعيين القيم من سياقات مختلفة:
- البحث في خريطة السياق
- بحث التاريخ
- بحث البيئة
- بحث جافا
- بحث JNDI
- بحث مدخلات JVM (JMX)
- بحث الوسيطات الرئيسية
- بحث الخريطة
- بحث البيانات المنظمة
- بحث خصائص النظام
- بحث الويب
يمكنك الرجوع إلى وثائق Log4j2 للحصول على مزيد من التفاصيل حول كل نوع من أنواع البحث، ولكن دعونا نلقي نظرة على بعض الأمثلة هنا لتغطية أساسيات البحث في Log4j2. يمثل البحث عن البيئة الطريقة التي يمكنك من خلالها تمرير قيمة بيئية (سواء عن طريق ملف Linux etc/profile، أو بيئة نظام Windows، أو نصوص البدء التشغيلية للتطبيق). كما يعلم معظمنا، لدينا القدرة على تحديد مجموعة من القيم البيئية لاستخدام التطبيق. دعنا نرى الطرق الأكثر شهرة لتحديد المتغيرات البيئية الخاصة بك.
- تعريف متغير بيئي باستخدام مرفق بيئة Windows:
- انقر بزر الماوس الأيمن على أيقونة الكمبيوتر الخاصة بك واختر الخصائص. يجب عرض الصفحة الرئيسية للوحة التحكم.
- انقر فوق إعدادات النظام المتقدمة ثم افتح نافذة متغيرات البيئة.
- في قسم المتغيرات النظامية، قم بتعريف المتغير JournalDevVar بقيمة JournalDev.
- تحديث PatternLayout داخل ملف log4j2.xml الخاص بك ليحتوي على المتغير الذي تمت إضافته حديثًا.
- تحديد متغيرات البيئة الخاصة بك باستخدام ميزة النص البدء التشغيل.
- بدلاً من استخدام النص الافتراضي العادي، يمكنك استخدام ميزة تشغيل النص في بيئة Eclipse IDE، انقر على قائمة التشغيل الخاصة بك واختر تكوين التشغيل.
- انتقل إلى علامة Environment وحدد المتغير الخاص بك هناك.
انظر الآن إلى ملف log4j2.xml المعدل ولاحظ استخدام المتغيرات البيئية.
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
وسيكون نتيجة التنفيذ على النحو التالي:
23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
قد تواجه بعض المشكلات هنا وخاصة عند تعريف متغيرات بيئة نظام التشغيل وهو ذاكرة التخزين المؤقتة لبرنامج Eclipse. في ال理想، يكون لـ Eclipse قد قام بتخزين جميع المتغيرات النظامية عند تشغيله ويمكنك العثور على جميعها تحت القائمة – تكوين التشغيل – علامة التبويب البيئية – انقر فوق زر الاختيار. لذا، قد تكون مرتبكًا عندما تكون قد قمت بتعريفها ولكن التطبيق لا يعترف بها. حتى إذا قمت بإعادة تشغيل Eclipse، لن تحصل على الحل ولحله يجب عليك تنفيذ eclipse.exe -clean عند تثبيت Eclipse الخاص بك. للتأكد من تعريف متغيرات بيئتك بشكل صحيح وأن نظامك سيجدها بسهولة، يمكنك استخدام نوع مكون البرنامج المقابل الذي يتم توفيره بواسطة Log4j2 API. قم بإنشاء مثيل لـ EnvironmentLookup واسأله عن البحث في متغير معين وإذا كان معرفًا، فستجده بسهولة.
EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));
Log4j2 Appenders
قد رأيت سابقا كيف يمكن استخدام Lookups لحقن المتغيرات في ملف التكوين الخاص بك. على الرغم من ذلك، قد ترغب في تعديل الوسيلة التي تمر من خلالها رسائلك. بدلاً من استخدام وحدة التحكم مباشرةً، قد ترغب في استخدام مستودع ملف أو قاعدة بيانات للتأكد من أن رسائلك تُحتفظ بها بشكل دائم. قد قدم Log4j2 العديد من المرفقات، ويمكنك الرجوع إلى وثائق log4j2 للحصول على مزيد من التفاصيل حول المرفق. بشكل موجز، فيما يلي قائمة بجميع مرفقات Log4j2.
- ConsoleAppender
- AsyncAppender
- FailoverAppender
- FileAppender
- FlumeAppender
- JDBCAppender
- JMSAppender
- JPAAppender
- MemoryMappedFileAppender
- NoSQLAppender
- OutputStreamAppender
- RandomAccessFileAppender
- RewriteAppender
- RollingFileAppender
- RollingRandomAccessFileAppender
- RoutingAppender
- SMTPAppender
- SocketAppender
- SyslogAppender
أشهر وسائط مستخدمة لتسجيل الأحداث هي وحدة التحكم، الملف، وقاعدة البيانات. حيث يمكن أن يحفظ الملف رسائلك، قد يُستخدم قاعدة البيانات لتدقيقها. لهذا الغرض، سيتم التركيز في هذا القسم على كيفية استخدام JDBCAppender بكفاءة.
مسجلJDBC
الهدف الرئيسي لـ مسجل JDBC هو كتابة أحداث السجل في جدول علاقي من خلال اتصالات JDBC. لا نستهلك الكثير من الوقت في شرح كيف يمكنك تحسين حمامات الاتصال الخاصة بك لأن هذا البرنامج التعليمي ليس مخصصًا لهذا الغرض. ولكن بالتأكيد ستحصل على مثال وظيفي كامل يساعد في كتابة أحداث السجل الخاصة بك في قاعدة البيانات. قبل أن نتقدم، دعنا نرى جميع المعلمات اللازمة ووصف لكل منها لتكوين مسجل JDBC بشكل صحيح.
Parameter Name | Type | Description |
---|---|---|
Name | String | Required, The name of the Appender |
ignoreExceptions | boolean | Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller. |
filter | Filter | The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not. |
bufferSize | int | Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified. |
connectionSource | ConnectionSource | Required, the connections source from which the database connections should be retrieved. |
tableName | String | Required, the name of the Table on which your log events should be persisted. |
columnConfigs | ColumnConfig[] | Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements. |
Parameter Name | Type | Description |
---|---|---|
jndiName | String | Required, full prefixed JNDI name that the javax.sql.Datasource is bound to. |
Parameter Name | Type | Description |
---|---|---|
class | String | Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections. |
method | boolean | Required, The name of the static factory method for obtaining JDBC connections. |
Parameter Name | Type | Description |
---|---|---|
name | String | Required, the name of the database column |
pattern | String | Ability to specify any legal pattern that Log event would be formatted with |
literal | String | Ability to specify literal value in this column (i.e. SEQ.NEXTVAL) |
isEventTimestamp | boolean | Indicating whether the event would consider Timestamp |
isUnicode | boolean | For unicode purpose as you may refer for Log4j2 documentation for further details |
isClob | boolean | For storing character large object, you may refer for Log4j2 documentation for further details. |
نظرًا لأنه يتعين عليك استخدام JNDI، سيقوم مثالنا بتكوين مصدر بيانات الاتصال لقاعدة بيانات Oracle و Apache Tomcat 7.
- إذا لم تقم بتثبيت قاعدة بيانات Oracle في بيئتك بعد، فإنه من الجيد إذا قمت بذلك. إذا كنت لا تعرف كثيرًا عن Oracle، فأنا أوصي بتثبيت الإصدار Express.
- ثبت Apache Tomcat 7 في بيئتك.
- أنشئ مشروع Maven WebApp في Eclipse الخاص بك.
- تأكد من أن مشروعك تم إنشاؤه بنجاح وإذا لاحظت أي خطأ في ملف pom، تأكد من إصلاحها.
- أضف تبعيات Log4j2.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
- قم بتكوين سياقك لتضمين إعلان مصدر بيانات MySQL. وفقًا لتوثيق Apache، يجب أن يكون هذا الملف داخل مجلد META-INF الخاص بتطبيق الويب الخاص بك.
<Context path="/JournalDevWebLogging"
privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource name="jdbc/JournalDevDB" auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
- قم بتكوين قاعدة البيانات الخاصة بك وأنشئ جدول السجل،
CREATE TABLE `logging` (
`EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
`EVENT_DATE` datetime DEFAULT NULL,
`LEVEL` varchar(45) DEFAULT NULL,
`LOGGER` varchar(45) DEFAULT NULL,
`MSG` varchar(45) DEFAULT NULL,
`THROWABLE` varchar(45) DEFAULT NULL,
PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- قم بتكوين ملف log4j2.xml ليبدو كما يلي:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- قم بإنشاء أي مورد ويب يمكنك من الحصول على مرجع لسجل (logger) ثم تسجيل حدث.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
logger.trace("JournalDev Database Logging Message !");
}
}
- يمكنك اختياريًا تكوين ServletContextListener الذي قد يضمن أن تتم عملية تهيئة مصدر البيانات بشكل صحيح.
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
private InitialContext context = null;
public void contextDestroyed(ServletContextEvent event) {
}
public void contextInitialized(ServletContextEvent event) {
try {
// احصل على السياق الأولي
context = new InitialContext();
// احصل على مرجع للسياق الفرعي env
Context envContext = (Context)context.lookup("java:comp/env");
// احصل على مرجع للسياق الفرعي jdbc ثم تحديد مصدر البيانات المحدد
LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
} catch (NamingException e) {
LogManager.getRootLogger().error(e);
}
}
}
- حدد Servlet داخل ملف الـ web.xml الخاص بك.
- قم بتشغيل التطبيق والوصول إلى Servlet المحدد أعلاه. سترى السجلات التالية.
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms
Log4j2 Filters
حتى إذا كان هناك مرشح LoggerConfig للتعامل مع حدث Log، يمكنك تكوينه لرفض تمرير حدثي السجل إلى Appenders الخلفية. يمكن القيام بذلك باستخدام Log4j2 Filter. لا يهدف هذا القسم إلى توفير كمية كبيرة وضخمة من البرامج التعليمية حول استخدام الفلاتر في Log4j2، حيث تحتاج إلى الكثير من المقالات التي تغطي كل منها. ولكن هنا، سترى كيفية استخدام الفلتر الأبسط لفهم المفهوم. أحد أبسط الفلاتر التي يمكنك استخدامها هو BurstFilter الذي يوفر لك آلية للتحكم في معدل معالجة LogEvents عن طريق التخلص بصمت من الأحداث بعد الحد الأقصى الذي تم الوصول إليه. في الوقت الحالي، يمكنك رؤية كل التفاصيل اللازمة لاستخدام BurstFilter أدناه.
Parameter Name | Type | Description |
---|---|---|
level | String | Level of messages to be filtered |
rate | float | The average number of events per second to allow |
maxBurst | integer | The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate. |
onMatch | String | Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
onMismatch | String | Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
الآن، انظر إلى موقع BurstFilter داخل Appender قاعدة البيانات الخاص بك.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- مكوّن إلحاق قاعدة البيانات يأخذ بعين الاعتبار BurstFilter بينما مكوّن الإخراج إلى وحدة التحكم لا يفعل ذلك.
- أثناء استخدام مسجّل الوحدة إلى وحدة التحكم يمكن أن يؤدي إلى تسجيل أحداث سجل كاملة، فإن مكوّن إلحاق قاعدة البيانات لن يقوم بذلك حيث سيمنعه BurstFilter من التقدم.
- يتم تحقيق هذا الرفض لأحداث السجل حتى مع استخدام المسجّلات المستخدمة كمرشح لمعالجة أحداث السجل. هذا منطقي للغاية كما هو موضح في JournalDevServlet أدناه.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
for(int i = 0 ; i < 1000 ; i++){
logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
}
}
}
حتى وإن كانت تلك تكوينات المسجّل مرشحة لمعالجة أحداث السجل المرسلة هناك، إلا أن المرشح قد منع بعضها من المعالجة وبالتالي التسجيل. يمكنك إضافة هذا لمفهوم مساحة التسجيل لفهم المفهوم بشكل كامل.
تخطيطات Log4j2
نظرًا لاختلاف الملحقات التي تستهلك أحداث السجل وطبيعة كل ملحق، يتم إنشاء التخطيطات لتشكيل حدث السجل في الشكل الذي يلبي احتياجات من سيستهلك حدث السجل. في واجهتي برمجة Log4j 1.x و Logback APIs، كان تحويل تخطيطات حدث السجل إلى سلسلة نصية، بينما قامت تخطيطات Log4j2 باعتبار طريقة تحويل مختلفة؛ وهي عن طريق تحويل حدث السجل إلى مصفوفة بايتات. ستجبرك هذه الطريقة الجديدة للتحويل على تكوين Charset للتأكد من أن مصفوفة البايتات تحتوي على القيم الصحيحة. من الوارد جدًا العودة إلى موقع Apache Log4j2 الرسمي ومعرفة المزيد حول التخطيطات والأنواع المختلفة التي يوفرها Log4j2. في هذا القسم، سننظر في أشهر التخطيطات التي يستخدمها معظم مطورينا وبالتأكيد قد سمعت عنها؛ وهي PatternLayout.
Log4j2 PatternLayout
تخطيط النمط هو نمط سلسلة قابل للتكوين والمرن يهدف إلى تنسيق حدث السجل. هذا النوع من التنسيق يعتمد على مفهوم نمط التحويل. ستوضح لك هذا القسم أهم الميزات التي يوفرها تخطيط النمط. يتعلق نمط التحويل بنمط التحويل الذي يوفر printf في لغة C. بشكل عام، يتألف نمط التحويل من نص حرفي و تعبيرات تحكم في التنسيق تُسمى محولات التحويل. يوضح الشكل أدناه مكونات نمط التحويل: هذا الشكل أعلاه محاولة لتبسيط نمط التحويل، ولكن بالتأكيد من الأفضل لك أن تشير إلى وثائق Apache Log4j2 لمزيد من التفاصيل حول التخطيطات وتخطيط النمط بشكل خاص. أيضًا، يمكنك الرجوع إلى أعلى لحدث السجل ومعرفة في كل مرة أي نمط تحويل يتم استخدامه لتنسيق الرسائل.
أي مستوى Log4j2 يجب عليك استخدامه
أكبر سؤال قد تطرحه على نفسك هو متى يجب استخدام مستوى حدث السجل المحدد. في مجال التطوير، من الطبيعي استخدام حدث سجل DEBUG بينما في الإنتاج يجب علينا استخدام مستوى INFO أو WARN. يجب أن يوجهك الجدول أدناه بشأن المستوى الذي يجب استخدامه في حالة معينة.
Log Event Level | When It Should Be Used |
---|---|
OFF | When no events will be logged |
FATAL | When a severe error will prevent the application from continuing |
ERROR | When an error in the application, possibly recoverable |
WARN | When an event that might possible lead to an error |
INFO | When an event for informational purposes |
DEBUG | When a general debugging event required |
TRACE | When a fine grained debug message, typically capturing the flow through the application |
ALL | When all events should be logged |
ملخص دليل تعليمي لـ Log4j2
Log4j2 هو إصدار مطور لإطار Apache Logging. قدم Log4j2 مجموعة من الميزات الجديدة وتحسينات الأداء عن Log4j1.x. يهدف هذا الدليل التعليمي لـ log4j2 إلى مساعدتك في الحصول على كل شيء في مكان واحد. نظرًا لأن بعض هذه المفاهيم ليست سهلة لتغطيتها جميعًا في مرة واحدة، قررنا تضمين جهودنا في شرح المفهوم واستخدام بعض الأمثلة للتوضيح أكثر. ينطبق ذلك على الملحقات والمرشحات والتخطيطات والبحث. بعض النقاط المهمة للتأكد من قدرتك على تشغيل التطبيق أدناه وتجنب أي عقبات، تحقق من ما يلي:
- إعداد برنامج Eclipse IDE مع Maven ممكّن.
- يجب أن يحتوي Tomcat الخاص بك على JAR mysql-connector داخل مجلد lib في Apache Home.
- يجب أن تكون على دراية بكيفية استخدام Maven.
قم بتنزيل مشروع Apache Log4j 2 المثالي
هذا كل شيء بالنسبة لدليل Log4j2، أتمنى أن تكون معظم النقاط الهامة مغطاة لتبدأ في استخدام Log4j2 في تطبيقك.