From d0e01fcd18269c7a57f79d94528d4116dd4705da Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 14 Sep 2022 17:45:24 -0500 Subject: [PATCH] Add resource providers (#6574) * Add resource providers * Use autoservice annotation --- .../kotlin/otel.java-conventions.gradle.kts | 1 + dependencyManagement/build.gradle.kts | 1 + instrumentation/resources/library/README.md | 62 +++++++ .../resources/library/build.gradle.kts | 64 ++++++++ .../resources/ContainerResource.java | 122 ++++++++++++++ .../resources/ContainerResourceProvider.java | 20 +++ .../resources/HostResource.java | 47 ++++++ .../resources/HostResourceProvider.java | 20 +++ .../instrumentation/resources/OsResource.java | 91 +++++++++++ .../resources/OsResourceProvider.java | 20 +++ .../instrumentation/resources/ProcessPid.java | 31 ++++ .../resources/ProcessResource.java | 85 ++++++++++ .../resources/ProcessResourceProvider.java | 20 +++ .../resources/ProcessRuntimeResource.java | 53 ++++++ .../ProcessRuntimeResourceProvider.java | 20 +++ .../resources/package-info.java | 13 ++ .../instrumentation/resources/ProcessPid.java | 19 +++ .../resources/ContainerResourceTest.java | 105 ++++++++++++ .../resources/HostResourceTest.java | 46 ++++++ .../resources/OsResourceTest.java | 153 ++++++++++++++++++ .../resources/ProcessResourceTest.java | 66 ++++++++ .../resources/ProcessRuntimeResourceTest.java | 46 ++++++ .../resources/SecurityManagerExtension.java | 60 +++++++ settings.gradle.kts | 1 + 24 files changed, 1166 insertions(+) create mode 100644 instrumentation/resources/library/README.md create mode 100644 instrumentation/resources/library/build.gradle.kts create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java create mode 100644 instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java create mode 100644 instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java create mode 100644 instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java diff --git a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts index 03173d5e2c54..ac6c0a936bb4 100644 --- a/conventions/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/conventions/src/main/kotlin/otel.java-conventions.gradle.kts @@ -136,6 +136,7 @@ testing { implementation("org.junit.jupiter:junit-jupiter-params") runtimeOnly("org.junit.jupiter:junit-jupiter-engine") runtimeOnly("org.junit.vintage:junit-vintage-engine") + implementation("org.junit-pioneer:junit-pioneer") implementation("org.assertj:assertj-core") diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index ab0d2ca31901..0b29def56d51 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -106,6 +106,7 @@ val DEPENDENCIES = listOf( "com.google.code.findbugs:jsr305:3.0.2", "org.apache.groovy:groovy:${groovyVersion}", "org.apache.groovy:groovy-json:${groovyVersion}", + "org.codehaus.mojo:animal-sniffer-annotations:1.22", "org.junit-pioneer:junit-pioneer:1.7.1", "org.objenesis:objenesis:3.2", "org.spockframework:spock-core:2.2-groovy-4.0", diff --git a/instrumentation/resources/library/README.md b/instrumentation/resources/library/README.md new file mode 100644 index 000000000000..d72b3f09d9b4 --- /dev/null +++ b/instrumentation/resources/library/README.md @@ -0,0 +1,62 @@ +# OpenTelemetry Resource Providers + +This package includes some standard `ResourceProvider`s for filling in attributes related to +common environments. Currently, the resources provide the following semantic conventions: + +## Populated attributes + +### Container + +Provider: `io.opentelemetry.instrumentation.resources.ContainerResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/container.md + +Implemented attributes: +- `container.id` + +### Host + +Provider: `io.opentelemetry.instrumentation.resources.HostResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/host.md + +Implemented attributes: +- `host.name` +- `host.arch` + +### Operating System + +Provider: `io.opentelemetry.instrumentation.resources.OsResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/os.md + +Implemented attributes: +- `os.type` +- `os.description` + +### Process + +Implementation: `io.opentelemetry.instrumentation.resources.ProcessResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/process.md#process + +Implemented attributes: +- `process.pid` +- `process.executable.path` (note, we assume the `java` binary is located in the `bin` subfolder of `JAVA_HOME`) +- `process.command_line` (note this includes all system properties and arguments when running) + +### Java Runtime + +Implementation: `io.opentelemetry.instrumentation.resources.ProcessRuntimeResource` + +Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/process.md#process-runtimes + +Implemented attributes: +- `process.runtime.name` +- `process.runtime.version` +- `process.runtime.description` + +## Platforms + +This package currently does not run on Android. It has been verified on OpenJDK and should work on +other server JVM distributions but if you find any issues please let us know. diff --git a/instrumentation/resources/library/build.gradle.kts b/instrumentation/resources/library/build.gradle.kts new file mode 100644 index 000000000000..50ddeea7d86d --- /dev/null +++ b/instrumentation/resources/library/build.gradle.kts @@ -0,0 +1,64 @@ +plugins { + id("otel.library-instrumentation") + id("otel.animalsniffer-conventions") +} + +val mrJarVersions = listOf(11) + +dependencies { + implementation("io.opentelemetry:opentelemetry-sdk-common") + implementation("io.opentelemetry:opentelemetry-semconv") + implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + + compileOnly("org.codehaus.mojo:animal-sniffer-annotations") + + annotationProcessor("com.google.auto.service:auto-service") + compileOnly("com.google.auto.service:auto-service-annotations") + + testImplementation("org.junit.jupiter:junit-jupiter-api") +} + +for (version in mrJarVersions) { + sourceSets { + create("java$version") { + java { + setSrcDirs(listOf("src/main/java$version")) + } + } + } + + tasks { + named("compileJava${version}Java") { + sourceCompatibility = "$version" + targetCompatibility = "$version" + options.release.set(version) + } + } + + configurations { + named("java${version}Implementation") { + extendsFrom(configurations["implementation"]) + } + named("java${version}CompileOnly") { + extendsFrom(configurations["compileOnly"]) + } + } + + dependencies { + // Common to reference classes in main sourceset from Java 9 one (e.g., to return a common interface) + add("java${version}Implementation", files(sourceSets.main.get().output.classesDirs)) + } +} + +tasks { + withType(Jar::class) { + for (version in mrJarVersions) { + into("META-INF/versions/$version") { + from(sourceSets["java$version"].output) + } + } + manifest.attributes( + "Multi-Release" to "true" + ) + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java new file mode 100644 index 000000000000..df988e7e0ac2 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java @@ -0,0 +1,122 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.internal.OtelEncodingUtils; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; + +/** Factory for {@link Resource} retrieving Container ID information. */ +public final class ContainerResource { + + private static final Logger logger = Logger.getLogger(ContainerResource.class.getName()); + private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; + private static final Resource INSTANCE = buildSingleton(UNIQUE_HOST_NAME_FILE_NAME); + + @IgnoreJRERequirement + private static Resource buildSingleton(String uniqueHostNameFileName) { + // can't initialize this statically without running afoul of animalSniffer on paths + return buildResource(Paths.get(uniqueHostNameFileName)); + } + + // package private for testing + static Resource buildResource(Path path) { + String containerId = extractContainerId(path); + + if (containerId == null || containerId.isEmpty()) { + return Resource.empty(); + } else { + return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)); + } + } + + /** Returns resource with container information. */ + public static Resource get() { + return INSTANCE; + } + + /** + * Each line of cgroup file looks like "14:name=systemd:/docker/.../... A hex string is expected + * inside the last section separated by '/' Each segment of the '/' can contain metadata separated + * by either '.' (at beginning) or '-' (at end) + * + * @return containerId + */ + @IgnoreJRERequirement + @Nullable + private static String extractContainerId(Path cgroupFilePath) { + if (!Files.exists(cgroupFilePath) || !Files.isReadable(cgroupFilePath)) { + return null; + } + try (Stream lines = Files.lines(cgroupFilePath)) { + Optional value = + lines + .filter(line -> !line.isEmpty()) + .map(ContainerResource::getIdFromLine) + .filter(Objects::nonNull) + .findFirst(); + if (value.isPresent()) { + return value.get(); + } + } catch (Exception e) { + logger.log(Level.WARNING, "Unable to read file", e); + } + return null; + } + + @Nullable + private static String getIdFromLine(String line) { + // This cgroup output line should have the container id in it + int lastSlashIdx = line.lastIndexOf('/'); + if (lastSlashIdx < 0) { + return null; + } + + String containerId; + + String lastSection = line.substring(lastSlashIdx + 1); + int colonIdx = lastSection.lastIndexOf(':'); + + if (colonIdx != -1) { + // since containerd v1.5.0+, containerId is divided by the last colon when the cgroupDriver is + // systemd: + // https://github.com/containerd/containerd/blob/release/1.5/pkg/cri/server/helpers_linux.go#L64 + containerId = lastSection.substring(colonIdx + 1); + } else { + int startIdx = lastSection.lastIndexOf('-'); + int endIdx = lastSection.lastIndexOf('.'); + + startIdx = startIdx == -1 ? 0 : startIdx + 1; + if (endIdx == -1) { + endIdx = lastSection.length(); + } + if (startIdx > endIdx) { + return null; + } + + containerId = lastSection.substring(startIdx, endIdx); + } + + if (OtelEncodingUtils.isValidBase16String(containerId) && !containerId.isEmpty()) { + return containerId; + } else { + return null; + } + } + + private ContainerResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java new file mode 100644 index 000000000000..b637e0123332 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ResourceProvider}. */ +@AutoService(ResourceProvider.class) +public class ContainerResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ContainerResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java new file mode 100644 index 000000000000..e322927cd940 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** Factory for a {@link Resource} which provides information about the host info. */ +public final class HostResource { + + private static final Resource INSTANCE = buildResource(); + + /** Returns a {@link Resource} which provides information about host. */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + AttributesBuilder attributes = Attributes.builder(); + try { + attributes.put(ResourceAttributes.HOST_NAME, InetAddress.getLocalHost().getHostName()); + } catch (UnknownHostException e) { + // Ignore + } + String hostArch = null; + try { + hostArch = System.getProperty("os.arch"); + } catch (SecurityException t) { + // Ignore + } + if (hostArch != null) { + attributes.put(ResourceAttributes.HOST_ARCH, hostArch); + } + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + private HostResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java new file mode 100644 index 000000000000..4b4dfb7465b8 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link HostResource}. */ +@AutoService(ResourceProvider.class) +public final class HostResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return HostResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java new file mode 100644 index 000000000000..4b223d77c15b --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import javax.annotation.Nullable; + +/** Factory of a {@link Resource} which provides information about the current operating system. */ +public final class OsResource { + + private static final Resource INSTANCE = buildResource(); + + /** + * Returns a factory for a {@link Resource} which provides information about the current operating + * system. + */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + + String os; + try { + os = System.getProperty("os.name"); + } catch (SecurityException t) { + // Security manager enabled, can't provide much os information. + return Resource.empty(); + } + + if (os == null) { + return Resource.empty(); + } + + AttributesBuilder attributes = Attributes.builder(); + + String osName = getOs(os); + if (osName != null) { + attributes.put(ResourceAttributes.OS_TYPE, osName); + } + + String version = null; + try { + version = System.getProperty("os.version"); + } catch (SecurityException e) { + // Ignore + } + String osDescription = version != null ? os + ' ' + version : os; + attributes.put(ResourceAttributes.OS_DESCRIPTION, osDescription); + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + @Nullable + private static String getOs(String os) { + os = os.toLowerCase(); + if (os.startsWith("windows")) { + return ResourceAttributes.OsTypeValues.WINDOWS; + } else if (os.startsWith("linux")) { + return ResourceAttributes.OsTypeValues.LINUX; + } else if (os.startsWith("mac")) { + return ResourceAttributes.OsTypeValues.DARWIN; + } else if (os.startsWith("freebsd")) { + return ResourceAttributes.OsTypeValues.FREEBSD; + } else if (os.startsWith("netbsd")) { + return ResourceAttributes.OsTypeValues.NETBSD; + } else if (os.startsWith("openbsd")) { + return ResourceAttributes.OsTypeValues.OPENBSD; + } else if (os.startsWith("dragonflybsd")) { + return ResourceAttributes.OsTypeValues.DRAGONFLYBSD; + } else if (os.startsWith("hp-ux")) { + return ResourceAttributes.OsTypeValues.HPUX; + } else if (os.startsWith("aix")) { + return ResourceAttributes.OsTypeValues.AIX; + } else if (os.startsWith("solaris")) { + return ResourceAttributes.OsTypeValues.SOLARIS; + } else if (os.startsWith("z/os")) { + return ResourceAttributes.OsTypeValues.Z_OS; + } + return null; + } + + private OsResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java new file mode 100644 index 000000000000..7dd0b4eb9680 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link OsResource}. */ +@AutoService(ResourceProvider.class) +public final class OsResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return OsResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java new file mode 100644 index 000000000000..e6cdaae1e9ca --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.lang.management.ManagementFactory; +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; + +final class ProcessPid { + + private ProcessPid() {} + + @IgnoreJRERequirement + static long getPid() { + // While this is not strictly defined, almost all commonly used JVMs format this as + // pid@hostname. + String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); + int atIndex = runtimeName.indexOf('@'); + if (atIndex >= 0) { + String pidString = runtimeName.substring(0, atIndex); + try { + return Long.parseLong(pidString); + } catch (NumberFormatException ignored) { + // Ignore parse failure. + } + } + return -1; + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java new file mode 100644 index 000000000000..a1e881fe3575 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java @@ -0,0 +1,85 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; + +/** Factory of a {@link Resource} which provides information about the current running process. */ +public final class ProcessResource { + + private static final Resource INSTANCE = buildResource(); + + /** + * Returns a factory for a {@link Resource} which provides information about the current running + * process. + */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + try { + return doBuildResource(); + } catch (LinkageError t) { + // Will only happen on Android, where these attributes generally don't make much sense + // anyways. + return Resource.empty(); + } + } + + @IgnoreJRERequirement + private static Resource doBuildResource() { + AttributesBuilder attributes = Attributes.builder(); + + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + + long pid = ProcessPid.getPid(); + + if (pid >= 0) { + attributes.put(ResourceAttributes.PROCESS_PID, pid); + } + + String javaHome = null; + String osName = null; + try { + javaHome = System.getProperty("java.home"); + osName = System.getProperty("os.name"); + } catch (SecurityException e) { + // Ignore + } + if (javaHome != null) { + StringBuilder executablePath = new StringBuilder(javaHome); + executablePath + .append(File.pathSeparatorChar) + .append("bin") + .append(File.pathSeparatorChar) + .append("java"); + if (osName != null && osName.toLowerCase().startsWith("windows")) { + executablePath.append(".exe"); + } + + attributes.put(ResourceAttributes.PROCESS_EXECUTABLE_PATH, executablePath.toString()); + + StringBuilder commandLine = new StringBuilder(executablePath); + for (String arg : runtime.getInputArguments()) { + commandLine.append(' ').append(arg); + } + attributes.put(ResourceAttributes.PROCESS_COMMAND_LINE, commandLine.toString()); + } + + return Resource.create(attributes.build(), ResourceAttributes.SCHEMA_URL); + } + + private ProcessResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java new file mode 100644 index 000000000000..fda5ef4325ba --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ProcessResource}. */ +@AutoService(ResourceProvider.class) +public final class ProcessResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ProcessResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java new file mode 100644 index 000000000000..1c1222c7ede7 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_DESCRIPTION; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_NAME; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.PROCESS_RUNTIME_VERSION; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +/** Factory of a {@link Resource} which provides information about the Java runtime. */ +public final class ProcessRuntimeResource { + + private static final Resource INSTANCE = buildResource(); + + /** Returns a factory for a {@link Resource} which provides information about the Java runtime. */ + public static Resource get() { + return INSTANCE; + } + + // Visible for testing + static Resource buildResource() { + try { + String name = System.getProperty("java.runtime.name"); + String version = System.getProperty("java.runtime.version"); + String description = + System.getProperty("java.vm.vendor") + + " " + + System.getProperty("java.vm.name") + + " " + + System.getProperty("java.vm.version"); + + return Resource.create( + Attributes.of( + PROCESS_RUNTIME_NAME, + name, + PROCESS_RUNTIME_VERSION, + version, + PROCESS_RUNTIME_DESCRIPTION, + description), + ResourceAttributes.SCHEMA_URL); + } catch (SecurityException ignored) { + return Resource.empty(); + } + } + + private ProcessRuntimeResource() {} +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java new file mode 100644 index 000000000000..1e370fe1a469 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import com.google.auto.service.AutoService; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ProcessRuntimeResource}. */ +@AutoService(ResourceProvider.class) +public final class ProcessRuntimeResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ProcessRuntimeResource.get(); + } +} diff --git a/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java new file mode 100644 index 000000000000..0db0bc965cb2 --- /dev/null +++ b/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider} implementations for common + * resource information. + */ +@ParametersAreNonnullByDefault +package io.opentelemetry.instrumentation.resources; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java b/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java new file mode 100644 index 000000000000..17202bd598be --- /dev/null +++ b/instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.lang.management.ManagementFactory; +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; + +final class ProcessPid { + + private ProcessPid() {} + + @IgnoreJRERequirement + static long getPid() { + return ManagementFactory.getRuntimeMXBean().getPid(); + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java new file mode 100644 index 000000000000..826270c02f78 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java @@ -0,0 +1,105 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static io.opentelemetry.instrumentation.resources.ContainerResource.buildResource; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ContainerResourceTest { + + @Test + void buildResource_Invalid(@TempDir Path tempFolder) throws IOException { + // invalid containerId (non-hex) + Path cgroup = + createCgroup( + tempFolder.resolve("cgroup1"), + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz"); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + + // unrecognized format (last "-" is after last ".") + cgroup = + createCgroup( + tempFolder.resolve("cgroup1"), + "13:name=systemd:/podruntime/docker/kubepods/ac679f8.a8319c8cf7d38e1adf263bc08-d23zzzz"); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + + // test invalid file + cgroup = tempFolder.resolve("DoesNotExist"); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + } + + @Test + void buildResource_Valid(@TempDir Path tempFolder) throws IOException { + // with suffix + Path cgroup = + createCgroup( + tempFolder.resolve("cgroup1"), + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"); + assertThat(getContainerId(buildResource(cgroup))) + .isEqualTo("ac679f8a8319c8cf7d38e1adf263bc08d23"); + + // with prefix and suffix + Path cgroup2 = + createCgroup( + tempFolder.resolve("cgroup2"), + "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff"); + assertThat(getContainerId(buildResource(cgroup2))) + .isEqualTo("dc679f8a8319c8cf7d38e1adf263bc08d23"); + + // just container id + Path cgroup3 = + createCgroup( + tempFolder.resolve("cgroup3"), + "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"); + assertThat(getContainerId(buildResource(cgroup3))) + .isEqualTo("d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"); + + // with prefix + Path cgroup4 = + createCgroup( + tempFolder.resolve("cgroup4"), + "//\n" + + "1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "2:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "3:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23"); + assertThat(getContainerId(buildResource(cgroup4))) + .isEqualTo("dc579f8a8319c8cf7d38e1adf263bc08d23"); + + // with two dashes in prefix + Path cgroup5 = + createCgroup( + tempFolder.resolve("cgroup5"), + "11:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod4415fd05_2c0f_4533_909b_f2180dca8d7c.slice/cri-containerd-713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c.scope"); + assertThat(getContainerId(buildResource(cgroup5))) + .isEqualTo("713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c"); + + // with colon, env: k8s v1.24.0, the cgroupDriver by systemd(default), and container is + // cri-containerd v1.6.8 + Path cgroup6 = + createCgroup( + tempFolder.resolve("cgroup6"), + "11:devices:/system.slice/containerd.service/kubepods-pod87a18a64_b74a_454a_b10b_a4a36059d0a3.slice:cri-containerd:05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0"); + assertThat(getContainerId(buildResource(cgroup6))) + .isEqualTo("05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0"); + } + + private static String getContainerId(Resource resource) { + return resource.getAttribute(ResourceAttributes.CONTAINER_ID); + } + + private static Path createCgroup(Path path, String line) throws IOException { + return Files.write(path, line.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java new file mode 100644 index 000000000000..d85c10d62708 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; + +class HostResourceTest { + @Test + void shouldCreateRuntimeAttributes() { + // when + Resource resource = HostResource.buildResource(); + Attributes attributes = resource.getAttributes(); + + // then + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(attributes.get(ResourceAttributes.HOST_NAME)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.HOST_ARCH)).isNotBlank(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + Attributes attributes = HostResource.buildResource().getAttributes(); + assertThat(attributes.asMap()).containsOnlyKeys(ResourceAttributes.HOST_NAME); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java new file mode 100644 index 000000000000..988514c47105 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java @@ -0,0 +1,153 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetSystemProperty; + +class OsResourceTest { + + @Test + @SetSystemProperty(key = "os.name", value = "Linux 4.11") + void linux() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.LINUX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "MacOS X 11") + void macos() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.DARWIN); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Windows 10") + void windows() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.WINDOWS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "FreeBSD 10") + void freebsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.FREEBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "NetBSD 10") + void netbsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.NETBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "OpenBSD 10") + void openbsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.OPENBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "DragonFlyBSD 10") + void dragonflybsd() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.DRAGONFLYBSD); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "HP-UX 10") + void hpux() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.HPUX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "AIX 10") + void aix() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.AIX); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Solaris 10") + void solaris() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.SOLARIS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Z/OS 10") + void zos() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)) + .isEqualTo(ResourceAttributes.OsTypeValues.Z_OS); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Test + @SetSystemProperty(key = "os.name", value = "RagOS 10") + void unknown() { + Resource resource = OsResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(resource.getAttribute(ResourceAttributes.OS_TYPE)).isNull(); + assertThat(resource.getAttribute(ResourceAttributes.OS_DESCRIPTION)).isNotEmpty(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + assertThat(OsResource.buildResource()).isEqualTo(Resource.empty()); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java new file mode 100644 index 000000000000..8ba697bdc2ba --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetSystemProperty; + +class ProcessResourceTest { + + @Test + @SetSystemProperty(key = "os.name", value = "Linux 4.12") + void notWindows() { + Resource resource = ProcessResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + Attributes attributes = resource.getAttributes(); + + assertThat(attributes.get(ResourceAttributes.PROCESS_PID)).isGreaterThan(1); + assertThat(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)) + .contains("java") + .doesNotEndWith(".exe"); + assertThat(attributes.get(ResourceAttributes.PROCESS_COMMAND_LINE)) + .contains(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)); + } + + @Test + @SetSystemProperty(key = "os.name", value = "Windows 10") + void windows() { + Resource resource = ProcessResource.buildResource(); + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + Attributes attributes = resource.getAttributes(); + + assertThat(attributes.get(ResourceAttributes.PROCESS_PID)).isGreaterThan(1); + assertThat(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)) + .contains("java") + .endsWith(".exe"); + assertThat(attributes.get(ResourceAttributes.PROCESS_COMMAND_LINE)) + .contains(attributes.get(ResourceAttributes.PROCESS_EXECUTABLE_PATH)); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + Attributes attributes = ProcessResource.buildResource().getAttributes(); + assertThat(attributes.asMap()).containsOnlyKeys(ResourceAttributes.PROCESS_PID); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java new file mode 100644 index 000000000000..2893d40a1df7 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; + +class ProcessRuntimeResourceTest { + @Test + void shouldCreateRuntimeAttributes() { + // when + Resource resource = ProcessRuntimeResource.buildResource(); + Attributes attributes = resource.getAttributes(); + + // then + assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_NAME)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_VERSION)).isNotBlank(); + assertThat(attributes.get(ResourceAttributes.PROCESS_RUNTIME_DESCRIPTION)).isNotBlank(); + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + @ExtendWith(SecurityManagerExtension.class) + @EnabledOnJre( + value = {JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_16}, + disabledReason = "Java 17 deprecates security manager for removal") + static class SecurityManagerEnabled { + @Test + void empty() { + assertThat(ProcessRuntimeResource.buildResource()).isEqualTo(Resource.empty()); + } + } +} diff --git a/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java new file mode 100644 index 000000000000..c9c32458b449 --- /dev/null +++ b/instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.resources; + +import java.security.Permission; +import java.util.HashSet; +import java.util.PropertyPermission; +import java.util.Set; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +final class SecurityManagerExtension implements BeforeEachCallback, AfterEachCallback { + + private static final ExtensionContext.Namespace NAMESPACE = + ExtensionContext.Namespace.create(SecurityManagerExtension.class); + + @Override + public void beforeEach(ExtensionContext context) { + context.getStore(NAMESPACE).put(SecurityManager.class, System.getSecurityManager()); + System.setSecurityManager(BlockPropertiesAccess.INSTANCE); + } + + @Override + public void afterEach(ExtensionContext context) { + System.setSecurityManager( + (SecurityManager) context.getStore(NAMESPACE).get(SecurityManager.class)); + } + + private static class BlockPropertiesAccess extends SecurityManager { + + private static final BlockPropertiesAccess INSTANCE = new BlockPropertiesAccess(); + + private static final Set BLOCKED_PROPERTIES = new HashSet<>(); + + static { + BLOCKED_PROPERTIES.add("java.home"); + BLOCKED_PROPERTIES.add("java.runtime.home"); + BLOCKED_PROPERTIES.add("java.runtime.version"); + BLOCKED_PROPERTIES.add("java.vm.name"); + BLOCKED_PROPERTIES.add("java.vm.vendor"); + BLOCKED_PROPERTIES.add("java.vm.version"); + BLOCKED_PROPERTIES.add("os.arch"); + BLOCKED_PROPERTIES.add("os.name"); + BLOCKED_PROPERTIES.add("os.version"); + } + + @Override + public void checkPermission(Permission perm) { + if (perm instanceof PropertyPermission) { + if (BLOCKED_PROPERTIES.contains(perm.getName())) { + throw new SecurityException("Property access not allowed."); + } + } + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 27342880f7e7..1aed36949fbc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -396,6 +396,7 @@ include(":instrumentation:redisson:redisson-3.0:javaagent") include(":instrumentation:redisson:redisson-3.17:javaagent") include(":instrumentation:redisson:redisson-common:javaagent") include(":instrumentation:redisson:redisson-common:testing") +include(":instrumentation:resources:library") include(":instrumentation:restlet:restlet-1.0:javaagent") include(":instrumentation:restlet:restlet-1.0:library") include(":instrumentation:restlet:restlet-1.0:testing")