העברת רישום ביומן לזמני ריצה של Java מדור שני

יש הבדלים מסוימים בין ההיבטים של הכניסה לזמני הריצה מהדור השני (Java 11 ומעלה) לבין זמני הריצה מהדור הראשון (Java 8). בשונה מסביבות זמן הריצה מהדור הראשון, בסביבות זמן הריצה מהדור השני, יומני האפליקציות לא מקובצים ביומני הבקשות ב-Logs Explorer. בנוסף, בסביבות זמן הריצה מהדור השני, App Engine כותב כל יומן אפליקציה שמשויך לבקשה כרשומה נפרדת ביומן ב-Cloud Logging.

במדריך הזה מוסבר איך אפשר להטמיע את אותן שיטות רישום ביומן שבהן השתמשתם באפליקציות של סביבת זמן ריצה מהדור הראשון, ולקבל תוצאות דומות מאוד של סינון ושל קורלציה של רישום ביומן כשמעבירים את האפליקציה ל-Java 11 ומעלה.

ההבדלים העיקריים

בטבלה הבאה מפורטים ההבדלים ברישום ביומן בין סביבות זמן ריצה מהדור הראשון לבין סביבות זמן ריצה מהדור השני:

סביבת זמן ריצה מהדור הראשון (Java 8) זמני ריצה מהדור השני (Java 11 ואילך)
יומני בקשות ואפליקציות ‫App Engine מטמיע את כל יומני האפליקציה ביומן הבקשות. יומני האפליקציה לא מוטמעים ביומן הבקשות ב-App Engine.
stdout ו-stderr יומנים שנכתבו אל stdout ו-stderr מוטמעים ביומן הבקשות של App Engine. ‫App Engine לא מטמיע יומנים שנכתבו ב-stdout וב-stderr.
יומני פלטפורמה של App Engine ‫App Engine לא פולט יומנים פנימיים של הפלטפורמה. ‫App Engine פולט יומני פלטפורמה פנימיים במהלך ההפעלה או ההשבתה של מופע עם שם היומן /var/log/google_init.log.
מכסה ל-Cloud Logging API כל בקשה תואמת לפעולת כתיבה אחת ב-Cloud Logging. כל רשומה ביומן האפליקציה שמשויכת לבקשה תואמת לפעולת כתיבה אחת ב-Cloud Logging. השימוש ב-Cloud Logging API גדל בסביבות זמן הריצה מהדור השני.

יומני בקשות ויומני אפליקציות

ההבדלים בין התנהגות הרישום ביומנים בסביבות ריצה מדור ראשון לבין סביבות ריצה מדור שני:

  • בזמן הריצה של הדור הראשון,‏ App Engine מטמיע את יומני האפליקציה בשדה protoPayload.line של יומני הבקשות. כדי לראות את יומני האפליקציה, צריך להרחיב בקשה ב-Logs Explorer.

    בתמונה הבאה מוצגים יומני בקשות ויומני אפליקציות עם קורלציה בסביבת זמן הריצה מהדור הראשון:

    יומנים עם קורלציה ב-Java 8

  • בזמני הריצה של הדור השני, יומני הבקשות לא מכילים רשומות של יומני האפליקציות כי השדה protoPayload.line לא מופיע ביומן הבקשות. במקום זאת, App Engine מתעד כל יומן אפליקציה כרשומה נפרדת. שם היומן תלוי בהגדרת הרישום שלכם. ‫var/log/app מכיל יומנים שהופקו באמצעות מסגרות רישום סטנדרטיות, כמו java.util.logging או Simple Logging Facade for Java (SLF4J). היומנים stderr ו-stdout מכילים יומני אפליקציות שנרשמים באמצעות System.err.print() או System.out.print().

    בתמונה הבאה מוצגים יומני בקשות ויומני אפליקציות נפרדים בסביבות זמן ריצה מהדור השני:

    הפרדה של יומנים ב-Java 11

stdout וגם stderr

בזמן הריצה של הדור הראשון, App Engine מחשיב את היומנים שמופקים אל stdout ו-stderr כיומני אפליקציה, ומקבץ באופן אוטומטי את הרשומות האלה של יומני האפליקציה תחת יומן הבקשות המשויך.

בזמני הריצה מהדור השני, מערכת App Engine לא מבצעת קורלציה בין היומנים שמופקים לבין stdout ו-stderr כברירת מחדל. כדי לקבץ ישויות של יומן האפליקציה עם יומן הבקשות באמצעות stdout ו-stderr, פועלים לפי ההוראות במאמר כתיבת יומנים מובנים אל stdout ו-stderr.

אנחנו לא ממליצים להתחבר ל-stdout ול-stderr כי יומנים מסוימים של פלטפורמת App Engine (JVM,‏ Jetty, יומנים של תשתית פנימית) מועברים גם ל-stderr. יומני האפליקציה ויומני הפלטפורמה מופיעים יחד עם שם היומן stderr, מה שגורם לאי-בהירות. הבעיה הזו חמורה יותר בסביבות ריצה מהדור השני, כי ב-App Engine יש נפח גדול יותר של יומני פלטפורמה.

