Memecahkan masalah Library Klien Cloud untuk Java

Dokumen ini memberikan ringkasan tentang logging dan pemecahan masalah umum dengan Library Klien Cloud untuk Java.

Logging

Ada dua cara untuk mengaktifkan logging untuk library klien: menggunakan java.util.logging, atau menggunakan Logging Debug Library Klien dengan SLF4J.

Menggunakan java.util.logging

Library Klien Cloud untuk Java menggunakan paket Java logging API (java.util.logging). Mengonfigurasi tingkat logging akan menampilkan informasi yang membantu Anda memecahkan masalah, termasuk:

  • Waktu komunikasi klien-server yang mendasarinya.
  • Header pesan permintaan dan respons.
  • Pesan panjang di library dependensi yang mendasarinya.

Untuk mengaktifkan logging panjang dengan cepat untuk Library Klien Cloud untuk Java, buat file logging.properties dengan konten berikut:

# 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

Selanjutnya, jalankan aplikasi Anda dengan -Djava.util.logging.config.file=path/to/logging.properties sebagai VM argument, bukan Program argument.

Jika menggunakan IntelliJ, Anda menentukan argumen VM di Run/Debug Configuration:

Konfigurasi Run/Debug IntelliJ yang menunjukkan tempat untuk menentukan argumen VM

Jika JVM program Anda berjalan dengan konfigurasi yang benar, Anda akan melihat logging tingkat FINE di konsol.

Contoh output:

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

Ini adalah salah satu cara untuk mengonfigurasi tingkat logging. Untuk mengetahui informasi selengkapnya tentang penggunaan Java logging API, lihat Ringkasan Logging Java.

Logging Debug Library Klien (SLF4J)

Library Klien Cloud dilengkapi dengan Logging Debug opsional yang dapat membantu Anda memecahkan masalah integrasi aplikasi dengan API. Saat logging diaktifkan, peristiwa utama seperti permintaan dan respons, beserta payload data dan metadata, seperti header, akan dicatat ke aliran error standar.

PERINGATAN: Logging Debug Library Klien menyertakan payload data Anda dalam teks biasa, yang dapat mencakup data sensitif seperti PII untuk Anda atau pelanggan Anda, kunci pribadi, atau data keamanan lainnya yang dapat disusupi jika bocor. Selalu praktikkan kebersihan data yang baik dengan log aplikasi Anda, dan ikuti prinsip akses paling sedikit. Google juga merekomendasikan agar logging debug library klien hanya diaktifkan untuk sementara selama proses debug aktif, dan tidak digunakan secara permanen dalam produksi.

Prasyarat

Library kami mendukung logging debug menggunakan SLF4J antarmuka.

Library klien dan BOM Library tidak menyertakan dependensi slf4j-api. Anda harus menyiapkan dependensi logging termasuk SLF4J dan implementasi serta konfigurasi logging yang sesuai sebelum mengaktifkan logging debug library klien.

NOTE: Anda harus memiliki SLF4J dan penyedia logging yang sesuai, misalnya, logback, log4j2, dll., di classpath agar dapat menggunakan fitur logging debug library klien; jika tidak, tidak ada logging debug meskipun Anda mengaktifkannya menggunakan variabel lingkungan.

Mengaktifkan Logging dengan variabel lingkungan

Logging debug dinonaktifkan secara default. Logging debug tidak diaktifkan kecuali secara eksplisit meskipun prasyarat terpenuhi.

Anda dapat mengaktifkan logging debug library klien dengan menetapkan variabel lingkungan GOOGLE_SDK_JAVA_LOGGING ke true, tanpa memperhatikan huruf besar/kecil. Jika tidak ditetapkan atau nilai lainnya, logging debug library klien akan dinonaktifkan.

Contoh penyiapan logging

Dokumentasi ini memberikan contoh logging debug menggunakan Logback.

Tambahkan dependensi Logback ke aplikasi Anda, yang akan membawa dependensi slf4j secara transitif. Pengguna harus melihat penyiapan Logback untuk versi terbaru. Anda dapat melewati langkah ini jika aplikasi Anda sudah memiliki dependensi logback di classpath.

Jika Anda menggunakan Maven:

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

Tambahkan konten ke file konfigurasi logback jika Anda ingin melihat log tingkat DEBUG. Anda dapat melewati langkah ini jika hanya memerlukan log tingkat INFO. Lihat Konfigurasi Logback untuk mengetahui informasi selengkapnya.

