Logging zu Java-Laufzeiten der zweiten Generation migrieren

Einige Aspekte des Loggings in den Laufzeiten der zweiten Generation (Java 11+) unterscheiden sich von der Laufzeit der ersten Generation (Java 8). Im Gegensatz zur Laufzeit der ersten Generation werden Anwendungslogs in den Laufzeiten der zweiten Generation nicht im Log-Explorer unter Anfragelogs gruppiert. Darüber hinaus schreibt App Engine in den Laufzeiten der zweiten Generation jedes mit einer Anfrage verknüpfte Anwendungslog als separaten Logeintrag in Cloud Logging.

In diesem Leitfaden wird beschrieben, wie Sie die Logging-Methoden implementieren können, die Sie in Ihren Laufzeitanwendungen der ersten Generation verwendet haben. Sie erhalten dann bei der Migration Ihrer Anwendung zu Java 11+ fast dieselben Filter- und Logging-Korrelationsergebnisse.

Wichtige Unterschiede

In der folgenden Tabelle werden die Unterschiede beim Logging zwischen Laufzeiten der ersten und zweiten Generation behandelt:

Laufzeit der ersten Generation (Java 8) Laufzeiten der zweiten Generation (Java 11 oder höher)
Anfrage- und Anwendungslogs (auch Anwendungslogs genannt) App Engine bettet alle Anwendungslogs in das Anfragelog ein. App Engine bettet keine Anwendungslogs in das zugehörige Anfragelog ein.
stdout und stderr App Engine bettet Logs, die in stdout und stderr geschrieben wurden, in das Anfragelog ein. In App Engine werden keine Logs eingebettet, die in stdout und stderr geschrieben wurden.
App Engine-Plattformlogs App Engine gibt keine internen Plattformlogs aus. App Engine gibt beim Starten oder Herunterfahren einer Instanz mit dem Lognamen /var/log/google_init.log interne Plattformlogs aus.
Kontingent für die Cloud Logging API Jede Anfrage entspricht einem Cloud Logging-Schreibvorgang. Jeder mit einer Anfrage verknüpfte Anwendungslogeintrag entspricht einem Cloud Logging-Schreibvorgang. Die Cloud Logging API-Nutzung steigt in den Laufzeiten der zweiten Generation an.

Logs und Anwendungslogs anfordern

Das Logging-Verhalten in Laufzeiten der ersten und zweiten Generation unterscheidet sich in folgender Weise:

  • In der Laufzeit der ersten Generation bettet App Engine Anwendungslogs in das Feld protoPayload.line der Anfragelogs ein. Maximieren Sie eine Anfrage im Log-Explorer, um Anwendungslogs aufzurufen.

    Die folgende Abbildung zeigt korrelierte Anfrage- und Anwendungslogs in den Laufzeiten der ersten Generation:

    Korrelierte Logs in Java 8

  • In den Laufzeiten der zweiten Generation enthalten Anfragelogs keine Anwendungslogeinträge, da das Feld protoPayload.line im Anfragelog fehlt. Stattdessen protokolliert App Engine jedes Anwendungslog als separaten Eintrag. Der Logname hängt von der Logging-Einrichtung ab. var/log/app enthält Logs, die mit Standard-Logging-Frameworks wie java.util.logging oder der Simple Logging Facade für Java (SLF4J) ausgegeben werden. Die Logs stderr und stdout enthalten Anwendungslogs, die mit System.err.print() oder System.out.print() protokolliert werden.

    Die folgende Abbildung zeigt separate Anfrage- und Anwendungslogs in den Laufzeiten der zweiten Generation:

    Separate Logs in Java 11

stdout und stderr

In der Laufzeit der ersten Generation betrachtet App Engine Logs, die an stdout und stderr ausgegeben werden, als Anwendungslogs und gruppiert diese Anwendungslogeinträge automatisch im zugehörigen Anfragelog.

In den Laufzeiten der zweiten Generation korreliert App Engine keine Logs, die standardmäßig an stdout und stderr ausgegeben werden. Folgen Sie der Anleitung unter Strukturierte Logs in stdout und stderr schreiben, um Anwendungslogentitäten mit dem Anfragelog mithilfe von stdout und stderr zu gruppieren. “

