Fehlerbehebung bei Cloud-Clientbibliotheken für Java

In diesem Dokument finden Sie eine Übersicht zur Protokollierung und zur Fehlerbehebung bei häufigen Problemen mit Cloud-Clientbibliotheken für Java.

Protokollierung

Es gibt zwei Möglichkeiten, die Protokollierung für die Clientbibliotheken zu aktivieren: mit java.util.logging oder mit der Debug-Protokollierung der Clientbibliothek mit SLF4J.

java.util.logging verwenden

Cloud-Clientbibliotheken für Java verwenden das Paket java.util.logging der Java Logging API. Wenn Sie die Protokollebene konfigurieren, erhalten Sie Informationen, die bei der Fehlerbehebung helfen, darunter:

  • Zeitpunkt der zugrunde liegenden Client-Server-Kommunikation
  • Header von Anfrage- und Antwortnachrichten
  • Ausführliche Nachrichten in zugrunde liegenden Abhängigkeitsbibliotheken

Wenn Sie die ausführliche Protokollierung für Cloud-Clientbibliotheken für Java schnell aktivieren möchten, erstellen Sie eine Datei logging.properties mit folgendem Inhalt:

# run java program pointing to this properties file with the java arg
#   -Djava.util.logging.config.file=path/to/logging.properties
handlers=java.util.logging.ConsoleHandler
java.util.logging.SimpleFormatter.format=%1$tF %1$tT,%1$tL %4$-8s %3$-50s - %5$s %6$s%n

# --- ConsoleHandler ---
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
.level=INFO

# --- Specify logging level for certain packages ---
# com.google.api is for HTTP 1.1 layer
com.google.api.level=ALL
# io.grpc is for gRPC + Netty layer
io.grpc.level=FINE
# com.google.auth is for authentication
com.google.auth.level=FINE

# Example when you specify the storage library's level. This works when
# the target Cloud library uses the logging API.
com.google.cloud.storage.level=INFO

Führen Sie dann Ihre Anwendung mit -Djava.util.logging.config.file=path/to/logging.properties als VM argument und nicht als Program argument aus.

Wenn Sie IntelliJ verwenden, geben Sie das VM-Argument unter Run/Debug Configuration an:

IntelliJ-Konfiguration für Ausführung/Fehlerbehebung, in der VM-Argumente angegeben werden

Wenn die JVM Ihres Programms mit der Konfiguration korrekt ausgeführt wird, sehen Sie die Protokollierung auf der Ebene FINE in der Konsole.

Beispielausgabe:

2023-04-05 13:03:01,761 FINE     com.google.auth.oauth2.DefaultCredentialsProvider  - Attempting to load credentials from well known file: /usr/local/google/home/suztomo/.config/gcloud/application_default_credentials.json
2023-04-05 13:03:01,847 FINE     io.grpc.ManagedChannelRegistry                     - Unable to find OkHttpChannelProvider
java.lang.ClassNotFoundException: io.grpc.okhttp.OkHttpChannelProvider
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:315)
...

Dies ist eine Möglichkeit, die Protokollebene zu konfigurieren. Weitere Informationen zur Verwendung der Java Logging API finden Sie unter Java Logging Overview.

Debug-Protokollierung der Clientbibliothek (SLF4J)

Cloud-Clientbibliotheken bieten eine optionale Debug-Protokollierung, mit der Sie die Integration Ihrer Anwendung in die API beheben können. Wenn die Protokollierung aktiviert ist, werden wichtige Ereignisse wie Anfragen und Antworten sowie Daten-Nutzlasten und Metadaten wie Header in den Standardfehlerstream protokolliert.

WARNUNG:Bei der Debug-Protokollierung der Clientbibliothek werden Ihre Daten-Nutzlasten im Klartext angegeben. Dazu können vertrauliche Daten wie personenbezogene Daten für Sie oder Ihre Kunden, private Schlüssel oder andere Sicherheitsdaten gehören, die bei einem Datenleck kompromittiert werden könnten. Achten Sie immer auf eine gute Datenhygiene bei Ihren Anwendungsprotokollen und folgen Sie dem Prinzip der geringsten Berechtigung. Google empfiehlt außerdem, die Debug-Protokollierung der Clientbibliothek nur vorübergehend während der aktiven Fehlerbehebung zu aktivieren und sie nicht dauerhaft in der Produktion zu verwenden.

Voraussetzungen

Unsere Bibliotheken unterstützen die Debug-Protokollierung über die SLF4J Schnittstelle.