<!-- 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 tidak dikonfigurasi dengan benar

Jika Anda melihat pengecualian terkait ALPN is not configured properly, seperti:

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.

Gunakan pemeriksa kompatibilitas untuk melihat apakah lingkungan Anda kompatibel dengan klien berbasis gRPC.

Ketidakcocokan mungkin berarti salah satu hal berikut:

Memecahkan masalah ClassNotFoundException, NoSuchMethodError, dan NoClassDefFoundError

Error ini sering disebabkan oleh adanya beberapa versi atau versi dependensi yang sama yang saling bertentangan di classpath. Konflik dependensi ini sering terjadi dengan guava atau protobuf-java.

Beberapa sumber dapat menyebabkan konflik classpath:

  • Beberapa versi dependensi transitif yang sama di pohon dependensi.
  • Classpath runtime Anda memiliki versi dependensi yang berbeda dengan yang Anda tentukan dalam build.

Misalnya, jika Anda memiliki dependensi langsung atau transitif pada Guava versi 19.0, dan google-cloud-java menggunakan Guava versi 30.0, maka google-cloud-java mungkin menggunakan metode Guava yang tidak ada di Guava 19.0, dan menyebabkan NoSuchMethodError.

Demikian pula, jika classpath Anda memiliki versi protobuf-java yang lebih lama, tetapi google-cloud-java memerlukan versi yang lebih baru, Anda mungkin melihat NoClassDefFoundError yang gagal menginisialisasi class google-cloud-java.

Contoh:

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

Memvalidasi konflik

Periksa pohon dependensi untuk melihat apakah Anda memiliki beberapa versi dependensi yang sama:

$ mvn dependency:tree

Cari versi dependensi yang berpotensi konflik seperti guava atau protobuf-java.

Jika Anda hanya mengalami error selama runtime, lingkungan runtime Anda mungkin memperkenalkan JAR yang bertentangan ke classpath runtime Anda. Kasus umumnya adalah Hadoop, Spark, atau software server lain yang dijalankan aplikasi Anda memiliki versi JAR netty, guava, atau protobuf-java yang bertentangan di classpath.

Mendeteksi konflik selama build

Untuk mendeteksi error linkage dependensi pada waktu kompilasi, tambahkan Aturan Enforcer Pemeriksa Linkage di pom.xml Anda:

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

Namun, tidak ada cara untuk mendeteksi konflik classpath runtime. Anda harus sepenuhnya mengetahui JAR/class mana yang disertakan dalam classpath runtime karena setiap lingkungan server berbeda.

Menyelesaikan konflik

Ada berbagai strategi untuk menyelesaikan konflik, tetapi Anda harus memahami akar penyebab konflik:

  • Jika Anda mengontrol pohon dependensi, upgrade dependensi yang bertentangan (misalnya, mengupgrade Guava). Pendekatan ini adalah yang paling kuat, tetapi dapat memerlukan upaya yang signifikan dan beberapa rilis library untuk memastikan kompatibilitas.
  • Jika Anda tidak dapat mengubah dan mengirim versi baru dependensi, impor com.google.cloud:libraries-bom:25.1.0 (atau versi yang lebih baru) untuk memilih versi dependensi yang konsisten. Pendekatan ini menyederhanakan pengelolaan dependensi. Misalnya, berikut cara Anda dapat bergantung pada versi Guava dan com.google.cloud:google-cloud-storage yang konsisten tanpa menetapkan versi salah satunya secara eksplisit:
  ...
  <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>
  ...
  • Catatan rilis libraries-bom menampilkan library dependensi yang kompatibel. Misalnya, https://github.com/googleapis/java-cloud-bom/releases/tag/v26.31.0 menampilkan:

    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
    

    Dengan memeriksa grafik dependensi project Anda (mvn dependency:tree -Dverbose, gradle dependencies, atau sbt dependencyTree), Anda mungkin menemukan beberapa dependensi memiliki versi yang tidak terduga, yang dapat menyebabkan konflik dependensi.

  • Jika mengubah versi dependensi menyebabkan kegagalan lain, pertimbangkan dependensi shading yang bertentangan dengan Google Cloud library Java.

    Misalnya, untuk shading guava dan 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>