Skip to content

Commit 487b8dd

Browse files
committed
feat(doc): Add boostrap development guidelines
1 parent 76a9cb1 commit 487b8dd

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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

docs/how_instrumentations_work.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ public class MyInstrumentationHelper {
477477
It is shaded and redirects to our internal logger.
478478

479479
> [!CAUTION]
480-
> Do NOT put logger fields in instrumentation classes or refer to them in advice:
480+
> Do NOT put logger fields in instrumentation classes:
481481
482482
```java
483483
// BAD - Logger in instrumentation class

0 commit comments

Comments
 (0)