Skip to content
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

Support declarative configuration #12265

Merged
merged 8 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ tasks.withType<JavaCompile>().configureEach {
)
if (System.getProperty("dev") != "true") {
// Fail build on any warning
compilerArgs.add("-Werror")
//compilerArgs.add("-Werror")
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
2 changes: 1 addition & 1 deletion dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ val dependencyVersions = hashMapOf<String, String>()
rootProject.extra["versions"] = dependencyVersions

// this line is managed by .github/scripts/update-sdk-version.sh
val otelSdkVersion = "1.42.1"
val otelSdkVersion = "1.43.0-SNAPSHOT"
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
val otelContribVersion = "1.38.0-alpha"
val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import io.opentelemetry.instrumentation.jmx.yaml.RuleParser;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.io.InputStream;
import java.nio.file.Files;
Expand All @@ -29,7 +28,7 @@ public class JmxMetricInsightInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);

if (config.getBoolean("otel.jmx.enabled", true)) {
JmxMetricInsight service =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.lang.reflect.Method;

Expand All @@ -21,7 +20,7 @@ public class OshiMetricsInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);

boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
if (!config.getBoolean("otel.instrumentation.oshi.enabled", defaultEnabled)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetricsBuilder;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;

/** An {@link AgentListener} that enables runtime metrics during agent startup. */
Expand All @@ -21,7 +20,7 @@ public class Java17RuntimeMetricsInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);

OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
RuntimeMetricsBuilder builder = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.javaagent.tooling.BeforeAgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.lang.instrument.Instrumentation;

Expand All @@ -19,7 +19,8 @@ public class JarAnalyzerInstaller implements BeforeAgentListener {

@Override
public void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredOpenTelemetrySdk);

boolean enabled =
config.getBoolean("otel.instrumentation.runtime-telemetry.package-emitter.enabled", false);
if (!enabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import io.opentelemetry.instrumentation.runtimemetrics.java8.internal.JmxRuntimeMetricsUtil;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -30,7 +29,7 @@ public class Java8RuntimeMetricsInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AutoConfigureUtil.getConfig(autoConfiguredSdk);
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);

boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
if (!config.getBoolean("otel.instrumentation.runtime-telemetry.enabled", defaultEnabled)
Expand Down
2 changes: 2 additions & 0 deletions javaagent-extension-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies {
// autoconfigure is unstable, do not expose as api
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")

testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")

// Used by byte-buddy but not brought in as a transitive dependency.
compileOnly("com.google.code.findbugs:annotations")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.bootstrap.internal;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import javax.annotation.Nullable;

/**
* A {@link ConfigProperties} which resolves properties based on {@link StructuredConfigProperties}.
*
* <p>Only properties starting with "otel.instrumentation." are resolved. Others return null (or
* default value if provided).
*
* <p>To resolve:
*
* <ul>
* <li>"otel.instrumentation" refers to the ".instrumentation.java" node
* <li>The portion of the property after "otel.instrumentation." is split into segments based on
* ".".
* <li>For each N-1 segment, we walk down the tree to find the relevant leaf {@link
* StructuredConfigProperties}.
* <li>We extract the property from the resolved {@link StructuredConfigProperties} using the last
* segment as the property key.
* </ul>
*
* <p>For example, given the following YAML, asking for {@code
* ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value":
*
* <pre>
* instrumentation:
* java:
* common:
* string_key: value
* </pre>
*/
public final class StructuredConfigPropertiesBridge implements ConfigProperties {

private static final StructuredConfigProperties EMPTY = new EmptyStructuredConfigProperties();

private final StructuredConfigProperties javaInstrumentation;

public StructuredConfigPropertiesBridge(
StructuredConfigProperties rootStructuredConfigProperties) {
StructuredConfigProperties instrumentation =
rootStructuredConfigProperties.getStructured("instrumentation");
if (instrumentation != null) {
javaInstrumentation = instrumentation.getStructured("java");
} else {
javaInstrumentation = EMPTY;
}
}

@Nullable
@Override
public String getString(String propertyName) {
return getPropertyValue(propertyName, StructuredConfigProperties::getString);
}

@Nullable
@Override
public Boolean getBoolean(String propertyName) {
return getPropertyValue(propertyName, StructuredConfigProperties::getBoolean);
}

@Nullable
@Override
public Integer getInt(String propertyName) {
return getPropertyValue(propertyName, StructuredConfigProperties::getInt);
}

@Nullable
@Override
public Long getLong(String propertyName) {
return getPropertyValue(propertyName, StructuredConfigProperties::getLong);
}

@Nullable
@Override
public Double getDouble(String propertyName) {
return getPropertyValue(propertyName, StructuredConfigProperties::getDouble);
}

@Nullable
@Override
public Duration getDuration(String propertyName) {
Long millis = getPropertyValue(propertyName, StructuredConfigProperties::getLong);
if (millis == null) {
return null;
}
return Duration.ofMillis(millis);
}

@Override
public List<String> getList(String propertyName) {
List<String> propertyValue =
getPropertyValue(
propertyName,
(properties, lastPart) -> properties.getScalarList(lastPart, String.class));
return propertyValue == null ? Collections.emptyList() : propertyValue;
}

@Override
public Map<String, String> getMap(String propertyName) {
StructuredConfigProperties propertyValue =
getPropertyValue(propertyName, StructuredConfigProperties::getStructured);
if (propertyValue == null) {
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>();
propertyValue
.getPropertyKeys()
.forEach(
key -> {
String value = propertyValue.getString(key);
if (value == null) {
return;
}
result.put(key, value);
});
return Collections.unmodifiableMap(result);
}

@Nullable
private <T> T getPropertyValue(
String property, BiFunction<StructuredConfigProperties, String, T> extractor) {
if (!property.startsWith("otel.instrumentation.")) {
return null;
}
String suffix = property.substring("otel.instrumentation.".length());
// Split the remainder of the property on ".", and walk to the N-1 entry
String[] segments = suffix.split("\\.");
if (segments.length == 0) {
return null;
}
StructuredConfigProperties target = javaInstrumentation;
if (segments.length > 1) {
for (int i = 0; i < segments.length - 1; i++) {
StructuredConfigProperties newTarget = target.getStructured(segments[i]);
if (newTarget == null) {
target = EMPTY;
break;
}
target = newTarget;
}
}
String lastPart = segments[segments.length - 1];
return extractor.apply(target, lastPart);
}

private static class EmptyStructuredConfigProperties implements StructuredConfigProperties {
@Nullable
@Override
public String getString(String s) {
return null;
}

@Nullable
@Override
public Boolean getBoolean(String s) {
return null;
}

@Nullable
@Override
public Integer getInt(String s) {
return null;
}

@Nullable
@Override
public Long getLong(String s) {
return null;
}

@Nullable
@Override
public Double getDouble(String s) {
return null;
}

@Nullable
@Override
public <T> List<T> getScalarList(String s, Class<T> aClass) {
return null;
}

@Nullable
@Override
public StructuredConfigProperties getStructured(String s) {
return null;
}

@Nullable
@Override
public List<StructuredConfigProperties> getStructuredList(String s) {
return null;
}

@Override
public Set<String> getPropertyKeys() {
return Collections.emptySet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@

package io.opentelemetry.javaagent.extension;

import io.opentelemetry.javaagent.bootstrap.internal.StructuredConfigPropertiesBridge;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import java.lang.instrument.Instrumentation;
import java.util.Collections;
import net.bytebuddy.agent.builder.AgentBuilder;

/**
Expand All @@ -25,4 +31,21 @@ public interface AgentListener extends Ordered {
* on an {@link Instrumentation}.
*/
void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk);

/** Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */
static ConfigProperties resolveConfigProperties(
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties sdkConfigProperties =
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
if (sdkConfigProperties != null) {
return sdkConfigProperties;
}
StructuredConfigProperties structuredConfigProperties =
AutoConfigureUtil.getStructuredConfig(autoConfiguredOpenTelemetrySdk);
if (structuredConfigProperties != null) {
return new StructuredConfigPropertiesBridge(structuredConfigProperties);
}
// Should never happen
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
return DefaultConfigProperties.create(Collections.emptyMap());
}
}
Loading
Loading