Browse Source

Add resource providers (#6574)

* Add resource providers

* Use autoservice annotation
jack-berg 2 years ago
parent
commit
6b607c1e18
24 changed files with 1166 additions and 0 deletions
  1. 1 0
      conventions/src/main/kotlin/otel.java-conventions.gradle.kts
  2. 1 0
      dependencyManagement/build.gradle.kts
  3. 62 0
      instrumentation/resources/library/README.md
  4. 64 0
      instrumentation/resources/library/build.gradle.kts
  5. 122 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java
  6. 20 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResourceProvider.java
  7. 47 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResource.java
  8. 20 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/HostResourceProvider.java
  9. 91 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResource.java
  10. 20 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/OsResourceProvider.java
  11. 31 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessPid.java
  12. 85 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResource.java
  13. 20 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessResourceProvider.java
  14. 53 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResource.java
  15. 20 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceProvider.java
  16. 13 0
      instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/package-info.java
  17. 19 0
      instrumentation/resources/library/src/main/java11/io/opentelemetry/instrumentation/resources/ProcessPid.java
  18. 105 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ContainerResourceTest.java
  19. 46 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/HostResourceTest.java
  20. 153 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/OsResourceTest.java
  21. 66 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessResourceTest.java
  22. 46 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/ProcessRuntimeResourceTest.java
  23. 60 0
      instrumentation/resources/library/src/test/java/io/opentelemetry/instrumentation/resources/SecurityManagerExtension.java
  24. 1 0
      settings.gradle.kts

+ 1 - 0
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")

+ 1 - 0
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",

+ 62 - 0
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.

+ 64 - 0
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<JavaCompile>("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"
+    )
+  }
+}

+ 122 - 0
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<String> lines = Files.lines(cgroupFilePath)) {
+      Optional<String> 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() {}
+}

+ 20 - 0
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();
+  }
+}

+ 47 - 0
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() {}
+}

+ 20 - 0
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();
+  }
+}

+ 91 - 0
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() {}
+}

+ 20 - 0
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();
+  }
+}

+ 31 - 0
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;
+  }
+}

+ 85 - 0
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() {}
+}

+ 20 - 0
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();
+  }
+}

+ 53 - 0
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() {}
+}

+ 20 - 0
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();
+  }
+}

+ 13 - 0
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;

+ 19 - 0
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();
+  }
+}

+ 105 - 0
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));
+  }
+}

+ 46 - 0
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);
+    }
+  }
+}

+ 153 - 0
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());
+    }
+  }
+}

+ 66 - 0
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);
+    }
+  }
+}

+ 46 - 0
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());
+    }
+  }
+}

+ 60 - 0
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<String> 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.");
+        }
+      }
+    }
+  }
+}

+ 1 - 0
settings.gradle.kts

@@ -395,6 +395,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")