CodeGym /בלוג Java /Random-HE /רישום: מה, איך, איפה ועם מה?
John Squirrels
רָמָה
San Francisco

רישום: מה, איך, איפה ועם מה?

פורסם בקבוצה
שלום לכולם בקהילת CodeGym! רישום: מה, איך, איפה ועם מה?  - 1 היום בואו נדבר על רישום:
  1. מה זה, למה זה קיים, מתי כדאי להשתמש בו, מתי כדאי להימנע ממנו.
  2. אילו יישומי רישום זמינים ב-Java, ומה עליך לעשות עם כל אפשרויות הרישום הללו.
  3. ורמות יומן. נדון מהו תוספת וכיצד להגדיר אותו בצורה נכונה.
  4. רישום צמתים וכיצד להגדיר אותם בצורה נכונה כך שהכל יעבוד כמו שאנחנו רוצים.
חומר זה מיועד לקהל רחב. זה יהיה ברור לכל מי שרק יכיר את ג'אווה, כמו גם לאנשים שכבר עובדים אבל רק חקרו logger.info("log something"); בואו!

למה אתה צריך רישום?

בואו נסתכל על כמה מקרים אמיתיים שבהם רישום יכול לפתור בעיה. הנה דוגמה מהעבודה שלי. ישנן נקודות שבהן אפליקציה משתלבת עם שירותים אחרים. אני משתמש ברישום בנקודות האלה כדי ליצור סוג של "אליבי" : אם האינטגרציה לא עובדת, קל להבין באיזה צד יש את הבעיה. כמו כן, רצוי לרשום מידע חשוב המאוחסן במסד נתונים. לדוגמה, יצירת משתמש אדמין. זה בדיוק מסוג הדברים שיהיה טוב לרשום.

כלים לכניסה ל-Java

בין פתרונות הרישום הידועים ב-Java, אנו יכולים להדגיש את הדברים הבאים:
  • Log4j
  • JUL - java.util.logging
  • JCL - רישום של ג'קרטה קומונס
  • התחבר חזרה
  • SLF4J - חזית רישום פשוטה עבור Java
אנו נספק סקירה כללית של כל אחד מהם. לאחר מכן ניקח כריכת slf4j - log4j כבסיס לדיון מעשי. זה אולי נראה מוזר עכשיו, אבל אל דאגה: עד סוף המאמר הכל יהיה ברור.

System.err.println

בהתחלה, היה System.err.println (הצגת ערכי יומן בקונסולה). גם היום, טכניקה זו משמשת לרישום מהיר בעת איתור באגים. כמובן, אין הגדרות לדון כאן, אז רק זכור את השיטה הזו ונמשיך הלאה.

Log4j

זהו פתרון שלם שהמפתחים יצרו מתוך צורך. התוצאה היא כלי ממש מעניין שתוכלו להשתמש בו. עקב נסיבות שונות, פתרון זה לא הגיע ל-JDK, עובדה שהרגיז מאוד את הקהילה כולה. ל-Log4j יש אפשרויות תצורה המאפשרות לך לאפשר כניסה לחבילה com.example.typeולכבות אותה בחבילת com.example.type.genericהמשנה. זה מאפשר להוציא במהירות קוד שלא צריך להירשם. חשוב לציין כאן שיש שתי גרסאות של Log4j: 1.2.x ו-2.xx, והן אינן תואמות זו לזו . Log4j הוסיף את המושגים של appender (כלי המשמש לכתיבת יומנים) ופריסה (עיצוב יומן). זה מאפשר לך לרשום רק את מה שאתה צריך ולהתחבר בדיוק איך שאתה צריך את זה. נדבר יותר על תוספתן מעט מאוחר יותר.

JUL - java.util.logging