Wir empfehlen, das Logging in stdout und stderr nicht, da bestimmte App Engine-Plattformlogs (JVM, Jetty, interne Infrastrukturlogs) ebenfalls an stderr ausgegeben werden. Anwendungs- und Plattformlogs werden zusammen mit dem Lognamen stderr angezeigt, was zu Mehrdeutigkeit führt. Dies ist in Laufzeiten der zweiten Generation übertrieben, da App Engine ein größeres Volumen an Plattformlogs hat.

App Engine-Plattformlogs

In der Laufzeit der ersten Generation gibt App Engine keine Plattformlogs aus.

In den Laufzeiten der zweiten Generation gibt App Engine beim Starten oder Herunterfahren einer Instanz Plattformlogs mit dem Lognamen /var/log/google_init.log aus.

Sie können die Plattformlogs ignorieren. Fügen Sie dem Log-Explorer einen Filter mit der Abfrage logName="/var/log/google_init.log" hinzu, um diese Logs zu vermeiden.

Die folgende Abbildung zeigt Plattformlogs in den Laufzeiten der zweiten Generation:

Plattform-Logs

Kontingente für die Cloud Logging API

In der Laufzeit der ersten Generation bettet App Engine alle Anwendungslogs ein, die während einer Anfrage ausgegeben werden, in ein einzelnes Anfragelog und sendet die eingebettete Anfrage an Cloud Logging. Jede Anfrage entspricht einem Cloud Logging-Schreibvorgang.

In den Laufzeiten der zweiten Generation sendet App Engine jedes Anwendungslog in separaten Logeinträgen an Cloud Logging, was zu einer Erhöhung der Gesamtzahl der Cloud Logging-Schreibvorgänge pro Anfrage führt.

Die Abrechnung in Google Cloud basiert auf dem Gesamtspeicher, den Cloud Logging verwendet. Auch wenn der für Logs benötigte Gesamtspeicher nicht erheblich zunimmt, steigen die Schreibvorgänge pro Minute, je nachdem, wie viele Anwendungslogs App Engine pro Anfrage schreibt. Wenn Ihre Anwendung das Schreibkontingent von Cloud Logging überschreitet, können Sie eine Erhöhung Ihres Nutzungskontingents beantragen.

Weitere Informationen finden Sie unter pricing.

Übersicht über den Migrationsprozess

Auch wenn Anwendungslogs nicht in die Anfragelogs der Laufzeiten der zweiten Generation eingebettet sind, können Sie die Logs wie bei Anwendungen der ersten Generation beibehalten.

Sie können eine der folgenden Optionen auswählen, um das Logging in Laufzeiten der zweiten Generation einzurichten:

Das Paket java.util.logging (JUL) verwenden

Wenn Ihre Anwendungen, die die Laufzeit der ersten Generation verwenden, das Paket java.util.logging für alle Logging-Anforderungen implementieren, sind für die Migration zu Laufzeiten der zweiten Generation keine Codeänderungen erforderlich.

Das folgende Beispiel zeigt, wie Sie das Paket java.util.logging zum Logging in Laufzeiten der zweiten Generation verwenden können:

    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");
      }
    }

Anwendungslogs mit dem Anfragelog gruppieren

In den Laufzeiten der zweiten Generation enthält das Feld protoPayload.line im Anfragelog keine Anwendungslogs. Der Log-Explorer verwendet das Feld trace, um Anfragelogs und Anwendungslogs zu gruppieren. Das Paket java.util.logging fügt die Trace-ID automatisch allen Anfrage- und Anwendungslogs hinzu. Informationen zum Anzeigen korrelierter Logs im Log-Explorer finden Sie unter Korrelierte Logs ansehen.

Einfache Logging-Fassade für Java (SLF4J) verwenden

SLF4J ist die gängigste Logging-Schnittstelle, die in Java-Anwendungen verwendet wird. Da es sich um eine Fassade handelt, ist die SLF4J-Schnittstelle plattformunabhängig von ihrem Logging-Back-End, sodass Sie jedes für Ihre Anwendungen geeignete Back-End auswählen können.

