ברוכים הבאים למדריך לדוגמה של 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
שימוש ב-Logging API באפליקציה אינו פריבילגיה, זה חיוני. Log4j הוא ספרייה קוד פתוחה המתפרסמת ומורשית תחת Apache Software. ניתן לאתר תקלות באפליקציה באמצעות איתור תקלות ב-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. התמונה למטה מציגה את המחלקות החשובות ב-API של Log4j2. כאן מוסבר בפירוט את הארכיטקטורה שמוצגת למעלה:
-
יישומים יבקשו מ-
LogManager
לקבלLogger
עם שם מסוים. -
LogManager
ימצא אתLoggerContext
המתאים ואז יקבלLogger
ממנו. -
אם המקליט עדיין לא נוצר, ייווצר ויתייחס ל־LoggerConfig על פי שלוש אפשרויות שלהלן:
- המופע של ה־Logger ייווצר ויתייחס ל־LoggerConfig שיש לו את אותו השם. לדוגמה, App.class ב־
getLogger(App.class)
יתבדק כמחרוזתcom.journaldev.App
. שם ה־LoggerConfig זהה לשם המחליף המאומת באופן מלא (רכיב תוכנה). - המופע של ה־Logger ייווצר ויתייחס ל־LoggerConfig שיש לו את אותו החבילה האב של ה־Loggers. לדוגמה
com.journaldev
ב־getLogger("com.journaldev")
- המופע של ה־Logger ייווצר ויתייחס ל־Root LoggerConfig. קובץ התצורה של ה־Root ישמש כאשר אין קובץ תצורה או כאשר אתה מקבל מקליט עם שם שאינו מוגדר בהכרזות המקליטים.
- המופע של ה־Logger ייווצר ויתייחס ל־LoggerConfig שיש לו את אותו השם. לדוגמה, App.class ב־
-
אובייקטים של
LoggerConfig
נוצרים מהכרזת המפקד בקובץ ההגדרה. משמש גם ה-LoggerConfig לטפל באירועים התופתעיםLogEvents
ולהפנות אותם למגבליהם המוגדרים Log4j2 Appenders. -
המפקד השורש הוא מקרה יוצא מן הכלל, במונחים של קיומו. הוא תמיד קיים ובראש כל היררכיה של המפקדים.
-
אפשר להשיג את המפקד השורש באמצעות ההצהרות הבאות:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); Logger logger = LogManager.getRootLogger();
-
שמות המפקדים של log4j2 רגישים לקונסונציה.
-
חוץ מהמפקד השורש, כל המפקדים ניתן להשיג על ידי העברת שמם לתוך
LogManager.getLogger()
. -
LoggerContext היא נקודת המוקד במערכת ה־Logging מאחר וניתן לקיים מספר LoggerContexts בתוך היישום שלך. לכל LoggerContext יש להגדיר תצורה פעילה.
-
תצורת Log4j2 מכילה את כל הנכסים של מערכת ה־Logging; LoggerConfig(s), Appender(s), Filter(s) ועוד.
-
קריאה ל־LogManager.getLogger() על ידי העברת השם תחזיר תמיד את ההפניה לאותו אובייקט logger המדוייק.
-
הגדרת מערכת ה־Logging בדרך כלל מתבצעת באתחול היישום. זה יכול להתבצע בצורות שונות; תכנתית או על ידי קריאה לקובץ תצורת log4j2.
כל לוגר מקושר לאובייקט LoggerConfig, סט של אובייקטי LoggerConfig מהווים מורכבת של לוגרים. המושג הזה ידוע כ־היררכיה של הלוגרים. היררכיה של הלוגרים מורכבת מסט של אובייקטי LoggerConfig עם יחס הורה־ילד. האלמנט העליון ביותר בכל היררכיה של הלוגרים הוא הלוגר השורש. אם Log4j2 לא מוצא את קובץ התצורה, רק הלוגר שורש יועשה שימוש להתעדכות עם רמת התעדות כשגיאה. בתמונה למטה מוצג הודעת אזהרה שתקבלו במקרה זה. שגיאת סטטוס לוגר לא נמצא קובץ הגדרת log4j2. משתמש בהגדרת ברירת המחדל: התעדכות רק שגיאות לקונסולה. הטבלה למטה מראה את יחס הורה־ילד ביררת הלוגרים.
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 |
כדי להבהיר את היחס הורה־ילד, הטבלה לעיל תקרא כדלקמן:
- Root הוא הורה ל־com.
- Root הוא אב אבן ל־com.journaldev.
- Root הוא אב אבן ל־com.journaldev.logging.
- com הוא ילד של Root.
- com הוא הורה ל־com.journaldev.
- com הוא אב אבן ל־com.journaldev.logging.
- com.journaldev.logging הוא ילד של com.journaldev וכן הלאה.
נאמר על מופע של LoggerConfig שהוא אב של מופע אחר של LoggerConfig אם שמו יופיע כפירוט לשם הנחלש. נאמר על מופע של LoggerConfig שהוא הורה למופע אחר של LoggerConfig אם אין שמות שמתחלפים ביניהם.
הגדרת Log4j2
ישנם דרכים רבות להשתמש בהגדרת Log4j2 ביישום שלך.
- על ידי קובץ הגדרה כתוב ב- XML, JSON, YAML או קובץ מאפיינים.
- תכנתית, על ידי יצירת מפענח הגדרה ויישום הגדרה.
- תכנתית, על ידי קריאה ל- API החשופים בממשק הגדרה.
- תכנתית, על ידי קריאה לשיטות במחלקת המאזין הפנימית.
נמקד בעיקר בקובץ ההגדרה. עם זאת, תוכלו לדעת גם על גישת התכנות, למקרה שתרצו להגדיר אסטרטגיית יומן ספציפית עבור מסוים Logger מסוים. לפני כל דבר, נבחן את המקרה שבו לא סיפקתם קובץ הגדרה. המימוש של 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>
וכאן ההסבר המפורט לקוד שמצוי לעיל:
- האפליקציה הפנתה ללוגר השורש על ידי קריאה לשיטת
getRootLogger
ב- LogManager. - הפניה ללוגר מתוך LogManager התחילה את מערכת Log4j.
- Log4j תבדוק את נכס המערכת log4j.configurationFile כדי לקבוע את קובץ התצורה של log4j2. תצורת Log4j ניתן לכתוב ב־JSON, YAML ו־XML.
- ניתן להגדיר את נכס המערכת log4j.configurationFile דרך
System.setProperties("log4j.configurationFile","FILE_PATH")
או על ידי העברתו כפרמטר של JVM כמו שאתה רואה בתמונה למטה. שים לב גם לקידומת של פרוטוקול הקובץ.
- במקרה של הגדרת מערכת אינה מוגדרת סדר התצורה לוקח את העדיפות הבאה:
- התצורה של 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.
- הלוגר הבסיסי יפיץ הודעות לקונסולה.
- הפריסה המתבצעת הוא להיות
%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 {
// קבלת מופע של מפענח התצורה; האפשרויות שלך הן מפענח התצורה המותאם, מפענח תצורת XML,
// מפענח תצורת YAML ו־מפענח תצורת JSON
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());
// הוספת מפענח חלון לתוך התצורה
configuration.addAppender(appender);
// יצירת LoggerConfig
LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
// הוספת מפענח
loggerConfig.addAppender(appender,null,null);
// הוספת לוגר ושיוך אותו למופע של LoggerConfig
configuration.addLogger("com", loggerConfig);
// קבלת מופע של הקשר
LoggerContext context = new LoggerContext("JournalDevLoggerContext");
// התחלת מערכת הלוגים
context.start(configuration);
// קבלת הפניה ללוגר
Logger logger = context.getLogger("com");
// LogEvent של הודעת DEBUG
logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
// LogEvent של הודעת שגיאה עבור לוגר המוגדר כ־FATAL
logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
// LogEvent של הודעת שגיאה שתתופס על ידי השורש
logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
}
}
- אפשר להשתמש באחד ממפענחי התצורה הזמינים שמספק Log4j2 או להשתמש במפענח המותאם ברירת המחדל.
- המפענח יעניק לך מופע של התייחסות תצורה הנדרשת על ידי העברת הקובץ התצורה המתאים.
- המופע של התצורה יופעל בשילוב עם 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 עם השם, ה-LEVEL וללא שימוש במסנן.
- מופע של 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 logger להדפיס הודעות שבהן רמתן היא FATAL.
- ברירת המחדל, Root logger מוגדר להדפיס הודעות שבהן רמתן היא ERROR.
- הודעות שגיאה לא יודפסו על ידי 'com' logger מכיוון שרמתו היא 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 logger והשנייה היא מה-Root Logger. הודעת השגיאה של com logger אינה מודפסת מכיוון שרמתה היא קטלנית.
רמות Log4j2
ניתן לראות בדוגמאות הקוד שלמעלה כי בכל פעם שאנו מגדירים LoggerConfig, אנו גם מספקים רמת רישום. כברירת מחדל, הרישום של log4j2 הוא מתווסף. זה אומר כי כל הלוגרים האב הם גם ייעשו בשימוש כאשר משמש לוגר מסוים. בתמונה למטה מתבהרת מצביע זה. ולהלן נקודות הבהרה לגבי זה:
- כפי שצוין מראש, לכל לוגר יש LoggerConfig שקשור איתו. ל LoggerConfig זה הוגדר בתחום התצורה.
- רמת הרישום ניתנת לקביעה בתחום של LoggerConfig.
- ניתן להשיג את הלוגר לפי שמו, חבילת ההורים או על ידי הפנייה אל Root Logger עצמו.
- Root Logger הוא צומת הרמה העליונה לכל היררכיית LoggerConfig.
- לאחר שאתה משיג את הלוגר com.journaldev ומתחיל אירוע לוג עבור הרישום, ה-loggerConfig (net.journaldev) ירשום את ההודעה וההודעה תועבר גם למעלה בהיררכיה ללא כל כבוד לרמות הרישום של ההורים. לכן, אירוע הלוג יועבר ללוגרים com ו-Root והם גם ירשמו את ההודעה בהתאם לרמות הרישום המוגדרות.
- לאחר שאתה משיג את הלוגר com ומתחיל אירוע לוג עבור הרישום, ה-loggerConfig (com) ירשום את ההודעה וההודעה תועבר גם למעלה בהיררכיה ללא כל כבוד לרמות הרישום של ההורים. כלומר, לוגר Root יועבר לו אירוע הלוג וגם הוא ירשום את ההודעה.
אותו מקרה עבור היררכיית net.journaldev. - אותו מקרה עבור היררכיה net.journaldev.
- בסעיפים הבאים, יוסיפו הסברים נוספים למושג ההוספתי.
- קיים סיכוי שההורה יתעלם מהודעה באמצעות מושג הפילטר או על ידי הגדרת האינדיקטור ההוספתי לפסיק, כך שאירועי הידיעה לא יתפשטו להורים.
- קיים סיכוי שהיומן יתעלם מהודעה אם רמת loggerConfig הרלוונטית גדולה מרמת אירועי הידיעה.
עכשיו, בואו נראה את הדוגמה הקשורה למושג ההוספתי המוסבר לעיל:
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 ::
הנה הסבר מפורט לקוד המוצג לעיל:
- קובץ ההתקנה מכיל חמישה מופעים של loggerConfig המוגדרים והם הם Root, com, com.journaldev, net & net.journaldev. בדיוק כמו יררכית היומן המוצגת לעיל.
- רמת Root מוגדרת להיות ERROR וזהו למעשה הערך הברירתי.
- רמות com & com.journaldev מוגדרות להיות TRACE.
- רמות net & net.journaldev מוגדרות להיות ERROR.
- ייתכן ששמת לב שהודעות היומן של ComAPP ו-ComJournalDevApp הוצגו פעמיים ושלוש בהתאמה. הודעות אלו הוצגו על פי יררכית היומן עבור ComApp & ComJournalDevApp שם הם נמצאים בחבילות com & com.journalDev בהתאמה. יש לנו מקרה דומה עם מחלקות NetApp & NetJournalDevApp.
- ההורים מועברים ככולל כיון שהאינדיקטור ההוספתי מוגדר לאמת כברירת מחדל.
מרחב התיעוד לוקח בחשבון רמות של אירועי תיעוד ואת רמת הלוגר בנוסף למסד הנתונים של הלוגר.
אז מה קורה אם נשנה את 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) הוזן לטיפול בו ולהדפסת ההודעה.
- המאפיין additive של LoggerConfig של com.journaldev.logging מוגדר כברירת מחדל כ-true, האירוע של הלוג הועבר להורה שלו שבמקרה זה מפנה ל-com.journaldev.
- בשל אי הגדרת LoggerConfig של com.journaldev בהגדרה, אין פעולה שנעשית ואירוע הלוג יועבר למעלה אל המופעים של LoggerConfig של com ואז שורש.
- 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, הוא אמור להופיע ארבע פעמים, אך בשל החסרון של LoggerConfig של com.jounraldev, הוא מוצג שלוש פעמים.
במקרה שהגדרת LoggerConfig של com.journaldev עם אין רמת רישום מצויינת, היא תירש את רמת הרישום של הוריה.
אך מה קורה אם הגדרת LoggerConfig של com.journaldev בקובץ התצורה שלך ושכחת לציין את רמת הרישום של LoggerConfig. למרבה המזל, המושג של היררכיית Logger יוכל לשמור עליך כאן ו־com.journaldev יירש את ערך הרמה שלו מההורה שלו. להלן דוגמת קובץ התצורה הבינארי יחד עם הטבלה של רמת הרישום של כל התצורה של ה־logger.
<?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 לא מוגדר, וההיררכיה של ה-Logger מגיעה עכשיו עד הראש. רמת הלוג של השורש היא 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. ניתן להגדיר את המאפיין additive להיות שקר כדי למנוע העברת האירוע בלוג ללוגרים האב. בהמשך יוצג דוגמה דומה לדוגמה שראית קודם אך הפעם עם מאפיין ההתמסרות, כך שתוכל להבחין בהבדל.
<?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 Lookups
לרענון, ניתן להגדיר lookups כדי להעביר ערכים לקובץ התצורה שלך. Log4j2 מספק לך סט שונה של Lookups שניתן להשתמש בהם באופן עצמאי להגדרת ערכים מהקשרים השונים:
- Context Map Lookup
- Date Lookup
- Environment Lookup
- Java Lookup
- JNDI Lookup
- JVM Input Argument Lookup (JMX)
- Main Arguments Lookup
- Map Lookup
- Structured Data Lookup
- System Properties Lookup
- Web Lookup
ניתן להפנות למסמך התיעוד של Log4j2 לקבלת פרטים נוספים על כל סוגי החיפושים, אך נבחן כאן דוגמאות כדי לכסות את היסודות של החיפוש של Log4j2. החיפוש של הסביבה מייצג את הדרך שבה ניתן להעביר ערך של סביבה (באמצעות Linux etc/profile, מערכת הסביבה של Windows או סקריפטי ההפעלה של היישום). כפי שרובנו יודעים, יש לנו אפשרות להגדיר קבוצה של ערכי סביבה עבור היישום לשימוש. נבחן את הדרכים הפופולריות ביותר להגדרת המשתנים הסביבתיים שלך.
-
הגדרת משתנה סביבתי על ידי שימוש ביכולות הסביבה של Windows:
- לחץ ימנית על סמל המחשב שלך ובחר 'מאפיינים'. תפריט הבקרה הראשי יוצג.
- לחץ על 'הגדרות מתקדמות של המערכת' ואז פתח את חלון משתני הסביבה.
- בתחום משתני המערכת, הגדר משתנה בשם JournalDevVar עם הערך JournalDev.
- עדכון PatternLayout בקובץ ה- log4j2.xml שלך כדי לכלול את המשתנה שהוספת.
- הגדר את המשתנים הסביבתיים שלך באמצעות היכולת של סקריפט ההפעלה.
- במקום להשתמש בתסריט ברירת המחדל הרגיל, ניתן להשתמש ביכולת ההרצה של סביבת הפיתוח Eclipse IDE. לחץ על תפריט ההרצה שלך ובחר בהגדרות הרצה.
- נווט לתוך לשונית הסביבה והגדר את המשתנה שלך שם.
עכשיו הסתכל על קובץ ה־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 ::
אך עשוי להיות לך בעיה קטנה כאן ובמיוחד כאשר אתה מגדיר משתני סביבת מערכת ב-OS וזהו מטמון ה-Eclipse. לרעה, Eclipse יכול להחזיק במטמון את כל המשתנים המערכתיים כאשר הוא מופעל ותוכל למצוא את כולם תחת הרץ – תצורת הרצה – לשונית סביבה – לחץ על לחצן בחר.
לכן, עשוי להיות לך בלבול כאשר אתה מגדיר אותו אבל היישום אינו מזהה אותו. גם אם תפעיל מחדש את ה-Eclipse שלך, לא תקבל את הפתרון וכדי לפתור אותו עליך להפעיל eclipse.exe -clean על ההתקנה שלך של Eclipse. כדי לוודא שמשתני הסביבה שלך מוגדרים בצורה תקינה והמערכת שלך תמצא אותם ברצונה, תוכל להשתמש בסוג התוסף המתאים שסופק על ידי Log4j2 API. צור מופע של EnvironmentLookup ושאל אותו לחיפוש משתנה מסוים ואם הוא מוגדר, תמצא אותם בקלות.
EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));
מצבי Log4j2
אתה ראית קודם כיצד ניתן להשתמש במזהים עבור הזרמת משתנים לקובץ התצורה שלך. אולם, ייתכן כי תרצה לשנות את התוכנה שבה ההודעות שלך עוברות. במקום להשתמש ישירות בקונסולה, תרצה אולי קובץ או מאגר מידע בסיסי לוודא שההודעות שלך נשמרות לצמיתות. Log4j2 סיפקה הרבה Appenders, ותוכל להפנות לתיעוד של log4j2 כדי לקבל פרטים נוספים לגבי Appender. באופן קצר, להלן רשימת כל ה-Appenders של Log4j2.
- ConsoleAppender
- AsyncAppender
- FailoverAppender
- FileAppender
- FlumeAppender
- JDBCAppender
- JMSAppender
- JPAAppender
- MemoryMappedFileAppender
- NoSQLAppender
- OutputStreamAppender
- RandomAccessFileAppender
- RewriteAppender
- RollingFileAppender
- RollingRandomAccessFileAppender
- RoutingAppender
- SMTPAppender
- SocketAppender
- SyslogAppender
הערוצים הכי נפוצים לרישום אירועים הם קונסולה, קובץ, ומסד נתונים. מאחר שהקובץ ישמור את ההודעות שלך, המסד נתונים עשוי לשמש לצורך ביקורת עליהם. למטרה זו, תיקון זה יתמקד באיך JDBCAppender יכול לשמש בצורה יעילה.
JDBCAppender
המטרה העיקרית של JDBCAppender היא לכתוב אירועי לוג לתוך טבלה רציפה דרך חיבורי JDBC. אנו לא מבלים הרבה זמן בהסבר על איך ניתן לייעל את בריכות החיבור שלך מכיוון שהמדריך הזה לא מיועד למטרה זו. אך בוודאי תקבל דוגמה מלאה ותפעולית המסייעת בכתיבת אירועי הלוג שלך למסד נתונים. לפני שנמשיך, נראה את כל הפרמטרים הנדרשים ותיאור עבור כל אחד כדי להגדיר את JDBCAppender בצורה תקינה.
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 Edition).
- התקן את 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
גם אם יש לוגר קונפיגורציה שמתאים לטיפול באירוע לוג, אפשר להגדיר אותו לדחות את האירועים הלוג של העברה למאחסנים הרקע. זה יכול להיעשות על ידי מסנן log4j2. סעיף זה אינו נועד לספק לך מדריך פולחן, מסיבי וענק על שימוש במסננים ב-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>
- Database Appender כן מתחשב BurstFilter בעוד ש-console Appender לא.
- בשימוש ב-Console logger יוביל אותך לארועי יומן מלאים להיות רשומים, בעוד ש-database Appender לא יעשה זאת כיוון ש-BurstFilter ימנע מחלק מהם להמשיך.
- הדחיית LogEvents הזו מתבצעת אפילו שה-Loggers בשימוש הם המועמדים לטיפול בארועי היומן. זה כל כך הגיוני כפי שנראה ב-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 !");
}
}
}
אפילו ש-LoggerConfigs אלו הם המועמדים לטיפול בארועי יומן שנזרקו שם, אך ה-Filter מנע מחלק מהם להיעלם ולאחר מכן להיות רשומים. אתה עשוי להוסיף זאת לרעיון של מרחב היומן כדי להבין את כל הרעיון של היומון.
פירושיות Log4j2
בשל מקובלים שונים שצורפים שותפים באירועי הלוג וטבע כל אחד מהם, הפריסות נוצרות כך שתוכנן להתאים את האירוע הלוג לתבנית המתאימה לצרכיו של מי שיצטרף לאירוע הלוג. ב- Log4j 1.x ו- Logback APIs, ההמרה של האירועי הלוג הייתה למחרוזת, בעוד שהפריסות של Log4j2 נחשבות לדרך שונה של המרה; וזהו באמצעות המרת האירוע הלוג למערך של בתים. סוג זה של המרה חדש יחייב אותך להגדיר את Charset כדי לוודא שמערך הבתים מכיל את הערכים הנכונים. מומלץ מאוד לחזור לאתר הרשמי של Apache Log4j2 ולקרוא עוד על הפריסות והסוגים השונים שמספק Log4j2. במקטע זה, נתייחס לפריסה הכי מפורסמת שתמיד משמשת על ידי רוב המפתחים שלנו וכמובן שאתה עשוי לשמוע עליה; זו הפריסה PatternLayout.
תבנית Log4j2
הפריסה של התבנית היא תבנית מחרוזת נמדדת, גמישה המיועדת לעיצוב של LogEvent. סוג זה של עיצוב תלוי על המושג של תבנית ההמרה. בסעיף זה נמחק לך את המאפיינים החשובים ביותר שמספקת התבנית הפריסה. תבנית ההמרה קשורה למושג של תבנית ההמרה שprintf בשפת C מספקת. בדרך כלל, תבנית ההמרה מורכבת מ טקסט ליטרלי ו ביטויי בקרת פורמט הנקראים ממריצי ההמרה. התמונה למטה מתארת את הרכיבים שבהם מורכבת תבנית ההמרה: התמונה למעלה היא ניסיון לפשט את תבנית ההמרה, אך בטוח שכדאי לך לעיין בתיעוד של Apache Log4j2 לפרטים נוספים אודות פריסות ובמיוחד על תבנית הפריסה. כמו כן, תוכל לעיין למעלה בהתייחס לאירועי הלוג ולראות בכל פעם איזו תבנית המרה משמשת לעיצוב ההודעות.
איזה רמת Log4j2 כדאי להשתמש
השאלה הגדולה ביותר שתישאל היא מתי צריך להשתמש ברמת אירוע Log מסוימת. בשדה הפיתוח, נפוץ להשתמש באירועי דיבוג, בעוד שבייצור אנו צריכים רמת INFO או WARN. הטבלה למטה צריכה להדריך אותך איזה רמת log4j2 צריך להשתמש בכל מקרה.
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. Log4j2 סיפק סט של תכונות חדשות ושיפורי ביצועים מ-Log4j1.x. מדריך זה של log4j2 מיועד לעזור לך לקבל את כל זה במקום אחד. מכיוון שחלק מהמושגים הללו לא קלים לכיסוים כל פעם אחת, החלטנו לסגור את המאמצים שלנו בהסברת המושג ובשימוש בדוגמאות כדי להבהיר יותר. משגרים, מסננים, פריסות וחיפושים נכללים בכלל זה. נקודות חשובות כדי לוודא שאתה יכול להפעיל את היישום למטה ולמנוע כל מחסור, ודא ש:
- ה-IDE שלך ב- Eclipse מאופשר מייבן.
- ל-Apache Tomcat שלך יש mysql-connector JAR בתוך תיקיית lib בתוך התיקייה הבית של Apache שלך.
- אתה מודע לאופן שימוש ב-Maven.
הורד את פרוייקט הדוגמה של Apache Log4j 2
זהו הכל עבור מדריך log4j2, אני מקווה שרוב הנקודות החשובות כוללות עזרו לך להתחיל להשתמש ב-Log4j2 ביישום שלך.