אחד היתרונות המרכזיים של פתרון זה הוא ש-JUL כלול ב-JDK (ערכת פיתוח ג'אווה). למרבה הצער, כאשר הוא פותח, יוצריו לא ביססו אותו על כלי השירות הפופולרי Log4j, אלא פתרון של IBM. להחלטה הזו היו השלכות. המציאות היא שאף אחד לא משתמש ב-JUL עכשיו. רמות היומן ב-JUL שונות ממה שיש ל-Logback, Log4j ו-Slf4j. זה מקשה עליהם להבין אחד את השני. יצירת לוגר דומה פחות או יותר. כדי לעשות זאת, עליך לבצע ייבוא:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
שם הכיתה מועבר, כך שנדע מאיפה יבוא הרישום שלנו. החל מ-Java 8, אתה יכול לעבור Supplier<String>. זה עוזר לנו לקרוא וליצור שורה רק כשאנחנו באמת צריכים אותה, ולא בכל פעם, כפי שהיה בעבר. רק עם שחרורו של Java 8 מפתחים סוף סוף פתרו בעיות חשובות והפכו את JUL לשמיש באמת. כלומר, שיטות עם Supplier<String> msgSupplierפרמטר, כפי שמוצג להלן:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - רישום של ג'קרטה קומונס

מכיוון שלא היה תקן תעשייתי לגבי רישום במשך זמן רב ואנשים רבים יצרו לוגרים מותאמים אישית משלהם, התקבלה ההחלטה לשחרר את JCL, עטיפה כללית שניתן להשתמש בה על אחרים. למה? לפעמים התלות שנוספו לפרויקט השתמשו ב-logger שונה מזה שבפרויקט. בגלל זה, הם נוספו לפרויקט כתלות טרנזיטיבית, וזה יצר בעיות אמיתיות כשניסינו לחבר את הכל ביחד. לרוע המזל, העטיפה לא הייתה פונקציונלית במיוחד ולא הוסיפה דבר. זה כנראה יהיה נוח אם כולם ישתמשו ב-JCL. אבל זה לא מה שקרה, אז השימוש ב-JCL הוא לא הרעיון הטוב ביותר כרגע.

התחבר חזרה

הנתיב של הקוד הפתוח קוצני... אותו מפתח שכתב את Log4j כתב גם את Logback כמסגרת רישום יורשת. זה התבסס על אותו רעיון כמו Log4j. ההבדלים ב-Logback הם:
  • ביצועים משופרים
  • הוסיפה תמיכה מקורית עבור Slf4j
  • אפשרויות סינון מורחבות
כברירת מחדל, Logback אינו דורש שום תצורה, ומתעד את כל האירועים ברמת DEBUG ומעלה. אם אתה צריך קצת התאמה אישית, אתה יכול להשיג זאת באמצעות תצורת XML:
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <encoder>
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
        </encoder>
    </appender>
    <logger name="org.hibernate.SQL" level="DEBUG" />
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
    <root level="info">
        <appender-ref ref="FILE" />
    </root>
</configuration>

SLF4J - חזית רישום פשוטה עבור Java

מתישהו בשנת 2006, אחד מהאבות המייסדים של Log4j עזב את הפרויקט ויצר את Slf4j (Simple Logging Facade עבור Java), עטיפה עבור Log4j, JUL, Common-logging ו-Logback. כפי שאתה יכול לראות, התקדמנו לנקודה של יצירת עטיפה על עטיפה... במקרה זה, היא מתחלקת לשני חלקים: API שנמצא בשימוש באפליקציה, ויישום שמתווסף עם נפרד תלות עבור כל סוג רישום. לדוגמה, slf4j-log4j12.jarו slf4j-jdk14.jar. אתה צריך לחבר את היישום הנכון וזהו: כל הפרויקט שלך ישתמש בו. Slf4j תומך בכל התכונות העדכניות ביותר, כגון עיצוב מחרוזות לרישום. בעבר הייתה בעיה כזו. נניח שאנו יוצרים ערך יומן כזה:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
בשל אופרטור השרשור, userהאובייקט הופך בשקט למחרוזת הודות ל user.toString(). זה לוקח זמן ומאט את המערכת. וזה עשוי להיות בסדר אם אנחנו מנפים באגים באפליקציה. אנו מתחילים להיתקל בבעיות אם רמת היומן עבור מחלקה זו היא INFO ומעלה. במילים אחרות, אל לנו לכתוב את ערך היומן הזה (עבור INFO ומעלה), ואסור לנו להשתמש בשרשור מחרוזת. בתיאוריה, ספריית הרישום עצמה צריכה לטפל בזה. כפי שזה קורה, זו התבררה כבעיה הגדולה ביותר בגרסה הראשונה של Log4j. זה לא סיפק פתרון הגון, אבל במקום זאת הציע לעשות משהו כזה:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
כלומר, במקום שורת קוד אחת לרישום, הם הציעו לכתוב 3! רישום צריך למזער שינויים בקוד, ושלוש השורות מפרות בבירור גישה כללית זו. ל-Slf4j לא היו בעיות תאימות עם ה-JDK וה-API, אז מיד הופיע פתרון נחמד:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
כאשר {}מציין מצייני מקום עבור הטיעונים שהועברו לשיטה. כלומר, הראשון {}מתאים ל user, והשני {}מתאים ל request.getRemoteAddr(). על ידי כך, נבצע שרשור מחרוזות רק אם רמת היומן מחייבת אותנו לכתוב את ערך היומן. לאחר מכן, Sjf4j החל לצמוח במהירות בפופולריות. נכון לעכשיו, זה הפתרון הטוב ביותר. בהתאם לכך, בואו נסתכל על רישום באמצעות slf4j-log4j12כריכה.

מה צריך לרשום

כמובן, אתה לא צריך לרשום הכל. זה לרוב לא הכרחי ולפעמים אפילו מסוכן. לדוגמה, אם אתה רושם נתונים אישיים של מישהו והם איכשהו ידלפו, יהיו בעיות אמיתיות, במיוחד בפרויקטים המתמקדים בשווקים מערביים. אבל יש גם דברים שאתה בהחלט צריך לרשום :
  1. התחלה/סיום של האפליקציה. עלינו לדעת אם היישום באמת התחיל והסתיים כמצופה.
  2. סוגיות אבטחה. כאן זה יהיה טוב לרשום ניסיונות לנחש את הסיסמה של מישהו, מקרים שבהם מנהלי מערכת נכנסים וכו'.
  3. מדינות יישום מסוימות . למשל, המעבר ממדינה אחת לאחרת בתהליך עסקי.
  4. מידע על ניפוי באגים מסוים יחד עם רמת היומן המתאימה.
  5. סקריפטים מסוימים של SQL. ישנם מקרים בעולם האמיתי שבהם זה הכרחי. אבל שוב, על ידי התאמה מיומנת של רמות היומן, אתה יכול להשיג תוצאות מצוינות.
  6. ניתן לרשום שרשורים פועלים בעת אימות שהדברים פועלים כהלכה.

שגיאות פופולריות ברישום

יש כאן ניואנסים רבים, אך נזכיר במיוחד כמה טעויות נפוצות:
  1. רישום מוגזם. אתה לא צריך לרשום כל שלב שיכול להיות חשוב תיאורטית. הנה כלל אצבע טוב: יומנים לא יעלו על 10% מהעומס. אחרת, יהיו בעיות ביצועים.
  2. רישום כל הנתונים לקובץ אחד. בשלב מסוים, זה יקשה מאוד על קריאת/כתיבת היומן, שלא לדבר על העובדה שלמערכות מסוימות יש מגבלות על גודל הקובץ.
  3. שימוש ברמות יומן שגויות. לכל רמת יומן יש גבולות ברורים, ויש לכבד אותם. אם הגבול אינו ברור, ניתן להגיע להסכמה באיזו רמה להשתמש.

יומן רמות

x: גלוי
קָטלָנִי שְׁגִיאָה לְהַזהִיר מידע לנפות זֵכֶר את כל
כבוי
קָטלָנִי איקס
שְׁגִיאָה איקס איקס
לְהַזהִיר איקס איקס איקס
מידע איקס איקס איקס איקס
לנפות איקס איקס איקס איקס איקס
זֵכֶר איקס איקס איקס איקס איקס איקס
את כל איקס איקס איקס איקס איקס איקס איקס
מהן רמות יומן? על מנת ליצור איכשהו היררכיה של ערכי יומן, יש צורך במוסכמות ותיחום מסוימות. זו הסיבה שהוצגו רמות יומן. הרמה מוגדרת באפליקציה. אם ערך הוא מתחת לרמה שצוינה, אז הוא לא נרשם. לדוגמה, יש לנו יומנים שבהם אנו משתמשים בעת איתור באגים באפליקציה. במהלך פעולה רגילה (כאשר האפליקציה משמשת למטרה שלה), אין צורך ביומנים כאלה. לכן, רמת היומן גבוהה יותר מאשר עבור ניפוי באגים. בואו נסתכל על רמות יומן באמצעות Log4j. מלבד JUL, פתרונות אחרים משתמשים באותן רמות יומן. הנה הם בסדר יורד:
  • כבוי: לא נרשמות רשומות ביומן; מתעלמים מהכל.
  • FATAL: שגיאה המונעת מהאפליקציה להמשיך לפעול. לדוגמה, "שגיאת זיכרון חסר ב-JVM".
  • שגיאה: שגיאות ברמה זו מצביעות על בעיות שיש לפתור. השגיאה אינה עוצרת את היישום בכללותו. בקשות אחרות עשויות לפעול כהלכה.
  • אזהרה: רשומות יומן המייצגות אזהרה. משהו לא צפוי קרה, אבל המערכת הצליחה להתמודד ומילאה את הבקשה
  • INFO: רשומות יומן המצביעות על פעולות חשובות באפליקציה. אלו לא שגיאות או אזהרות. הם אירועי מערכת צפויים.
  • ניפוי באגים: ערכי יומן צריכים לנפות באגים באפליקציה. על מנת לוודא שהאפליקציה עושה בדיוק את המצופה, או על מנת לתאר את הפעולות שמבצעת האפליקציה, כלומר "הוזן שיטה1".
  • TRACE: ערכי יומן בעדיפות נמוכה יותר עבור ניפוי באגים. רמת היומן הנמוכה ביותר.
  • ALL: רמת יומן לכתיבת כל ערכי היומן של היישום.
ברמת יומן INFO מופעלת איפשהו באפליקציה, ואז הערכים עבור כל רמה יירשמו, מ-INFO ועד FATAL. אם רמת היומן FATAL מוגדרת, ייכתבו רק רשומות יומן עם רמה זו.

רישום ושליחת יומנים: Appender

הבה נבחן כיצד כל זה עובד כאשר אנו משתמשים ב-Log4j, המספק הזדמנויות רבות לכתיבת/שליחת יומנים:
  • לכתוב לקובץ -DailyRollingFileAppender
  • לכתוב מידע לקונסולה -ConsoleAppender
  • לכתוב יומנים למסד נתונים -JDBCAppender
  • לנהל יומני שליחת דרך TCP/IP -TelnetAppender
  • כדי להבטיח שהרישום לא ישפיע לרעה על הביצועים -AsyncAppender
יש עוד כמה יישומים: רשימה מלאה זמינה כאן . אגב, אם התוספת שאתה צריך לא קיים, זו לא בעיה. אתה יכול לכתוב תוספת משלך על ידי יישום ממשק Appender , שבו Log4j תומך.

רישום צמתים

למטרות הדגמה, נשתמש בממשק Slf4j, עם מימוש מ-Log4j. יצירת לוגר היא פשוטה מאוד: במחלקה בשם MainDemo, שתבצע רישום מסוים, עלינו להוסיף את הדברים הבאים:
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
זה יצור עבורנו לוגר. כדי לבצע רישום יומן, ישנן מספר שיטות זמינות ששמותיהן משקפות איזו רמת יומן תשמש. לדוגמה:
logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
למרות שאנו עוברים את הכיתה, השם הסופי הוא השם המלא של הכיתה, כולל חבילות. זה נעשה כדי שבהמשך תוכל לחלק את הרישום לצמתים ולהגדיר את רמת הרישום והתוספת עבור כל צומת. לדוגמה, לוגר נוצר בכיתה com.github.romankh3.logginglecture.MainDemo. השם מספק את הבסיס ליצירת היררכיה של צמתי רישום. הצומת הראשי הוא RootLogger ברמה העליונה . זהו הצומת שמקבל את כל רשומות היומן עבור האפליקציה כולה. ניתן לתאר את הצמתים הנותרים כפי שמוצג להלן: רישום: מה, איך, איפה ועם מה?  - 3נספחים מוגדרים עבור צמתי רישום ספציפיים. כעת נסתכל על הקובץ log4j.properties כדי לראות דוגמה כיצד להגדיר אותם.

מדריך שלב אחר שלב לקובץ log4j.properties

נגדיר הכל צעד אחד בכל פעם ונראה מה אפשרי:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
שורה זו אומרת שאנו רושמים את התוספת CONSOLE, המשתמשת ביישום org.apache.log4j.ConsoleAppender. התוספת הזו כותבת מידע לקונסולה. לאחר מכן, אנו רושמים תוספת נוספת. זה יכתוב לקובץ:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
חשוב לציין שעדיין יש להגדיר את הנספחים עצמם. לאחר שרשמנו את הנספחים שלנו, נוכל לקבוע אילו רמות יומן ואלו נספחים ישמשו בצמתים.

log4j.rootLogger=ניקוי באגים, מסוף, קובץ

  • log4j.rootLogger אומר שאנו מגדירים את צומת השורש, המכיל את כל ערכי היומן
  • המילה הראשונה אחרי סימן השווה מציינת את רמת היומן המינימלית לכתיבה (במקרה שלנו, היא DEBUG)
  • לאחר הפסיק, אנו מציינים את כל הנספחים שיש להשתמש בהם.
כדי להגדיר צומת רישום ספציפי יותר, תשתמש בערך כזה:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
היכן log4j.logger.משמש להפניה לצומת ספציפי. במקרה שלנו, com.github.romankh3.logginglecture. עכשיו בואו נדבר על קביעת התצורה של התוספת של CONSOLE:
# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
כאן אנו רואים שניתן להגדיר את הרמה הספציפית שבה התוספתן יתחיל לעבוד. הנה דוגמה למה שקורה בפועל: נניח שהודעה עם רמת INFO מתקבלת על ידי צומת הרישום ומועברת לתוספת שהוקצה לה. אם הסף של התוספת מוגדר ל-WARN, אז הוא מקבל את רשומת היומן אבל לא עושה איתו כלום. לאחר מכן, עלינו להחליט באיזו פריסה תשתמש ההודעה. אני משתמש ב-PatternLayout בדוגמה, אבל יש הרבה אפשרויות אחרות. לא נעסוק בהם במאמר זה. דוגמה להגדרת התוספת FILE:
# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
אתה יכול להגדיר את הקובץ הספציפי שאליו ייכתבו רשומות יומן, כפי שניתן לראות מהשורה הזו:
log4j.appender.FILE.File=./target/logging/logging.log
הערך נכתב לקובץ logging.log. כדי למנוע בעיות בגודל הקובץ, אתה יכול להגדיר את המקסימום, שבמקרה זה הוא 1MB. MaxBackupIndexמציין כמה קבצי יומן כאלה יהיו. אם אנחנו צריכים ליצור יותר קבצים מזה, אז הקובץ הראשון יימחק. כדי להסתכל על דוגמה אמיתית שבה מוגדר רישום, אתה יכול ללכת למאגר הציבורי ב- GitHub.

חזקו את מה שדיברנו עליו

נסה בעצמך לעשות את כל מה שתיארנו:
  • צור פרויקט משלך בדומה לדוגמא שלנו למעלה.
  • אם אתה יודע איך להשתמש ב-Maven, השתמש בו. אם לא, קרא את המדריך הזה , שמתאר כיצד לחבר את הספרייה.

לסיכום

  1. דיברנו על פתרונות הרישום הקיימים בג'אווה.
  2. כמעט כל ספריות הרישום הידועות נכתבו על ידי אדם אחד :D
  3. למדנו מה צריך ומה אסור לרשום.
  4. גילינו את רמות היומן.
  5. התוודענו לצמתי רישום.
  6. בדקנו מה זה תוספת ולמה הוא מיועד.
  7. יצרנו קובץ log4j.proterties צעד אחר צעד.

חומרים נוספים

  1. CodeGym: שיעור לוגר
  2. Weekly Geekly: רישום Java. שלום עולם
  3. אימה קידוד: הבעיה ברישום
  4. YouTube: הבנת הגיהנום של ג'אווה רישום - היסודות. Java Logging Hell ואיך להתרחק ממנו
  5. Log4j: Appender
  6. Log4j: פריסה
ראה גם מאמר אחר שלי:
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION