-
Notifications
You must be signed in to change notification settings - Fork 867
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto-detect service name based on the jar name
- Loading branch information
Mateusz Rzeszutek
committed
Oct 6, 2022
1 parent
ab0c875
commit 96e9de1
Showing
6 changed files
with
268 additions
and
3 deletions.
There are no files selected for viewing
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
122 changes: 122 additions & 0 deletions
122
...rary/src/main/java/io/opentelemetry/instrumentation/resources/JarServiceNameProvider.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,122 @@ | ||
/* | ||
* 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.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.resource.attributes.ResourceAttributes; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Map; | ||
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 guess the application name from the jar name. */ | ||
@AutoService(ResourceProvider.class) | ||
public class JarServiceNameProvider implements ConditionalResourceProvider { | ||
|
||
private static final Logger logger = Logger.getLogger(JarServiceNameProvider.class.getName()); | ||
|
||
private final Supplier<String[]> getProcessHandleArguments; | ||
private final Function<String, String> getSystemProperty; | ||
private final Predicate<Path> fileExists; | ||
|
||
@SuppressWarnings("unused") // SPI | ||
public JarServiceNameProvider() { | ||
this(ProcessArguments::getProcessArguments, System::getProperty, Files::exists); | ||
} | ||
|
||
// visible for tests | ||
JarServiceNameProvider( | ||
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) { | ||
Path candidate = Paths.get(programArguments); | ||
return fileExists.test(candidate) ? candidate : null; | ||
} | ||
Path candidate = Paths.get(programArguments.substring(0, nextSpace)); | ||
next = nextSpace + 1; | ||
if (fileExists.test(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
} | ||
|
||
private static String getServiceName(Path jarPath) { | ||
String jarName = jarPath.getFileName().toString(); | ||
int dotIndex = jarName.lastIndexOf("."); | ||
return dotIndex == -1 ? jarName : jarName.substring(0, dotIndex); | ||
} | ||
|
||
@Override | ||
public int order() { | ||
// make it run later than the SpringBootServiceNameGuesser | ||
return 300; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...es/library/src/main/java/io/opentelemetry/instrumentation/resources/ProcessArguments.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,15 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
final class ProcessArguments { | ||
|
||
static String[] getProcessArguments() { | ||
return new String[0]; | ||
} | ||
|
||
private ProcessArguments() {} | ||
} |
15 changes: 15 additions & 0 deletions
15
...s/library/src/main/java9/io/opentelemetry/instrumentation/resources/ProcessArguments.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,15 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
final class ProcessArguments { | ||
|
||
static String[] getProcessArguments() { | ||
return ProcessHandle.current().info().arguments().orElseGet(() -> new String[0]); | ||
} | ||
|
||
private ProcessArguments() {} | ||
} |
111 changes: 111 additions & 0 deletions
111
.../src/test/java/io/opentelemetry/instrumentation/resources/JarServiceNameProviderTest.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,111 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; | ||
import static org.junit.jupiter.params.provider.Arguments.arguments; | ||
|
||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import io.opentelemetry.sdk.resources.Resource; | ||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; | ||
import java.nio.file.Path; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.ArgumentsProvider; | ||
import org.junit.jupiter.params.provider.ArgumentsSource; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class JarServiceNameProviderTest { | ||
|
||
@Mock ConfigProperties config; | ||
|
||
@Test | ||
void createResource_empty() { | ||
JarServiceNameProvider serviceNameProvider = | ||
new JarServiceNameProvider(() -> new String[0], prop -> null, file -> false); | ||
|
||
Resource resource = serviceNameProvider.createResource(config); | ||
|
||
assertThat(resource.getAttributes()).isEmpty(); | ||
} | ||
|
||
@Test | ||
void createResource_noJarFileInArgs() { | ||
String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar"}; | ||
JarServiceNameProvider serviceNameProvider = | ||
new JarServiceNameProvider(() -> args, prop -> null, file -> false); | ||
|
||
Resource resource = serviceNameProvider.createResource(config); | ||
|
||
assertThat(resource.getAttributes()).isEmpty(); | ||
} | ||
|
||
@Test | ||
void createResource_processHandleJar() { | ||
String[] args = | ||
new String[] {"-Dtest=42", "-Xmx666m", "-jar", "/path/to/app/my-service.jar", "abc", "def"}; | ||
JarServiceNameProvider serviceNameProvider = | ||
new JarServiceNameProvider(() -> args, prop -> null, file -> false); | ||
|
||
Resource resource = serviceNameProvider.createResource(config); | ||
|
||
assertThat(resource.getAttributes()) | ||
.hasSize(1) | ||
.containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); | ||
} | ||
|
||
@Test | ||
void createResource_processHandleJarWithoutExtension() { | ||
String[] args = new String[] {"-Dtest=42", "-Xmx666m", "-jar", "/path/to/app/my-service"}; | ||
JarServiceNameProvider serviceNameProvider = | ||
new JarServiceNameProvider(() -> args, prop -> null, file -> false); | ||
|
||
Resource resource = serviceNameProvider.createResource(config); | ||
|
||
assertThat(resource.getAttributes()) | ||
.hasSize(1) | ||
.containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); | ||
} | ||
|
||
@ParameterizedTest | ||
@ArgumentsSource(SunCommandLineProvider.class) | ||
void createResource_sunCommandLine(String commandLine, String jarPath) { | ||
Function<String, String> getProperty = | ||
key -> "sun.java.command".equals(key) ? commandLine : null; | ||
Predicate<Path> fileExists = file -> jarPath.equals(file.toString()); | ||
|
||
JarServiceNameProvider serviceNameProvider = | ||
new JarServiceNameProvider(() -> new String[0], getProperty, fileExists); | ||
|
||
Resource resource = serviceNameProvider.createResource(config); | ||
|
||
assertThat(resource.getAttributes()) | ||
.hasSize(1) | ||
.containsEntry(ResourceAttributes.SERVICE_NAME, "my-service"); | ||
} | ||
|
||
static final class SunCommandLineProvider implements ArgumentsProvider { | ||
|
||
@Override | ||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) { | ||
return Stream.of( | ||
arguments("/path/to/my-service.jar", "/path/to/my-service.jar"), | ||
arguments( | ||
"/path to app/with spaces/my-service.jar 1 2 3", | ||
"/path to app/with spaces/my-service.jar"), | ||
arguments( | ||
"/path to app/with spaces/my-service 1 2 3", "/path to app/with spaces/my-service")); | ||
} | ||
} | ||
} |
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