Skip to content

Commit

Permalink
Merge branch 'main' into external-plugin-application-class-loader
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasstadler authored Jan 26, 2022
2 parents 0ac83db + 0a61bca commit 8384c32
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .ci/jobs/apm-agent-java-mbp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
discover-pr-forks-trust: permission
discover-pr-origin: merge-current
discover-tags: true
head-filter-regex: '(master|PR-.*|v\d+\.\d+\.\d+.*)'
head-filter-regex: '(main|PR-.*|v\d+\.\d+\.\d+.*)'
notification-context: 'apm-ci'
repo: apm-agent-java
repo-owner: elastic
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ endif::[]
* Adding agent logging capabilities to our SDK, making it available for external plugins - {pull}2390[#2390]
* When the `MANIFEST.MF` of the main jar contains the `Implementation-Title` attribute, it is used as the default service name - {pull}1921[#1921]
Note: this may change your service names if you relied on the auto-discovery that uses the name of the jar file. If that jar file also contains an `Implementation-Title` attribute in the `MANIFEST.MF` file, the latter will take precedence.
* When the MANIFEST.MF of the main jar contains the Implementation-Version attribute, it is used as the default service version (except for application servers) - {pull}1922[#1922]
* Added support for setting the application class loader when starting a transaction via the public api - {pull}2369[#2369]
[float]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public class CoreConfiguration extends ConfigurationOptionProvider {
"NOTE: Service name auto discovery mechanisms require APM Server 7.0+.")
.addValidator(RegexValidator.of("^[a-zA-Z0-9 _-]+$", "Your service name \"{0}\" must only contain characters " +
"from the ASCII alphabet, numbers, dashes, underscores and spaces"))
.buildWithDefault(ServiceNameUtil.getDefaultServiceName());
.buildWithDefault(ServiceInfo.autoDetected().getServiceName());

private final ConfigurationOption<String> serviceNodeName = ConfigurationOption.stringOption()
.key(SERVICE_NODE_NAME)
Expand Down Expand Up @@ -145,6 +145,7 @@ public class CoreConfiguration extends ConfigurationOptionProvider {
.description("A version string for the currently deployed version of the service. If you don’t version your deployments, " +
"the recommended value for this field is the commit identifier of the deployed revision, " +
"e.g. the output of git rev-parse HEAD.")
.defaultValue(ServiceInfo.autoDetected().getServiceVersion())
.build();

private final ConfigurationOption<String> hostname = ConfigurationOption.stringOption()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,79 @@
*/
package co.elastic.apm.agent.configuration;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.JarFile;

public class ServiceNameUtil {
public class ServiceInfo {

private static final String JAR_VERSION_SUFFIX = "-(\\d+\\.)+(\\d+)(.*)?$";
private static final String DEFAULT_SERVICE_NAME = "unknown-java-service";
private static final ServiceInfo AUTO_DETECTED = autoDetect(System.getProperties());

public static String getDefaultServiceName() {
String lambdaFunctionName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
if (null != lambdaFunctionName) {
return lambdaFunctionName;
private final String serviceName;
@Nullable
private final String serviceVersion;

public ServiceInfo(@Nullable String serviceName) {
this(serviceName, null);
}

public ServiceInfo(@Nullable String serviceName, @Nullable String serviceVersion) {
if (serviceName == null || serviceName.trim().isEmpty()) {
this.serviceName = DEFAULT_SERVICE_NAME;
} else {
return getDefaultServiceName(System.getProperty("sun.java.command"));
this.serviceName = replaceDisallowedServiceNameChars(serviceName).trim();
}
this.serviceVersion = serviceVersion;
}

static String getDefaultServiceName(@Nullable String sunJavaCommand) {
String serviceName = parseSunJavaCommand(sunJavaCommand);
if (serviceName != null) {
serviceName = replaceDisallowedChars(serviceName);
serviceName = serviceName.trim();
}
if (serviceName == null || serviceName.isEmpty()) {
serviceName = "unknown-java-service";
}
public String getServiceName() {
return serviceName;
}

@Nullable
private static String parseSunJavaCommand(@Nullable String command) {
public String getServiceVersion() {
return serviceVersion;
}

public static String replaceDisallowedServiceNameChars(String serviceName) {
return serviceName.replaceAll("[^a-zA-Z0-9 _-]", "-");
}

public static ServiceInfo autoDetected() {
return AUTO_DETECTED;
}

public static ServiceInfo autoDetect(Properties properties) {
String lambdaFunctionName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
if (lambdaFunctionName != null) {
return new ServiceInfo(lambdaFunctionName, null);
} else {
ServiceInfo serviceInfo = createFromSunJavaCommand(properties.getProperty("sun.java.command"));
if (serviceInfo != null) {
return serviceInfo;
}
return new ServiceInfo(null);
}
}

@Nullable
private static ServiceInfo createFromSunJavaCommand(@Nullable String command) {
if (command == null) {
return null;
}
command = command.trim();
String serviceName = getContainerServiceName(command);
if (serviceName != null) {
return serviceName;
return new ServiceInfo(serviceName);
}
if (command.contains(".jar")) {
serviceName = parseJarCommand(command);
return parseJarCommand(command);
} else {
serviceName = parseMainClass(command);
return parseMainClass(command);
}
return serviceName;
}

@Nullable
Expand All @@ -83,31 +111,28 @@ private static String getContainerServiceName(String command) {
return null;
}

public static String replaceDisallowedChars(String serviceName) {
return serviceName.replaceAll("[^a-zA-Z0-9 _-]", "-");
}

@Nullable
private static String parseJarCommand(String command) {
private static ServiceInfo parseJarCommand(String command) {
final String[] commandParts = command.split(" ");
String result = null;
String serviceName = null;
String serviceVersion = null;
for (String commandPart : commandParts) {
if (commandPart.endsWith(".jar")) {
try (JarFile jarFile = new JarFile(commandPart)) {
result = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE);
Attributes mainAttributes = jarFile.getManifest().getMainAttributes();
serviceName = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
serviceVersion = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
} catch (Exception ignored) {
}

if (result == null || result.isEmpty()) {
result = removeVersionFromJar(removePath(removeJarExtension(commandPart)));
if (serviceName == null || serviceName.isEmpty()) {
serviceName = removeVersionFromJar(removePath(removeJarExtension(commandPart)));
}
break;
}
}
return result;
return new ServiceInfo(serviceName, serviceVersion);
}

@Nonnull
private static String removeJarExtension(String commandPart) {
return commandPart.substring(0, commandPart.indexOf(".jar"));
}
Expand All @@ -120,14 +145,14 @@ private static String removeVersionFromJar(String jarFileName) {
return jarFileName.replaceFirst(JAR_VERSION_SUFFIX, "");
}

private static String parseMainClass(String command) {
private static ServiceInfo parseMainClass(String command) {
final String mainClassName;
int indexOfSpace = command.indexOf(' ');
if (indexOfSpace != -1) {
mainClassName = command.substring(0, indexOfSpace);
} else {
mainClassName = command;
}
return mainClassName.substring(mainClassName.lastIndexOf('.') + 1);
return new ServiceInfo(mainClassName.substring(mainClassName.lastIndexOf('.') + 1));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import co.elastic.apm.agent.common.JvmRuntimeInfo;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServiceNameUtil;
import co.elastic.apm.agent.configuration.ServiceInfo;
import co.elastic.apm.agent.context.ClosableLifecycleListenerAdapter;
import co.elastic.apm.agent.context.LifecycleListener;
import co.elastic.apm.agent.impl.error.ErrorCapture;
Expand Down Expand Up @@ -753,7 +753,7 @@ public void overrideServiceNameForClassLoader(@Nullable ClassLoader classLoader,
return;
}

String sanitizedServiceName = ServiceNameUtil.replaceDisallowedChars(serviceName);
String sanitizedServiceName = ServiceInfo.replaceDisallowedServiceNameChars(serviceName);
logger.debug("Using `{}` as the service name for class loader [{}]", sanitizedServiceName, classLoader);
if (!serviceNameByClassLoader.containsKey(classLoader)) {
serviceNameByClassLoader.putIfAbsent(classLoader, sanitizedServiceName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import co.elastic.apm.agent.bci.ElasticApmAgent;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServiceNameUtil;
import co.elastic.apm.agent.configuration.ServiceInfo;
import co.elastic.apm.agent.configuration.converter.ByteValue;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LoggerContext;
Expand Down Expand Up @@ -176,7 +176,7 @@ private LayoutComponentBuilder createLayout(ConfigurationBuilder<BuiltConfigurat
.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%thread] %-5level %logger{36} - %msg{nolookups}%n");
} else {
String serviceName = getValue(CoreConfiguration.SERVICE_NAME, sources, ServiceNameUtil.getDefaultServiceName());
String serviceName = getValue(CoreConfiguration.SERVICE_NAME, sources, ServiceInfo.autoDetected().getServiceName());
return builder.newLayout("EcsLayout")
.addAttribute("eventDataset", serviceName + ".apm");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@

import org.junit.jupiter.api.Test;

import static co.elastic.apm.agent.configuration.ServiceNameUtil.getDefaultServiceName;
import java.util.Properties;

import static org.assertj.core.api.SoftAssertions.assertSoftly;

class ServiceNameUtilTest {
class ServiceInfoTest {

private static String getDefaultServiceName(String sunJavaCommand) {
Properties properties = new Properties();
if (sunJavaCommand != null) {
properties.setProperty("sun.java.command", sunJavaCommand);
}

return ServiceInfo.autoDetect(properties).getServiceName();
}

@Test
void serviceNameShouldBeNormalizedOrDefaults() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import co.elastic.apm.agent.MockReporter;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServiceNameUtil;
import co.elastic.apm.agent.configuration.ServiceInfo;
import co.elastic.apm.agent.configuration.SpyConfiguration;
import co.elastic.apm.agent.configuration.source.ConfigSources;
import co.elastic.apm.agent.impl.error.ErrorCapture;
Expand Down Expand Up @@ -471,7 +471,7 @@ void testNotOverrideServiceNameWhenDefaultServiceNameConfigured() {
startTestRootTransaction().end();

CoreConfiguration coreConfig = localConfig.getConfig(CoreConfiguration.class);
assertThat(ServiceNameUtil.getDefaultServiceName()).isEqualTo(coreConfig.getServiceName());
assertThat(ServiceInfo.autoDetect(System.getProperties()).getServiceName()).isEqualTo(coreConfig.getServiceName());
assertThat(reporter.getFirstTransaction().getTraceContext().getServiceName()).isNull();
if (command != null) {
System.setProperty("sun.java.command", command);
Expand Down

0 comments on commit 8384c32

Please sign in to comment.