יומנים של פלטפורמת App Engine

בזמן הריצה מהדור הראשון, App Engine לא פולט יומני פלטפורמה.

בזמני הריצה של הדור השני, App Engine פולט יומני פלטפורמה במהלך הפעלה או כיבוי של מופע עם שם היומן /var/log/google_init.log.

אפשר להתעלם מיומני הפלטפורמה. כדי להימנע מהצגת היומנים האלה, מוסיפים מסנן ל-Logs Explorer עם השאילתה logName="/var/log/google_init.log".

התמונה הבאה מציגה יומנים של פלטפורמה בסביבות זמן ריצה מהדור השני:

יומני פלטפורמה

מכסת Cloud Logging API

בזמן ריצה מהדור הראשון, App Engine מטמיע את כל יומני האפליקציה שנוצרו במהלך בקשה ביומן בקשות יחיד, ושולח את הבקשה המוטמעת אל Cloud Logging. כל בקשה מתאימה לכתיבה אחת ב-Cloud Logging.

בסביבות זמן הריצה מהדור השני, App Engine שולח כל יומן של אפליקציה ל-Cloud Logging ברשומות נפרדות ביומן, מה שגורם לעלייה במספר הכולל של פעולות הכתיבה ב-Cloud Logging לכל בקשה.

‫Google Cloud החיוב נקבע לפי נפח האחסון הכולל שמשמש את Cloud Logging. גם אם נפח האחסון הכולל שנדרש ליומנים לא גדל באופן משמעותי, מספר הפעולות של כתיבה לדקה גדל, בהתאם למספר יומני האפליקציות ש-App Engine כותב לכל בקשה. אם האפליקציה חורגת ממכסת הכתיבה של Cloud Logging, אפשר לבקש הגדלה של מכסת השימוש.

מידע נוסף זמין במאמר בנושא תמחור של Cloud Logging.

סקירה כללית של תהליך המיגרציה

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

אפשר לבחור אחת מהאפשרויות הבאות להגדרת רישום ביומן בסביבות זמן הריצה מהדור השני:

שימוש בחבילה java.util.logging (JUL)

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

בדוגמה הבאה אפשר לראות איך משתמשים בחבילת java.util.logging בסביבות זמן ריצה מהדור השני:

    package com.example.appengine.my_app;
    import java.io.IOException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.*;
    import java.util.logging.Logger;

    @WebServlet(name = "HelloAppEngine", value = "/")
    public class HelloAppEngine extends HttpServlet {

      // Use the java.util.logging.Logger to log messages
      private static final Logger logger = Logger.getLogger(HelloAppEngine.class.getName());

      @Override
      public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException {
        // Sample log messages
        logger.info("Info message");
        logger.warning("Warning message");
        logger.severe("Severe message");

        response.setContentType("text/plain");
        response.getWriter().println("Hello App Engine");
      }
    }

קיבוץ יומני אפליקציות עם יומן הבקשות

בזמני הריצה מהדור השני, השדה protoPayload.line ביומן הבקשות לא מכיל יומני אפליקציות. ב-Logs Explorer נעשה שימוש בשדה trace כדי לקבץ יומני בקשות ויומני אפליקציות. חבילת java.util.logging מוסיפה באופן אוטומטי את מזהה המעקב לכל הבקשות ולכל יומני האפליקציות. כדי להציג יומנים עם קורלציה ב-Logs Explorer, ראו הצגת יומנים עם קורלציה.

שימוש ב-Simple Logging Facade for Java‏ (SLF4J)

‫SLF4J הוא ממשק הרישום הפופולרי ביותר שמשמש באפליקציות Java. ממשק SLF4J הוא חזית, ולכן הוא לא תלוי בפלטפורמה של קצה העורפי של הרישום ביומן. כך אפשר לבחור כל קצה עורפי שמתאים לאפליקציות שלכם.

בתמונה הבאה מוצגות אפשרויות שילוב לשני עורפי קצה של SLF4J, כולל הספרייה java.util.logging וה-appender של Logback:

סקירה כללית של SLF4J

‫SLF4J עם חבילת java.util.logging

‫SLF4J מאפשר לשלב את האפליקציה עם Cloud Logging כשמשתמשים ב-SLF4J עם java.util.logging כקצה העורפי של רישום היומן. כברירת מחדל, App Engine מוסיף מזהה מעקב לכל הבקשות וליומני האפליקציות. המנגנון הזה מאפשר לקבץ את היומנים של הבקשות והאפליקציות ב-Logs Explorer.

כדי להטמיע את SLF4J עם java.util.logging, פועלים לפי השלבים הבאים:

  1. מוסיפים את התלות slf4j-jdk14.jar לקובץ pom.xml. קובץ slf4j-jdk14.jar מספק את שילוב ה-Backend לספריית java.util.logging:

    <!-- SLF4J interface -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>2.0.4</version>
    </dependency>
    <!-- JUL implementation for SLF4J -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
      <version>2.0.9</version>
    </dependency>
    
  2. יוצרים קובץ WEB-INF/logging.properties כדי להוסיף הגדרות בהתאמה אישית:

    com.example.appengine.java8.HelloAppEngine.level = INFO
    
  3. מעדכנים את הקובץ appengine-web.xml ומוסיפים את הערך <property> בשביל WEB-INF/logging.properties:

    <system-properties>
      <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
    </system-properties>
    

