Soluciona problemas de las bibliotecas cliente de Cloud para Java

En este documento, se proporciona una descripción general del registro y la solución de problemas comunes con las bibliotecas cliente de Cloud para Java.

Logging

Existen dos maneras de habilitar el registro para las bibliotecas cliente: usar java.util.logging o usar el registro de depuración de la biblioteca cliente con SLF4J.

Usa java.util.logging

Las bibliotecas cliente de Cloud para Java usan el paquete de la API de Java Logging (java.util.logging). Configurar el nivel de registro revela información que te ayuda a solucionar problemas, incluidos los siguientes:

  • Tiempo de comunicación subyacente entre el cliente y el servidor
  • Encabezados de mensajes de solicitud y respuesta
  • Mensajes detallados en bibliotecas de dependencias subyacentes

Para habilitar rápidamente el registro detallado para las bibliotecas cliente de Cloud para Java, crea un archivo logging.properties con el siguiente contenido:

# 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

Luego, ejecuta tu aplicación con -Djava.util.logging.config.file=path/to/logging.properties como el VM argument, no el Program argument.

Si usas IntelliJ, especifica el argumento de VM en Run/Debug Configuration:

Configuración de ejecución/depuración de IntelliJ que muestra dónde especificar los argumentos de la VM

Si la JVM de tu programa se ejecuta correctamente con la configuración, verás el registro de nivel FINE en tu consola.

Resultado de ejemplo:

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

Esta es una forma de configurar el nivel de registro. Para obtener más información sobre el uso de la API de Java Logging, consulta Descripción general de Java Logging.

Registro de depuración de la biblioteca cliente (SLF4J)

Las bibliotecas cliente de Cloud incluyen el registro de depuración de participación, que puede ayudarte a solucionar problemas de la integración de tu aplicación con la API. Cuando se activa el registro, los eventos clave, como las solicitudes y las respuestas, junto con las cargas útiles de datos y los metadatos, como los encabezados, se registran en el flujo de errores estándar.

ADVERTENCIA: El registro de depuración de la biblioteca cliente incluye tus cargas útiles de datos en texto sin formato, lo que podría incluir datos sensibles, como PII para ti o tus clientes, claves privadas o cualquier otro dato de seguridad que podría verse comprometido si se filtra. Siempre practica una buena higiene de datos con los registros de tu aplicación y sigue el principio de acceso mínimo. Google también recomienda que el registro de depuración de la biblioteca cliente se habilite solo de forma temporal durante la depuración activa y no se use de forma permanente en la producción.

Requisitos

Nuestras bibliotecas admiten el registro de depuración con la interfaz SLF4J.

Las bibliotecas cliente y la BOM de bibliotecas no incluyen la dependencia slf4j-api. Deberás configurar las dependencias de registro, incluidas SLF4J y las implementaciones y configuraciones de registro correspondientes, antes de activar el registro de depuración de la biblioteca cliente.

NOTE: Debes tener SLF4J y los proveedores de registro correspondientes, por ejemplo, logback, log4j2, etcétera, en tu ruta de clase para usar la función de registro de depuración de la biblioteca cliente. De lo contrario, no habrá registro de depuración, incluso si lo habilitas con la variable de entorno.

Habilita el registro con una variable de entorno

El registro de depuración está desactivado de forma predeterminada. El registro de depuración no se activa, a menos que se haga de forma explícita, incluso si se cumplen los requisitos previos.

Para habilitar el registro de depuración de la biblioteca cliente, configura la variable de entorno GOOGLE_SDK_JAVA_LOGGING como true, sin distinción entre mayúsculas y minúsculas. Si no se configura o se usa cualquier otro valor, se inhabilita el registro de depuración de la biblioteca cliente.

Ejemplos de configuración de registros

En la documentación, se proporciona un ejemplo de registro de depuración con Logback.

Agrega dependencias de Logback a tu aplicación, lo que incorporará la dependencia slf4j de forma transitiva. Los usuarios deben consultar la configuración de Logback para obtener las versiones más recientes. Puedes omitir este paso si tu aplicación ya tiene dependencias de logback en la ruta de clase.

Si usas Maven, sucede lo siguiente:

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

