- Notifications
You must be signed in to change notification settings - Fork 313
Introduce environment component #9071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
plugins { | ||
`java-library` | ||
id("com.gradleup.shadow") | ||
} | ||
| ||
apply(from = "$rootDir/gradle/java.gradle") | ||
| ||
/* | ||
* Add an addition gradle configuration to be consumed by bootstrap only. | ||
* "datadog.trace." prefix is required to be excluded from Jacoco instrumentation. | ||
* See ConfigDefaults.DEFAULT_CIVISIBILITY_JACOCO_PLUGIN_EXCLUDES for more details. | ||
*/ | ||
tasks.shadowJar { | ||
relocate("datadog.environment", "datadog.trace.bootstrap.environment") | ||
} | ||
| ||
/* | ||
* Configure test coverage. | ||
*/ | ||
extra.set("minimumInstructionCoverage", 0.7) | ||
val excludedClassesCoverage by extra { | ||
listOf( | ||
"datadog.environment.JavaVirtualMachine", // depends on OS and JVM vendor | ||
"datadog.environment.JavaVirtualMachine.JvmOptionsHolder", // depends on OS and JVM vendor | ||
"datadog.environment.JvmOptions", // depends on OS and JVM vendor | ||
"datadog.environment.OperatingSystem", // depends on OS | ||
) | ||
} | ||
val excludedClassesBranchCoverage by extra { | ||
listOf("datadog.environment.CommandLine") // tested using forked process | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
package datadog.environment; | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
import static java.util.Collections.emptyList; | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
import de.thetaphi.forbiddenapis.SuppressForbidden; | ||||||||||||||||||||||||||||||||||||||||||||||
import java.util.Arrays; | ||||||||||||||||||||||||||||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||
* Fetches and captures the command line, both command and its arguments. It relies on a | ||||||||||||||||||||||||||||||||||||||||||||||
* non-standard {@code sun.java.command} system property and was tested on: | ||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||
* <ul> | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>OracleJDK, | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>OpenJDK, | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>Temurin based JDK, | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>IMB JDK, | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>Azul Zulu, | ||||||||||||||||||||||||||||||||||||||||||||||
* <li>Amazon Coretto, | ||||||||||||||||||||||||||||||||||||||||||||||
* </ul> | ||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||
* This should be replaced by {@code ProcessHandle} and {@code ProcessHandle.Info} once Java 9+ | ||||||||||||||||||||||||||||||||||||||||||||||
* become available. | ||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||
class CommandLine { | ||||||||||||||||||||||||||||||||||||||||||||||
private static final String SUN_JAVA_COMMAND_PROPERTY = "sun.java.command"; | ||||||||||||||||||||||||||||||||||||||||||||||
final List<String> fullCommand = findFullCommand(); | ||||||||||||||||||||||||||||||||||||||||||||||
final String name = getCommandName(); | ||||||||||||||||||||||||||||||||||||||||||||||
final List<String> arguments = getCommandArguments(); | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
@SuppressForbidden // split on single-character uses fast path | ||||||||||||||||||||||||||||||||||||||||||||||
private List<String> findFullCommand() { | ||||||||||||||||||||||||||||||||||||||||||||||
String command = SystemProperties.getOrDefault(SUN_JAVA_COMMAND_PROPERTY, "").trim(); | ||||||||||||||||||||||||||||||||||||||||||||||
return command.isEmpty() ? emptyList() : Arrays.asList(command.split(" ")); | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this work if args have spaces ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. full command might be problematic to parse properly. I'm thinking about windows in particular.
...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that would work well. But that is the current behavior: dd-trace-java/internal-api/src/main/java/datadog/trace/api/env/CapturedEnvironment.java Lines 22 to 43 in 0f41106
So far, I would be in favor to keep it as is, document the limitation, and revisit it later when I will finally have time to do LP work (this dev is mostly based of R&D week and overnight time). WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes OK. Eventually this can be a good call as a future improvement to leverage | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
private String getCommandName() { | ||||||||||||||||||||||||||||||||||||||||||||||
return fullCommand.isEmpty() ? null : fullCommand.get(0); | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||
private List<String> getCommandArguments() { | ||||||||||||||||||||||||||||||||||||||||||||||
if (fullCommand.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||
return fullCommand; | ||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||
return fullCommand.subList(1, fullCommand.size()); | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package datadog.environment; | ||
| ||
import javax.annotation.Nonnull; | ||
import javax.annotation.Nullable; | ||
| ||
/** | ||
* Safely queries environment variables against security manager. | ||
* | ||
* @see <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SecurityManager.html">Security | ||
* Manager</a> | ||
*/ | ||
public final class EnvironmentVariables { | ||
private EnvironmentVariables() {} | ||
| ||
/** | ||
* Gets an environment variable value. | ||
* | ||
* @param name The environment variable name. | ||
* @return The environment variable value, {@code null} if missing or can't be retrieved. | ||
*/ | ||
public static @Nullable String get(String name) { | ||
return getOrDefault(name, null); | ||
} | ||
| ||
/** | ||
* Gets an environment variable value, or default value if missing or can't be retrieved. | ||
* | ||
* @param name The environment variable name. | ||
* @param defaultValue The default value to return if the environment variable is missing or can't | ||
* be retrieved. | ||
* @return The environment variable value, {@code defaultValue} if missing or can't be retrieved. | ||
*/ | ||
public static String getOrDefault(@Nonnull String name, String defaultValue) { | ||
try { | ||
String value = System.getenv(name); | ||
return value == null ? defaultValue : value; | ||
} catch (SecurityException e) { | ||
return defaultValue; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package datadog.environment; | ||
| ||
import java.util.ArrayList; | ||
import java.util.List; | ||
| ||
/** | ||
* This class represents a Java version according the String Naming Convention. | ||
* | ||
* @see <a href="https://www.oracle.com/java/technologies/javase/versioning-naming.html">String | ||
* Naming Convention</a> | ||
*/ | ||
final class JavaVersion { | ||
final int major; | ||
final int minor; | ||
final int update; | ||
| ||
JavaVersion(int major, int minor, int update) { | ||
this.major = major; | ||
this.minor = minor; | ||
this.update = update; | ||
} | ||
| ||
static JavaVersion getRuntimeVersion() { | ||
return parseJavaVersion(SystemProperties.getOrDefault("java.version", "")); | ||
} | ||
| ||
static JavaVersion parseJavaVersion(String javaVersion) { | ||
// Remove pre-release part, usually -ea | ||
final int indexOfDash = javaVersion.indexOf('-'); | ||
if (indexOfDash >= 0) { | ||
javaVersion = javaVersion.substring(0, indexOfDash); | ||
} | ||
| ||
int major = 0; | ||
int minor = 0; | ||
int update = 0; | ||
| ||
try { | ||
List<Integer> nums = splitDigits(javaVersion); | ||
major = nums.get(0); | ||
| ||
// for java 1.6/1.7/1.8 | ||
if (major == 1) { | ||
major = nums.get(1); | ||
minor = nums.get(2); | ||
update = nums.get(3); | ||
} else { | ||
minor = nums.get(1); | ||
update = nums.get(2); | ||
} | ||
} catch (NumberFormatException | IndexOutOfBoundsException e) { | ||
// unable to parse version string - do nothing | ||
} | ||
return new JavaVersion(major, minor, update); | ||
} | ||
| ||
/* The method splits java version string by digits. Delimiters are: dot, underscore and plus */ | ||
private static List<Integer> splitDigits(String str) { | ||
List<Integer> results = new ArrayList<>(); | ||
| ||
int len = str.length(); | ||
int value = 0; | ||
for (int i = 0; i < len; i++) { | ||
char ch = str.charAt(i); | ||
if (ch >= '0' && ch <= '9') { | ||
value = value * 10 + (ch - '0'); | ||
} else if (ch == '.' || ch == '_' || ch == '+') { | ||
results.add(value); | ||
value = 0; | ||
} else { | ||
throw new NumberFormatException(); | ||
} | ||
} | ||
results.add(value); | ||
return results; | ||
} | ||
| ||
public boolean is(int major) { | ||
return this.major == major; | ||
} | ||
| ||
public boolean is(int major, int minor) { | ||
return this.major == major && this.minor == minor; | ||
} | ||
| ||
public boolean is(int major, int minor, int update) { | ||
return this.major == major && this.minor == minor && this.update == update; | ||
} | ||
| ||
public boolean isAtLeast(int major, int minor, int update) { | ||
return isAtLeast(this.major, this.minor, this.update, major, minor, update); | ||
} | ||
| ||
public boolean isBetween( | ||
int fromMajor, int fromMinor, int fromUpdate, int toMajor, int toMinor, int toUpdate) { | ||
return isAtLeast(toMajor, toMinor, toUpdate, fromMajor, fromMinor, fromUpdate) | ||
&& isAtLeast(fromMajor, fromMinor, fromUpdate) | ||
&& !isAtLeast(toMajor, toMinor, toUpdate); | ||
} | ||
| ||
private static boolean isAtLeast( | ||
int major, int minor, int update, int atLeastMajor, int atLeastMinor, int atLeastUpdate) { | ||
return (major > atLeastMajor) | ||
|| (major == atLeastMajor && minor > atLeastMinor) | ||
|| (major == atLeastMajor && minor == atLeastMinor && update >= atLeastUpdate); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.