Risolvere i problemi delle librerie client di Cloud per Java

Questo documento fornisce una panoramica della registrazione e della risoluzione dei problemi comuni con le librerie client di Cloud per Java.

Logging

Esistono due modi per attivare la registrazione per le librerie client: utilizzando java.util.logging o utilizzando il debug della registrazione della libreria client con SLF4J.

In uso: java.util.logging

Le librerie client di Cloud per Java utilizzano il pacchetto API Java Logging (java.util.logging). La configurazione del livello di logging rivela informazioni utili per la risoluzione dei problemi, tra cui:

  • Tempistica della comunicazione client-server sottostante.
  • Intestazioni dei messaggi di richiesta e risposta.
  • Messaggi dettagliati nelle librerie di dipendenze sottostanti.

Per abilitare rapidamente la registrazione dettagliata per le librerie client di Cloud per Java, crea un file logging.properties con i seguenti contenuti:

# 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

A questo punto, esegui l'applicazione con -Djava.util.logging.config.file=path/to/logging.properties come VM argument, non come Program argument.

Se utilizzi IntelliJ, specifica l'argomento VM in Run/Debug Configuration:

Configurazione di esecuzione/debug di IntelliJ che mostra dove specificare gli argomenti della VM

Se la JVM del tuo programma è in esecuzione con la configurazione corretta, vedrai la registrazione a livello FINE nella console.

Output di esempio:

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)
...

Questo è un modo per configurare il livello di logging. Per saperne di più sull'utilizzo dell'API Java Logging, consulta la panoramica di Java Logging.

Logging di debug della libreria client (SLF4J)

Le librerie client di Cloud includono la registrazione di debug facoltativa che può aiutarti a risolvere i problemi di integrazione dell'applicazione con l'API. Quando la registrazione viene attivata, gli eventi chiave come richieste e risposte, insieme a payload di dati e metadati, come le intestazioni, vengono registrati nel flusso di errori standard.

AVVISO: la registrazione del debug della libreria client include i payload dei dati in testo normale, che potrebbe includere dati sensibili come PII per te o per i tuoi clienti, chiavi private o altri dati di sicurezza che potrebbero essere compromessi se divulgati. Segui sempre le best practice per la pulizia dei dati con i log delle applicazioni e rispetta il principio del privilegio minimo. Google consiglia inoltre di attivare la registrazione del debug della libreria client solo temporaneamente durante il debug attivo e di non utilizzarla in modo permanente in produzione.

Prerequisito

Le nostre librerie supportano la registrazione di debug utilizzando l'interfaccia SLF4J.

Le librerie client e l'elenco dei materiali delle librerie non includono la dipendenza slf4j-api. Prima di attivare la registrazione di debug della libreria client, devi configurare le dipendenze di logging, incluse SLF4J e le implementazioni e configurazioni di logging corrispondenti.

NOTE: per utilizzare la funzionalità di logging di debug della libreria client, devi avere SLF4J e i provider di logging corrispondenti, ad esempio logback, log4j2 e così via, nel classpath. In caso contrario, non verrà eseguito alcun logging di debug anche se lo abiliti utilizzando la variabile di ambiente.

Abilitare la registrazione con una variabile di ambiente

Il logging di debug è disattivato per impostazione predefinita. Il logging di debug non è attivato a meno che non sia esplicitamente richiesto, anche se i prerequisiti sono soddisfatti.

Puoi attivare il logging di debug della libreria client impostando la variabile di ambiente GOOGLE_SDK_JAVA_LOGGING su true, senza distinzione tra maiuscole e minuscole. Se non è impostato o è impostato su un altro valore, il logging di debug della libreria client è disattivato.

Esempi di configurazioni di logging

La documentazione fornisce un esempio di logging di debug utilizzando Logback.

Aggiungi le dipendenze Logback alla tua applicazione, in modo da includere la dipendenza slf4j in modo transitivo. Gli utenti devono fare riferimento alla configurazione di Logback per le versioni più recenti. Puoi saltare questo passaggio se la tua applicazione ha già dipendenze logback nel classpath.

Se utilizzi Maven:

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