Die folgende Abbildung zeigt Integrationsoptionen für zwei SLF4J-Back-Ends, einschließlich der Bibliothek java.util.logging und des Logback-Appenders:

SLF4J-Übersicht

SLF4J mit java.util.logging-Paket

Mit SLF4J können Sie Ihre Anwendung in Cloud Logging einbinden, wenn Sie SLF4J mit java.util.logging als Logging-Backend verwenden. App Engine fügt standardmäßig allen Anfrage- und Anwendungslogs eine Trace-ID hinzu. Dieser Mechanismus ermöglicht die Gruppierung von Anfrage- und Anwendungslogs im Log-Explorer.

So implementieren Sie SLF4J mit java.util.logging:

  1. Fügen Sie der Datei pom.xml die Abhängigkeit slf4j-jdk14.jar hinzu. Die Datei slf4j-jdk14.jar stellt die Back-End-Integration für die Bibliothek java.util.logging bereit:

    <!-- 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. Erstellen Sie eine WEB-INF/logging.properties-Datei, um benutzerdefinierte Konfigurationen hinzuzufügen:

    com.example.appengine.java8.HelloAppEngine.level = INFO
    
  3. Aktualisieren Sie die Datei appengine-web.xml so, dass sie den Wert <property> für WEB-INF/logging.properties enthält:

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

Die folgende Abbildung zeigt, wie SLF4J mit java.util.logging die Trace-ID automatisch zu einem Anwendungslog hinzufügt:

Anwendungslogs nach Anfrage mit Trace-ID gruppieren

Informationen zum Optimieren der Log-Explorer-Ansicht auf eine anfrageorientierte Ansicht wie die Laufzeit der ersten Generation finden Sie unter Korrelierte Logs ansehen.

SLF4J mit Logback-Appender verwenden

Wenn Ihre Laufzeitanwendungen der ersten Generation die integrierte Implementierung von SLF4J mit Logback verwenden, müssen Sie Ihren Quellcode aktualisieren und zusätzliche Schritte zum Gruppieren von Anfrage- und Anwendungslogs implementieren.

So binden Sie Ihre Anwendung in Cloud Logging ein:

  1. Fügen Sie den Appender google-cloud-logging-logback zur Datei pom.xml hinzu, um den Logging-Appender für Logback zu installieren:

     <!-- 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. Erstellen Sie eine Logging-Optimierung, um jedem LogEntry-Feld eine Trace-ID hinzuzufügen. Die folgende TraceIdLoggingEnhancer-Klasse verwendet die ApiProxy API, um die mit einer Anfrage verknüpfte Trace-ID abzurufen:

      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. Fügen Sie die Cloud Logging-Appender-Konfiguration in die Datei logback.xml ein, um das Logback zu konfigurieren. Das Logback-Framework verwaltet die Konfiguration über die Datei logback.xml in 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>
    

    Die folgende Abbildung zeigt die endgültige Struktur des Anwendungsverzeichnisses:

    Verzeichnisstruktur

Korrelierte Logs ansehen

Sie können korrelierte Logs im Log-Explorer mithilfe des Trace-ID-Felds in jedem Logeintrag aufrufen. So rufen Sie korrelierte Logs im Log-Explorer auf:

  1. Wählen Sie im Navigationsbereich der Google Cloud Console Logging und anschließend Log-Explorer aus:

    Zum Log-Explorer

  2. Wählen Sie unter Ressourcentyp die Option GAE-Anwendung aus.

  3. Wählen Sie unter Logname die Option request_log aus, um Anfragelogs aufzurufen und zu korrelieren. Wenn Sie nach Anfragelogs korrelieren möchten, klicken Sie alternativ auf Korrelieren nach und wählen Sie request_log aus.

    Logs korrelieren

  4. Klicken Sie im Bereich Abfrageergebnisse auf Maximieren, um einen Logeintrag zu maximieren. Beim Maximieren werden in jedem Anfragelog die zugehörigen Anwendungslogs angezeigt.