Die Clientbibliotheken und die Bibliotheken-BOM enthalten keine slf4j-api-Abhängigkeit. Sie müssen Protokollierungsabhängigkeiten einschließlich SLF4J und entsprechende Protokollierungsimplementierungen und ‑konfigurationen einrichten, bevor Sie die Debug-Protokollierung der Clientbibliothek aktivieren.

NOTE: Sie benötigen SLF4J und entsprechende Protokollierungsanbieter wie logback, log4j2 usw. in Ihrem Klassenpfad, um die Debug-Protokollierung der Clientbibliothek zu verwenden. Andernfalls erfolgt keine Debug-Protokollierung, auch wenn Sie sie über die Umgebungsvariable aktivieren.

Protokollierung mit einer Umgebungsvariablen aktivieren

Die Debug-Protokollierung ist standardmäßig deaktiviert. Sie wird nicht aktiviert, es sei denn, Sie aktivieren sie explizit, auch wenn die Voraussetzungen erfüllt sind.

Sie können die Debug-Protokollierung der Clientbibliothek aktivieren, indem Sie die Umgebungsvariable GOOGLE_SDK_JAVA_LOGGING auf true setzen. Die Groß-/Kleinschreibung wird dabei nicht berücksichtigt. Wenn die Variable nicht festgelegt ist oder einen anderen Wert hat, ist die Debug-Protokollierung der Clientbibliothek deaktiviert.

Beispiele für Protokollierungseinrichtungen

Die Dokumentation enthält ein Beispiel für die Debug-Protokollierung mit Logback.

Fügen Sie Ihrer Anwendung Logback-Abhängigkeiten hinzu. Dadurch wird die slf4j-Abhängigkeit transitiv eingeführt. Nutzer sollten die Logback-Einrichtung für die neuesten Versionen verwenden. Sie können diesen Schritt überspringen, wenn Ihre Anwendung bereits Logback-Abhängigkeiten im Klassenpfad hat.

Wenn Sie Maven verwenden:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>LATEST_VERSION</version>
</dependency>

Fügen Sie den Inhalt der Logback-Konfigurationsdatei hinzu, wenn Sie Logs auf DEBUG-Ebene sehen möchten. Sie können diesen Schritt überspringen, wenn Sie nur Logs auf INFO-Ebene benötigen. Weitere Informationen finden Sie unter Logback-Konfiguration.

<!-- set Client Library log level to DEBUG -->
<logger name="com.google.api"  level="DEBUG"/>
<!-- set Auth Library log level to DEBUG -->
<logger name="com.google.auth" level="DEBUG"/>

ALPN ist nicht richtig konfiguriert

Wenn Sie Ausnahmen im Zusammenhang mit ALPN is not configured properly sehen, z. B.:

Caused by: java.lang.IllegalArgumentException: ALPN is not configured properly. See https://github.com/grpc/grpc-java/blob/master/SECURITY.md#troubleshooting for more information.

Mit dem Kompatibilitätscheck können Sie prüfen, ob Ihre Umgebung mit gRPC-basierten Clients kompatibel ist.

Eine Inkompatibilität kann Folgendes bedeuten:

Fehlerbehebung bei ClassNotFoundException, NoSuchMethodError und NoClassDefFoundError

Diese Fehler werden häufig dadurch verursacht, dass mehrere Versionen oder inkompatible Versionen derselben Abhängigkeit im Klassenpfad vorhanden sind. Diese Abhängigkeitskonflikte treten häufig bei guava oder protobuf-java auf.

Klassenpfadkonflikte können mehrere Ursachen haben:

  • Mehrere Versionen derselben transitiven Abhängigkeit im Abhängigkeitsbaum.
  • Ihr Laufzeitklassenpfad enthält andere Versionen von Abhängigkeiten als die, die Sie im Build angegeben haben.

Wenn Sie beispielsweise eine direkte oder transitive Abhängigkeit von Guava Version 19.0 haben und google-cloud-java Guava Version 30.0 verwendet, verwendet google-cloud-java möglicherweise Guava-Methoden, die in Guava 19.0 nicht vorhanden sind, und verursacht NoSuchMethodError.

Wenn Ihr Klassenpfad eine ältere Version von protobuf-java enthält, google-cloud-java aber eine neuere Version erfordert, wird möglicherweise NoClassDefFoundError angezeigt, wodurch die Initialisierung von google-cloud-java-Klassen fehlschlägt.

Beispiel:

java.lang.NoClassDefFoundError: Could not initialize class com.google.pubsub.v1.PubsubMessage$AttributesDefaultEntryHolder

Konflikt validieren

