diff --git a/.ci/jobs/apm-agent-java-mbp.yml b/.ci/jobs/apm-agent-java-mbp.yml index 320d85ac64..49e5f16ef9 100644 --- a/.ci/jobs/apm-agent-java-mbp.yml +++ b/.ci/jobs/apm-agent-java-mbp.yml @@ -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 diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 94f9b31c10..3cce11ce5c 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -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] diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java index 042be8b969..d6679b1491 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java @@ -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 serviceNodeName = ConfigurationOption.stringOption() .key(SERVICE_NODE_NAME) @@ -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 hostname = ConfigurationOption.stringOption() diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceNameUtil.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java similarity index 57% rename from apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceNameUtil.java rename to apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java index c394c33eba..fafb9f41ad 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceNameUtil.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java @@ -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 @@ -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")); } @@ -120,7 +145,7 @@ 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) { @@ -128,6 +153,6 @@ private static String parseMainClass(String command) { } else { mainClassName = command; } - return mainClassName.substring(mainClassName.lastIndexOf('.') + 1); + return new ServiceInfo(mainClassName.substring(mainClassName.lastIndexOf('.') + 1)); } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java index 244c0112b7..4e129f63a3 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java @@ -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; @@ -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); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/Log4j2ConfigurationFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/Log4j2ConfigurationFactory.java index 9e66869dd4..8f5d91ca5c 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/Log4j2ConfigurationFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/Log4j2ConfigurationFactory.java @@ -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; @@ -176,7 +176,7 @@ private LayoutComponentBuilder createLayout(ConfigurationBuilder