|
| 1 | +# Bootstrap and Premain Design Guidelines |
| 2 | + |
| 3 | +This document outlines critical design constraints and best practices when developing code that runs during the Java agent's `premain` phase (bootstrap). |
| 4 | +Following these guidelines will help avoid breaking applications and ensure maximum compatibility. |
| 5 | + |
| 6 | +## Background |
| 7 | + |
| 8 | +The Java agent runs in two distinct phases: |
| 9 | +1. **premain phase**: Code executed before the application's `main` method |
| 10 | +2. **post-main phase**: Code executed after the application has started |
| 11 | + |
| 12 | +The premain phase is particularly sensitive because many frameworks and applications configure their runtime environment during or after `main`. |
| 13 | +Loading certain Java classes too early can lock in the wrong implementations or cause unexpected behavior. |
| 14 | + |
| 15 | +## Critical Classes to Avoid in Premain |
| 16 | + |
| 17 | +### 1. Java Util Logging (`java.util.logging.*`) |
| 18 | + |
| 19 | +**Why to avoid:** |
| 20 | + |
| 21 | +- Some frameworks set system properties to select different JUL implementations |
| 22 | +- Webapp servers may set these properties after `main` |
| 23 | +- Using JUL during premain can cause the wrong implementation to become locked-in |
| 24 | +- Can cause log-spam and startup delays when the chosen implementation class is not available |
| 25 | +- May cause issues when web-apps expect to set up a context class-loader before JUL is used |
| 26 | + |
| 27 | +**What to use instead:** |
| 28 | + |
| 29 | +Logging depends on **when** the code runs, not where it's loaded. |
| 30 | + |
| 31 | +#### Very Early Bootstrap (AgentPreCheck / AgentBootstrap) |
| 32 | + |
| 33 | +Code running before the internal logger is configured ([Agent.java line 154](https://github.com/DataDog/dd-trace-java/blob/v1.53.0/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java#L154)) must use `System.err`, not `System.out`, which is reserved for application output: |
| 34 | + |
| 35 | +```java |
| 36 | +System.err.println("Diagnostic message"); // System.error, NOT System.out |
| 37 | +``` |
| 38 | + |
| 39 | +#### Later Bootstrap Code |
| 40 | + |
| 41 | +After the internal logger is ready, use `org.slf4j` that is shaded and redirects to our internal logger: |
| 42 | + |
| 43 | +```java |
| 44 | +import org.slf4j.Logger; |
| 45 | +import org.slf4j.LoggerFactory; |
| 46 | + |
| 47 | +private static final Logger log = LoggerFactory.getLogger(MyClass.class); |
| 48 | +``` |
| 49 | + |
| 50 | +### 2. Java NIO (`java.nio.*`) |
| 51 | + |
| 52 | +**Why to avoid:** |
| 53 | +- Calling `FileSystems.getDefault()` during premain can break applications that set the `java.nio.file.spi.DefaultFileSystemProvider` system property in their `main` method |
| 54 | +- Some applications expect to control the default filesystem implementation, and premature initialization locks in the wrong provider |
| 55 | +- Loading `java.nio` also triggers native library initialization (`pthread` on Linux) which can introduce a race condition |
| 56 | + |
| 57 | +**Reference:** [Java NIO FileSystems Documentation](https://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystems.html#getDefault--) |
| 58 | + |
| 59 | +**What to use instead:** |
| 60 | +- Use `java.io.*` classes for file operations during premain |
| 61 | +- `java.io.File`, `FileInputStream`, `FileOutputStream`, etc. are safe alternatives |
| 62 | +- If you must use NIO features, defer initialization until after `main` |
| 63 | + |
| 64 | +**Example from issue #9780:** |
| 65 | +```java |
| 66 | +// BAD - Triggers java.nio initialization in premain |
| 67 | +FileSystems.getDefault(); |
| 68 | +Path.of("/some/path"); |
| 69 | + |
| 70 | +// GOOD - Use java.io instead |
| 71 | +new File("/some/path"); |
| 72 | +new FileInputStream(file); |
| 73 | +``` |
| 74 | + |
| 75 | +### 3. JMX (`javax.management.*`) |
| 76 | + |
| 77 | +**Why to avoid:** |
| 78 | +- Similar to JUL, some frameworks set system properties to select custom JMX builders |
| 79 | +- These properties may be set after `main` |
| 80 | +- Premature JMX initialization can lock in the wrong builder |
| 81 | +- Can cause startup delays if the implementation class is not immediately available |
| 82 | + |
| 83 | +**What to use instead:** |
| 84 | +- Defer JMX registration and usage until after the application has started |
| 85 | +- Initialize JMX components lazily when first needed |
| 86 | + |
| 87 | +## General Principles |
| 88 | + |
| 89 | +### 1. Minimize Premain Footprint |
| 90 | + |
| 91 | +**Guideline:** Move as much code as possible out of premain |
| 92 | + |
| 93 | +The general direction has been to minimize what runs during premain. Only execute what is absolutely necessary for: |
| 94 | +- Setting up the instrumentation framework |
| 95 | +- Registering transformers |
| 96 | +- Critical initialization that must happen before application code runs |
| 97 | + |
| 98 | +### 2. Avoid Side Effects |
| 99 | + |
| 100 | +**Guideline:** Be extremely careful about what classes are loaded during premain |
| 101 | + |
| 102 | +Loading a class during premain can have unintended side effects: |
| 103 | +- Triggers static initializers |
| 104 | +- May load related classes |
| 105 | +- Can initialize native libraries |
| 106 | +- May lock in system property values |
| 107 | + |
| 108 | +### 3. Native Library Initialization |
| 109 | + |
| 110 | +**Guideline:** Be aware of native library loading and initialization |
| 111 | + |
| 112 | +Some Java classes trigger native library loading: |
| 113 | +- `java.nio` - triggers pthread initialization on Linux and may create a [race condition (race conditionJDK-8345810)](https://bugs.openjdk.org/browse/JDK-8345810) |
| 114 | +- Socket operations - may trigger native networking libraries |
| 115 | +- File system operations - platform-specific native code |
0 commit comments