בתמונה הבאה אפשר לראות איך SLF4J עם java.util.logging מוסיף אוטומטית את מזהה המעקב ליומן של אפליקציה:

קיבוץ של יומני אפליקציות לפי בקשה עם מזהה מעקב

כדי לשנות את התצוגה ב-Logs Explorer לתצוגה ממוקדת בבקשות כמו בדור הראשון של זמן הריצה, אפשר לעיין במאמר בנושא הצגת יומנים עם קורלציה.

שימוש ב-SLF4J עם Logback appender

אם אפליקציות זמן הריצה מהדור הראשון משתמשות בהטמעה המובנית של SLF4J עם Logback, צריך לעדכן את קוד המקור ולבצע שלבים נוספים כדי לקבץ את בקשות היומן ואת יומני האפליקציה.

כדי לשלב את האפליקציה עם Cloud Logging, פועלים לפי השלבים הבאים:

  1. כדי להתקין את כלי ה-appender לרישום ב-Logback, מוסיפים את כלי ה-appender‏ google-cloud-logging-logback לקובץ pom.xml:

     <!-- SLF4J interface -->
     <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>2.0.4</version>
     </dependency>
    
     <!-- Logback JARs -->
     <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-classic</artifactId>
           <version>1.3.6</version>
     </dependency>
     <dependency>
           <groupId>ch.qos.logback</groupId>
           <artifactId>logback-core</artifactId>
           <version>1.3.5</version>
     </dependency>
    
     <!-- Google Cloud logging appender for Logback -->
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-logging-logback</artifactId>
     </dependency>
    
  2. יוצרים כלי לשיפור הרישום ביומן כדי להוסיף מזהה מעקב לכל שדה LogEntry. המחלקות הבאות TraceIdLoggingEnhancer משתמשות ב-ApiProxy API כדי לאחזר את מזהה המעקב שמשויך לבקשה:

      import com.google.appengine.api.utils.SystemProperty;
      import com.google.cloud.logging.LogEntry;
      import com.google.cloud.logging.LoggingEnhancer;
    
      import com.google.apphosting.api.ApiProxy;
      import com.google.apphosting.api.ApiProxy.Environment;
    
      // Add trace ID to the log entry
      public class TraceIdLoggingEnhancer implements LoggingEnhancer {
    
        @Override
        public void enhanceLogEntry(LogEntry.Builder logEntry) {
          final String PROJECT_ID = SystemProperty.applicationId.get();
    
          Environment environment = ApiProxy.getCurrentEnvironment();
    
          if (environment instanceof ApiProxy.EnvironmentWithTrace) {
            ApiProxy.EnvironmentWithTrace environmentWithTrace = (ApiProxy.EnvironmentWithTrace) environment;
            environmentWithTrace
                .getTraceId()
                .ifPresent(
                    id ->
                      logEntry.setTrace(String.format("projects/%s/traces/%s", PROJECT_ID, id)));
          }
        }
      }
      // [END logging_enhancer]
    
  3. מוסיפים את ההגדרה של Cloud Logging appender בקובץ logback.xml כדי להגדיר את Logback. הגדרות מנוהלות באמצעות קובץ logback.xml ב-WEB-INF/classes:

    <configuration>
      <appender name="CLOUD" class="com.google.cloud.logging.logback.LoggingAppender">
          <!-- This should be set to the new Logging Enhancer in the app code. -->
          <enhancer>com.example.appengine.my_app.enhancers.TraceIdLoggingEnhancer</enhancer>
          <resourceType>gae_app</resourceType>
      </appender>
      <root level="info">
          <appender-ref ref="CLOUD" />
      </root>
    </configuration>
    

    בתמונה הבאה מוצג המבנה הסופי של תיקיית האפליקציה:

    מבנה הספרייה

צפייה ביומנים עם קורלציה

אפשר לראות יומנים עם קורלציה ב-Logs Explorer באמצעות השדה של מזהה המעקב בכל רשומה ביומן. כדי לראות יומנים עם קורלציה ב-Logs Explorer:

  1. בחלונית הניווט של מסוף Google Cloud , בוחרים באפשרות Logging ואז באפשרות Logs Explorer:

    כניסה לדף Logs Explorer

  2. בקטע Resource Type, בוחרים באפשרות GAE Application.

  3. כדי להציג יומני בקשות ולבצע ביניהם קורלציה, בוחרים באפשרות request_log בשם היומן. לחלופין, כדי לבצע קורלציה לפי יומני בקשות, לוחצים על Correlate by (ביצוע קורלציה לפי) ובוחרים באפשרות request_log (יומן בקשות):

    הצלבת יומנים

  4. בחלונית Query results, כדי להרחיב רשומה ביומן, לוחצים על Expand. כשמרחיבים את יומן הבקשות, מוצגים יומני האפליקציות שמשויכים אליו.