Aggiungi i contenuti al file di configurazione logback se vuoi visualizzare i log a livello di DEBUG. Puoi saltare questo passaggio se hai bisogno solo dei log di livello INFO. Per ulteriori informazioni, consulta la pagina Configurazione di Logback.

<!-- 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 non è configurato correttamente

Se vedi eccezioni relative a ALPN is not configured properly, ad esempio:

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.

Utilizza lo strumento di verifica della compatibilità per verificare se il tuo ambiente è compatibile con i client basati su gRPC.

L'incompatibilità potrebbe significare una delle seguenti situazioni:

Risolvi i problemi relativi a ClassNotFoundException, NoSuchMethodError e NoClassDefFoundError

Questi errori sono spesso causati dalla presenza di più versioni o versioni in conflitto della stessa dipendenza nel classpath. Questi conflitti di dipendenza si verificano spesso con guava o protobuf-java.

Più origini potrebbero causare conflitti nel classpath:

  • Più versioni della stessa dipendenza transitiva nell'albero delle dipendenze.
  • Il classpath del runtime ha versioni diverse delle dipendenze rispetto a quelle specificate nella build.

Ad esempio, se hai una dipendenza diretta o transitiva dalla versione 19.0 di Guava e google-cloud-java utilizza la versione 30.0 di Guava, google-cloud-java potrebbe utilizzare metodi Guava che non esistono nella versione 19.0 e causare NoSuchMethodError.

Allo stesso modo, se il classpath ha una versione precedente di protobuf-java, ma google-cloud-java richiede una versione successiva, potresti visualizzare NoClassDefFoundError che non riesce a inizializzare le classi google-cloud-java.

Ad esempio:

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

Convalidare il conflitto

Controlla l'albero delle dipendenze per vedere se hai più versioni delle stesse dipendenze:

$ mvn dependency:tree

Cerca versioni di dipendenze potenzialmente in conflitto, ad esempio guava o protobuf-java.

Se si verifica l'errore solo durante l'esecuzione, l'ambiente di runtime potrebbe introdurre JAR in conflitto nel classpath di runtime. Un caso tipico è che Hadoop, Spark o altro software server su cui viene eseguita l'applicazione hanno versioni netty, guava o protobuf-java JAR in conflitto nel classpath.

Rilevare i conflitti durante la build

Per rilevare gli errori di collegamento delle dipendenze in fase di compilazione, aggiungi la regola di applicazione del controllo dei collegamenti nel file pom.xml:

      <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>

Tuttavia, non è possibile rilevare conflitti del classpath di runtime. Devi essere pienamente consapevole di quali JAR/classi sono inclusi nel classpath di runtime perché ogni ambiente server è diverso.

Risolvere il conflitto

Esistono diverse strategie per risolvere i conflitti, ma devi comprendere la causa principale dei conflitti:

  • Se controlli l'albero delle dipendenze, esegui l'upgrade delle dipendenze in conflitto (ad esempio, l'upgrade di Guava). Questo approccio è il più solido, ma può richiedere uno sforzo significativo e il rilascio di più librerie per garantire la compatibilità.
  • Se non riesci a modificare e inviare nuove versioni delle tue dipendenze, importa com.google.cloud:libraries-bom:25.1.0 (o una versione più recente) per selezionare versioni delle dipendenze coerenti. Questo approccio semplifica la gestione delle dipendenze. Ad esempio, ecco come puoi fare affidamento su versioni coerenti di Guava e com.google.cloud:google-cloud-storage senza impostare esplicitamente la versione di una delle due:
  ...
  <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>
  ...
  • Le note di rilascio di libraries-bom mostrano le librerie di dipendenze compatibili. Ad esempio, https://github.com/googleapis/java-cloud-bom/releases/tag/v26.31.0 mostra:

    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
    

    Esaminando il grafico delle dipendenze del tuo progetto (mvn dependency:tree -Dverbose, gradle dependencies o sbt dependencyTree), potresti scoprire che alcune dipendenze hanno versioni inattese, il che potrebbe causare conflitti tra le dipendenze.

  • Se la modifica delle versioni delle dipendenze introduce altri errori, valuta la possibilità di ombreggiare le dipendenze che sono in conflitto con le librerie Java. Google Cloud

    Ad esempio, per ombreggiare guava e protobuf-java:

      <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>