Prüfen Sie den Abhängigkeitsbaum, um festzustellen, ob mehrere Versionen derselben Abhängigkeiten vorhanden sind:

$ mvn dependency:tree

Suchen Sie nach Versionen potenziell inkompatibler Abhängigkeiten wie guava oder protobuf-java.

Wenn der Fehler nur zur Laufzeit auftritt, führt Ihre Laufzeitumgebung möglicherweise inkompatible JARs in Ihren Laufzeitklassenpfad ein. Ein typischer Fall ist, dass Hadoop, Spark oder andere Serversoftware, auf der Ihre Anwendung ausgeführt wird, inkompatible Versionen von netty-, guava- oder protobuf-java-JARs im Klassenpfad hat.

Konflikte während des Builds erkennen

Wenn Sie Fehler bei der Abhängigkeitsverknüpfung zur Kompilierzeit erkennen möchten, fügen Sie die Linkage Checker Enforcer Rule in Ihre pom.xml ein:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>3.0.0-M3</version>
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>linkage-checker-enforcer-rules</artifactId>
            <version>1.5.7</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>enforce-linkage-checker</id>
            <!-- Important! Should run after compile -->
            <phase>verify</phase>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <LinkageCheckerRule
                    implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule"/>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>

Es gibt jedoch keine Möglichkeit, Konflikte im Laufzeitklassenpfad zu erkennen. Sie müssen genau wissen, welche JARs/Klassen im Laufzeitklassenpfad enthalten sind, da sich jede Serverumgebung unterscheidet.

Konflikt beheben

Es gibt verschiedene Strategien zur Behebung von Konflikten. Sie müssen jedoch die Ursache der Konflikte kennen:

  • Wenn Sie den Abhängigkeitsbaum steuern, führen Sie ein Upgrade der inkompatiblen Abhängigkeiten durch (z. B. ein Upgrade von Guava). Dieser Ansatz ist der robusteste, erfordert aber möglicherweise erheblichen Aufwand und mehrere Bibliotheksversionen, um die Kompatibilität zu gewährleisten.
  • Wenn Sie keine neuen Versionen Ihrer Abhängigkeiten ändern und bereitstellen können, importieren Sie com.google.cloud:libraries-bom:25.1.0 (oder eine neuere Version), um konsistente Abhängigkeitsversionen auszuwählen. Dieser Ansatz vereinfacht die Abhängigkeitsverwaltung. So können Sie beispielsweise von konsistenten Versionen von Guava und com.google.cloud:google-cloud-storage abhängig sein, ohne die Version für eine der beiden explizit festzulegen:
  ...
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>libraries-bom</artifactId>
        <version>25.1.0</version>
        <type>pom</type>
        <scope>import</scope>
       </dependency>
     </dependencies>
  </dependencyManagement>
  ...
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-storage</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
    </dependency>
    ...
  </dependencies>
  ...
  • In den Versionshinweisen zu libraries-bom sind die kompatiblen Abhängigkeitsbibliotheken aufgeführt. Beispiel: https://github.com/googleapis/java-cloud-bom/releases/tag/v26.31.0 zeigt:

    These client libraries are built with the following Java libraries:
    
    - Guava: 32.1.3-jre
    - Protobuf Java: 3.25.2
    - Google Auth Library: 1.22.0
    - Google API Client: 2.2.0
    - gRPC: 1.61.0
    - GAX: 2.41.0
    - Google Cloud Core: 2.31.0
    

    Wenn Sie den Abhängigkeitsgraphen Ihres Projekts untersuchen (mvn dependency:tree -Dverbose, gradle dependencies oder sbt dependencyTree), stellen Sie möglicherweise fest, dass einige Abhängigkeiten unerwartete Versionen haben, was zu Abhängigkeitskonflikten führen kann.

  • Wenn das Ändern von Abhängigkeitsversionen andere Fehler verursacht, sollten Sie Abhängigkeiten per Shading einbinden , die mit Google Cloud Java-Bibliotheken in Konflikt stehen.

    So binden Sie beispielsweise guava und protobuf-java per Shading ein:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>...</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope>
              <relocations>
                <!-- move protobuf to a shaded package -->
                <relocation>
                  <pattern>com.google.protobuf</pattern>
                  <shadedPattern>myapp.shaded.com.google.protobuf</shadedPattern>
                </relocation>
                <!-- move Guava to a shaded package -->
                <relocation>
                  <pattern>com.google.common</pattern>
                  <shadedPattern>myapp.shaded.com.google.common</shadedPattern>
                </relocation>
              </relocations>
            </configuration>
          </execution>
        </executions>
      </plugin>