Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority resource detector #10540

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.resources;

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 java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

public abstract class JarResourceDetector
extends PriorityResourceProvider<JarResourceDetector.NameAndVersion> {
protected static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName());
private static final Pattern JAR_FILE_VERSION_PATTERN = Pattern.compile("[-_]v?\\d.*");
private static final Pattern ANY_DIGIT = Pattern.compile("\\d");
protected final Supplier<String[]> getProcessHandleArguments;
protected final Function<String, String> getSystemProperty;
protected final Predicate<Path> fileExists;

public JarResourceDetector(
Supplier<String[]> getProcessHandleArguments,
Function<String, String> getSystemProperty,
Predicate<Path> fileExists) {
this.getProcessHandleArguments = getProcessHandleArguments;
this.getSystemProperty = getSystemProperty;
this.fileExists = fileExists;
}

private Optional<String> getJarPath() {
Path jarPath = getJarPathFromProcessHandle();
if (jarPath == null) {
jarPath = getJarPathFromSunCommandLine();
}
return Optional.ofNullable(jarPath).map(p -> p.getFileName().toString());
}

@Override
protected final String cacheKey() {
return JarResourceDetector.class.getName();
}

@Override
protected Optional<NameAndVersion> readData() {
return getJarPath()
.flatMap(
jarName -> {
int dotIndex = jarName.lastIndexOf(".");
if (dotIndex == -1 || ANY_DIGIT.matcher(jarName.substring(dotIndex)).find()) {
// don't change if digit it extension, it's probably a version
return Optional.of(new NameAndVersion(jarName, Optional.empty()));
}

return Optional.of(
JarResourceDetector.getNameAndVersion(jarName.substring(0, dotIndex)));
});
}

private static NameAndVersion getNameAndVersion(String jarNameWithoutExtension) {
Matcher matcher = JAR_FILE_VERSION_PATTERN.matcher(jarNameWithoutExtension);
if (matcher.find()) {
int start = matcher.start();
String name = jarNameWithoutExtension.substring(0, start);
String version = jarNameWithoutExtension.substring(start + 1);
return new NameAndVersion(name, Optional.of(version));
}

return new NameAndVersion(jarNameWithoutExtension, Optional.empty());
}

@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;
}

@Override
protected int priority() {
// lower priority than the SpringBootServiceNameDetector
return -1000;
}

protected static class NameAndVersion {
final String name;
final Optional<String> version;

NameAndVersion(String name, Optional<String> version) {
this.name = name;
this.version = version;
}

@Override
public String toString() {
return version
.map(v -> String.format("name: %s, version: %s", name, v))
.orElse(String.format("name: %s", name));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,20 @@
import static java.util.logging.Level.FINE;

import com.google.auto.service.AutoService;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ResourceAttributes;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/**
* A {@link ResourceProvider} that will attempt to detect the application name from the jar name.
*/
@AutoService(ResourceProvider.class)
public final class JarServiceNameDetector implements ConditionalResourceProvider {

private static final Logger logger = Logger.getLogger(JarServiceNameDetector.class.getName());

private final Supplier<String[]> getProcessHandleArguments;
private final Function<String, String> getSystemProperty;
private final Predicate<Path> fileExists;
public final class JarServiceNameDetector extends JarResourceDetector {

@SuppressWarnings("unused") // SPI
public JarServiceNameDetector() {
Expand All @@ -47,89 +33,17 @@ public JarServiceNameDetector() {
Supplier<String[]> getProcessHandleArguments,
Function<String, String> getSystemProperty,
Predicate<Path> fileExists) {
this.getProcessHandleArguments = getProcessHandleArguments;
this.getSystemProperty = getSystemProperty;
this.fileExists = fileExists;
}

@Override
public Resource createResource(ConfigProperties config) {
Path jarPath = getJarPathFromProcessHandle();
if (jarPath == null) {
jarPath = getJarPathFromSunCommandLine();
}
if (jarPath == null) {
return Resource.empty();
}
String serviceName = getServiceName(jarPath);
logger.log(FINE, "Auto-detected service name from the jar file name: {0}", serviceName);
return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, serviceName));
}

@Override
public boolean shouldApply(ConfigProperties config, Resource existing) {
String serviceName = config.getString("otel.service.name");
Map<String, String> resourceAttributes = config.getMap("otel.resource.attributes");
return serviceName == null
&& !resourceAttributes.containsKey(ResourceAttributes.SERVICE_NAME.getKey())
&& "unknown_service:java".equals(existing.getAttribute(ResourceAttributes.SERVICE_NAME));
}

@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;
}

private static String getServiceName(Path jarPath) {
String jarName = jarPath.getFileName().toString();
int dotIndex = jarName.lastIndexOf(".");
return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex);
super(getProcessHandleArguments, getSystemProperty, fileExists);
}

@Override
public int order() {
// make it run later than the SpringBootServiceNameDetector
return 1000;
protected void registerAttributes(
PriorityResourceProvider<NameAndVersion>.AttributeBuilder attributeBuilder) {
attributeBuilder.add(
ResourceAttributes.SERVICE_NAME,
d -> {
logger.log(FINE, "Auto-detected service.name from the jar file: {0}", d.name);
return Optional.of(d.name);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.resources;

import static java.util.logging.Level.FINE;

import com.google.auto.service.AutoService;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.semconv.ResourceAttributes;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* A {@link ResourceProvider} that will attempt to detect the application version from the jar name.
*/
@AutoService(ResourceProvider.class)
public final class JarServiceVersionDetector extends JarResourceDetector {

@SuppressWarnings("unused") // SPI
public JarServiceVersionDetector() {
this(ProcessArguments::getProcessArguments, System::getProperty, Files::isRegularFile);
}

// visible for tests
JarServiceVersionDetector(
Supplier<String[]> getProcessHandleArguments,
Function<String, String> getSystemProperty,
Predicate<Path> fileExists) {
super(getProcessHandleArguments, getSystemProperty, fileExists);
}

@Override
protected void registerAttributes(
PriorityResourceProvider<NameAndVersion>.AttributeBuilder attributeBuilder) {

attributeBuilder.add(
ResourceAttributes.SERVICE_VERSION,
d -> {
logger.log(FINE, "Auto-detected service.version from the jar file: {0}", d.version);
return d.version;
});
}
}
Loading
Loading