-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
- Loading branch information
1 parent
2701c4d
commit 5df8a5a
Showing
10 changed files
with
488 additions
and
95 deletions.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
...s/library/src/main/java/io/opentelemetry/instrumentation/resources/AttributeProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
import io.opentelemetry.api.common.AttributeKey; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* An easier alternative to {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider}, which | ||
* avoids some common pitfalls and boilerplate. | ||
* | ||
* <p>An example of how to use this interface can be found in {@link ManifestResourceProvider}. | ||
*/ | ||
interface AttributeProvider<D> { | ||
Optional<D> readData(); | ||
|
||
void registerAttributes(Builder<D> builder); | ||
|
||
interface Builder<D> { | ||
@CanIgnoreReturnValue | ||
<T> Builder<D> add(AttributeKey<T> key, Function<D, Optional<T>> getter); | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
...y/src/main/java/io/opentelemetry/instrumentation/resources/AttributeResourceProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
import io.opentelemetry.api.common.AttributeKey; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.common.AttributesBuilder; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; | ||
import io.opentelemetry.sdk.resources.Resource; | ||
import io.opentelemetry.semconv.ResourceAttributes; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* An easier alternative to {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider}, which | ||
* avoids some common pitfalls and boilerplate. | ||
* | ||
* <p>An example of how to use this interface can be found in {@link ManifestResourceProvider}. | ||
*/ | ||
@SuppressWarnings({"unchecked", "rawtypes"}) | ||
public abstract class AttributeResourceProvider<D> implements ConditionalResourceProvider { | ||
|
||
private final AttributeProvider<D> attributeProvider; | ||
|
||
public class AttributeBuilder implements AttributeProvider.Builder<D> { | ||
|
||
private AttributeBuilder() {} | ||
|
||
@CanIgnoreReturnValue | ||
@Override | ||
public <T> AttributeBuilder add(AttributeKey<T> key, Function<D, Optional<T>> getter) { | ||
attributeGetters.put((AttributeKey) key, Objects.requireNonNull((Function) getter)); | ||
return this; | ||
} | ||
} | ||
|
||
private static final ThreadLocal<Resource> existingResource = new ThreadLocal<>(); | ||
|
||
private final Map<AttributeKey<Object>, Function<D, Optional<?>>> attributeGetters = | ||
new HashMap<>(); | ||
|
||
public AttributeResourceProvider(AttributeProvider<D> attributeProvider) { | ||
this.attributeProvider = attributeProvider; | ||
attributeProvider.registerAttributes(new AttributeBuilder()); | ||
} | ||
|
||
@Override | ||
public final boolean shouldApply(ConfigProperties config, Resource existing) { | ||
existingResource.set(existing); | ||
|
||
Map<String, String> resourceAttributes = getResourceAttributes(config); | ||
return attributeGetters.keySet().stream() | ||
.allMatch(key -> shouldUpdate(config, existing, key, resourceAttributes)); | ||
} | ||
|
||
@Override | ||
public final Resource createResource(ConfigProperties config) { | ||
return attributeProvider | ||
.readData() | ||
.map( | ||
data -> { | ||
// what should we do here? | ||
// we don't have access to the existing resource | ||
// if the resource provider produces a single key, we can rely on shouldApply | ||
// i.e. this method won't be called if the key is already present | ||
// the thread local is a hack to work around this | ||
Resource existing = | ||
Objects.requireNonNull(existingResource.get(), "call shouldApply first"); | ||
Map<String, String> resourceAttributes = getResourceAttributes(config); | ||
AttributesBuilder builder = Attributes.builder(); | ||
attributeGetters.entrySet().stream() | ||
.filter(e -> shouldUpdate(config, existing, e.getKey(), resourceAttributes)) | ||
.forEach( | ||
e -> | ||
e.getValue() | ||
.apply(data) | ||
.ifPresent(value -> putAttribute(builder, e.getKey(), value))); | ||
return Resource.create(builder.build()); | ||
}) | ||
.orElse(Resource.empty()); | ||
} | ||
|
||
private static <T> void putAttribute(AttributesBuilder builder, AttributeKey<T> key, T value) { | ||
builder.put(key, value); | ||
} | ||
|
||
private static Map<String, String> getResourceAttributes(ConfigProperties config) { | ||
return config.getMap("otel.resource.attributes"); | ||
} | ||
|
||
private static boolean shouldUpdate( | ||
ConfigProperties config, | ||
Resource existing, | ||
AttributeKey<?> key, | ||
Map<String, String> resourceAttributes) { | ||
if (resourceAttributes.containsKey(key.getKey())) { | ||
return false; | ||
} | ||
|
||
Object value = existing.getAttribute(key); | ||
|
||
if (key.equals(ResourceAttributes.SERVICE_NAME)) { | ||
return config.getString("otel.service.name") == null && "unknown_service:java".equals(value); | ||
} | ||
|
||
return value == null; | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
...urces/library/src/main/java/io/opentelemetry/instrumentation/resources/JarPathFinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.InvalidPathException; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
import java.util.function.Supplier; | ||
import javax.annotation.Nullable; | ||
|
||
class JarPathFinder { | ||
private final Supplier<String[]> getProcessHandleArguments; | ||
private final Function<String, String> getSystemProperty; | ||
private final Predicate<Path> fileExists; | ||
|
||
private static class DetectionResult { | ||
private final Optional<Path> jarPath; | ||
|
||
private DetectionResult(Optional<Path> jarPath) { | ||
this.jarPath = jarPath; | ||
} | ||
} | ||
|
||
private static Optional<DetectionResult> detectionResult = Optional.empty(); | ||
|
||
public JarPathFinder() { | ||
this(ProcessArguments::getProcessArguments, System::getProperty, Files::isRegularFile); | ||
} | ||
|
||
// visible for tests | ||
JarPathFinder( | ||
Supplier<String[]> getProcessHandleArguments, | ||
Function<String, String> getSystemProperty, | ||
Predicate<Path> fileExists) { | ||
this.getProcessHandleArguments = getProcessHandleArguments; | ||
this.getSystemProperty = getSystemProperty; | ||
this.fileExists = fileExists; | ||
} | ||
|
||
// visible for testing | ||
static void resetForTest() { | ||
detectionResult = Optional.empty(); | ||
} | ||
|
||
Optional<Path> getJarPath() { | ||
if (!detectionResult.isPresent()) { | ||
detectionResult = Optional.of(new DetectionResult(Optional.ofNullable(detectJarPath()))); | ||
} | ||
return detectionResult.get().jarPath; | ||
} | ||
|
||
private Path detectJarPath() { | ||
Path jarPath = getJarPathFromProcessHandle(); | ||
if (jarPath != null) { | ||
return jarPath; | ||
} | ||
return getJarPathFromSunCommandLine(); | ||
} | ||
|
||
@Nullable | ||
private Path getJarPathFromProcessHandle() { | ||
String[] javaArgs = getProcessHandleArguments.get(); | ||
for (int i = 0; i < javaArgs.length; ++i) { | ||
if ("-jar".equals(javaArgs[i]) && (i < javaArgs.length - 1)) { | ||
return Paths.get(javaArgs[i + 1]); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Nullable | ||
private Path getJarPathFromSunCommandLine() { | ||
// the jar file is the first argument in the command line string | ||
String programArguments = getSystemProperty.apply("sun.java.command"); | ||
if (programArguments == null) { | ||
return null; | ||
} | ||
|
||
// Take the path until the first space. If the path doesn't exist extend it up to the next | ||
// space. Repeat until a path that exists is found or input runs out. | ||
int next = 0; | ||
while (true) { | ||
int nextSpace = programArguments.indexOf(' ', next); | ||
if (nextSpace == -1) { | ||
return pathIfExists(programArguments); | ||
} | ||
Path path = pathIfExists(programArguments.substring(0, nextSpace)); | ||
next = nextSpace + 1; | ||
if (path != null) { | ||
return path; | ||
} | ||
} | ||
} | ||
|
||
@Nullable | ||
private Path pathIfExists(String programArguments) { | ||
Path candidate; | ||
try { | ||
candidate = Paths.get(programArguments); | ||
} catch (InvalidPathException e) { | ||
return null; | ||
} | ||
return fileExists.test(candidate) ? candidate : null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.