Agrega el contenido al archivo de configuración de logback si deseas ver registros de nivel DEBUG. Puedes omitir este paso si solo necesitas registros de nivel INFO. Consulta la configuración de Logback para obtener más información.

<!-- 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 no está configurado correctamente

Si ves excepciones relacionadas con ALPN is not configured properly, como las siguientes:

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.

Usa el comprobador de compatibilidad para ver si tu entorno es compatible con clientes basados en gRPC.

La incompatibilidad puede significar una de las siguientes opciones:

Soluciona problemas de ClassNotFoundException, NoSuchMethodError y NoClassDefFoundError

Estos errores suelen deberse a que hay varias versiones o versiones en conflicto de la misma dependencia en la ruta de clase. Estos conflictos de dependencia suelen ocurrir con guava o protobuf-java.

Varias fuentes pueden causar conflictos de ruta de clase:

  • Varias versiones de la misma dependencia transitiva en el árbol de dependencias
  • Tu ruta de clase del entorno de ejecución tiene diferentes versiones de dependencias de las que especificaste en la compilación.

Por ejemplo, si tienes una dependencia directa o transitiva en la versión 19.0 de Guava y google-cloud-java usa la versión 30.0 de Guava, es posible que google-cloud-java use métodos de Guava que no existen en Guava 19.0 y causen NoSuchMethodError.

Del mismo modo, si tu ruta de clase tiene una versión anterior de protobuf-java, pero google-cloud-java requiere una versión posterior, es posible que veas NoClassDefFoundError que no puede inicializar las clases google-cloud-java.

Por ejemplo:

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

Valida el conflicto

Verifica el árbol de dependencias para ver si tienes varias versiones de las mismas dependencias:

$ mvn dependency:tree

Busca versiones de dependencias potencialmente conflictivas, como guava o protobuf-java.

Si experimentas el error solo durante el tiempo de ejecución, es posible que tu entorno de ejecución introduzca archivos JAR en conflicto en tu ruta de clase del entorno de ejecución. Un caso típico es que Hadoop, Spark o cualquier otro software de servidor en el que se ejecuta tu aplicación tenga versiones en conflicto de archivos JAR netty, guava o protobuf-java en la ruta de clase.

Detecta conflictos durante la compilación

Para detectar errores de vinculación de dependencias en el momento de la compilación, agrega la regla de Linkage Checker Enforcer en tu 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>

Sin embargo, no hay forma de detectar conflictos de ruta de clase del entorno de ejecución. Debes conocer por completo qué archivos JAR o clases se incluyen en la ruta de clase del entorno de ejecución, ya que cada entorno de servidor es diferente.

Resuelve el conflicto

Existen diferentes estrategias para resolver conflictos, pero debes comprender la causa raíz de los conflictos:

  • Si controlas el árbol de dependencias, actualiza las dependencias en conflicto (por ejemplo, actualiza Guava). Este enfoque es el más sólido, pero puede requerir un esfuerzo significativo y varias versiones de la biblioteca para garantizar la compatibilidad.
  • Si no puedes modificar ni enviar versiones nuevas de tus dependencias, importa com.google.cloud:libraries-bom:25.1.0 (o una versión más reciente) para seleccionar versiones de dependencia coherentes. Este enfoque simplifica la administración de dependencias. Por ejemplo, así puedes depender de versiones coherentes de Guava y com.google.cloud:google-cloud-storage sin configurar de forma explícita la versión de ninguna de ellas:
  ...
  <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>
  ...
  • En las notas de la versión de libraries-bom, se muestran las bibliotecas de dependencias compatibles. Por ejemplo, https://github.com/googleapis/java-cloud-bom/releases/tag/v26.31.0 muestra lo siguiente:

    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
    

    Si examinas el gráfico de dependencias de tu proyecto (mvn dependency:tree -Dverbose, gradle dependencies o sbt dependencyTree), es posible que encuentres que algunas dependencias tienen versiones inesperadas, lo que podría causar conflictos de dependencias.

  • Si el cambio de versiones de dependencia introduce otras fallas, considera las dependencias de sombreado que entran en conflicto con Cloud de Confiance by S3NS las bibliotecas de Java.

    Por ejemplo, para sombrear guava y protobuf-java, haz lo siguiente:

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