open-telemetry / open-telemetry/opentelemetry-java-instrumentation

Avoid main-thread disk I/O during InstrumenterCustomizerProvider SPI initialization on Android

Open
#19,954 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
2.6k
Forks
1.2k
Avg merge
2d 18h
Merged PRs (30d)
228

Description

Description

On Android, the first OpenTelemetry instrumenter construction performs a classpath ServiceLoader lookup for InstrumenterCustomizerProvider.

When this occurs on the main thread with Android StrictMode enabled, it causes a DiskReadViolation.

The lookup path is:

InstrumenterBuilder.build()
→ InternalInstrumenterCustomizerUtil
→ InstrumenterCustomizerUtil.
→ ServiceLoaderUtil.load(...)
→ ServiceLoader
→ ClassLoader.getResources()
→ JarFile / ZipFile
→ disk read

The operation is one-time, but it took approximately 80 ms in our test and violates Android main-thread I/O rules.

Versions
  • opentelemetry-instrumentation-api: 2.30.0
  • opentelemetry-instrumentation-api-incubator: 2.30.0-alpha
  • Android application using StrictMode
  • Instrumenter: DefaultHttpClientInstrumenterBuilder
Reproduction

Enable StrictMode before constructing an instrumenter:
Construct the first OpenTelemetry instrumenter.

import android.os.StrictMode;
import com.splunk.rum.instrumentation.okhttp3.OkHttpTelemetry;
import io.opentelemetry.api.OpenTelemetry;

StrictMode.setThreadPolicy(
    new StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .penaltyLog()
        .build());

OkHttpTelemetry telemetry =
    OkHttpTelemetry.builder(OpenTelemetry.noop()).build(); 

The first instrumenter construction produces a DiskReadViolation similar to:

StrictMode policy violation; ~duration=80 ms: android.os.strictmode.DiskReadViolation
	at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1658)
	at java.io.UnixFileSystem.getLastModifiedTime(UnixFileSystem.java:289)
	at java.io.File.lastModified(File.java:937)
	at java.util.zip.ZipFile.<init>(ZipFile.java:265)
	at java.util.zip.ZipFile.<init>(ZipFile.java:187)
	at java.util.jar.JarFile.<init>(JarFile.java:169)
	at java.util.jar.JarFile.<init>(JarFile.java:106)
	at libcore.io.ClassPathURLStreamHandler.<init>(ClassPathURLStreamHandler.java:46)
	at dalvik.system.DexPathList$Element.maybeInit(DexPathList.java:750)
	at dalvik.system.DexPathList$Element.findResource(DexPathList.java:777)
	at dalvik.system.DexPathList.findResources(DexPathList.java:572)
	at dalvik.system.BaseDexClassLoader.findResources(BaseDexClassLoader.java:330)
	at java.lang.ClassLoader.getResources(ClassLoader.java:839)
	at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:349)
	at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:402)
	at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:488)
	at io.opentelemetry.instrumentation.api.incubator.instrumenter.internal.InstrumenterCustomizerUtil.<clinit>(InstrumenterCustomizerUtil.java:24)
	at java.lang.Class.classForName(Native Method)
	at java.lang.Class.forName(Class.java:454)
	at java.lang.Class.forName(Class.java:379)
	at io.opentelemetry.instrumentation.api.internal.InternalInstrumenterCustomizerUtil.<clinit>(InternalInstrumenterCustomizerUtil.java:22)
	at io.opentelemetry.instrumentation.api.internal.InternalInstrumenterCustomizerUtil.getInstrumenterCustomizerProviders(InternalInstrumenterCustomizerUtil.java:38)
	at io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder.applyCustomizers(InstrumenterBuilder.java:438)
	at io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder.buildInstrumenter(InstrumenterBuilder.java:303)
	at io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder.buildInstrumenter(InstrumenterBuilder.java:295)
	at io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder.build(DefaultHttpClientInstrumenterBuilder.java:242)
Possible Solutions

Make SPI discovery Android-safe.
Avoid the lookup when no customizer providers are present.
Provide a supported opt-out for Android/library instrumentation.

Note: The customizer SPI was introduced through the instrumenter extension work in
#13917,
which shipped with the 2.19.0 release.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with InstrumenterBuilder.applyCustomizers and trace InternalInstrumenterCustomizerUtil, InstrumenterCustomizerUtil, and ServiceLoaderUtil to understand when SPI discovery runs. Reproduce the first DefaultHttpClientInstrumenterBuilder construction with Android StrictMode enabled, then verify the chosen approach avoids the main-thread disk read while preserving customizer-provider discovery.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, java
Domain
api, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.