diff --git a/dd-java-agent/instrumentation/tomcat-5.5/build.gradle b/dd-java-agent/instrumentation/tomcat-5.5/build.gradle index a1fa18fdb31..003e3eb0753 100644 --- a/dd-java-agent/instrumentation/tomcat-5.5/build.gradle +++ b/dd-java-agent/instrumentation/tomcat-5.5/build.gradle @@ -44,6 +44,12 @@ muzzle { assertInverse = true } // org.apache.catalina.connector.CoyoteAdapter introduced in Catalina 5.5 + pass { + name = "tomcat-processtags" + group = "org.apache.tomcat" + module = 'tomcat-catalina-ha' + versions = "[,]" + } } apply from: "$rootDir/gradle/java.gradle" diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServer.groovy b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServer.groovy index 2ac2491d37d..c292cace62a 100644 --- a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServer.groovy +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServer.groovy @@ -1,6 +1,8 @@ import com.google.common.io.Files -import datadog.trace.agent.test.base.HttpServer import datadog.trace.agent.test.base.WebsocketServer +import datadog.trace.api.Config +import datadog.trace.api.ProcessTags +import datadog.trace.util.TraceUtils import jakarta.servlet.ServletContextEvent import jakarta.servlet.ServletContextListener import jakarta.websocket.Session @@ -62,6 +64,12 @@ class TomcatServer implements WebsocketServer { server.start() port = server.service.findConnectors()[0].localPort assert port > 0 + if (Config.get().isExperimentalPropagateProcessTagsEnabled()) { + def serverName = TraceUtils.normalizeTag(server.getEngine().getName()) + assert ProcessTags.getTagsAsStringList().containsAll(["server.type:tomcat", "server.name:" + serverName]) + } else { + assert ProcessTags.getTagsAsStringList() == null + } } @Override diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy index 3d6b9db9fe8..541b1b56380 100644 --- a/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/latestDepTest/groovy/TomcatServletTest.groovy @@ -1,3 +1,5 @@ +import datadog.trace.api.ProcessTags + import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.WEBSOCKET import datadog.trace.agent.test.base.HttpServer @@ -21,6 +23,7 @@ import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.CUSTOM import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.SUCCESS import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.TIMEOUT_ERROR +import static datadog.trace.api.config.GeneralConfig.EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED import static org.junit.Assume.assumeTrue class TomcatServletTest extends AbstractServletTest { @@ -66,6 +69,22 @@ class TomcatServletTest extends AbstractServletTest { true } + boolean testProcessTags() { + false + } + + @Override + protected void configurePreAgent() { + super.configurePreAgent() + injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "${testProcessTags()}") + ProcessTags.reset() + } + + def cleanupSpec() { + injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "false") + ProcessTags.reset() + } + @Override Map expectedExtraErrorInformation(ServerEndpoint endpoint) { if (endpoint.throwsException) { @@ -267,6 +286,11 @@ class TomcatServletEnvEntriesTagTest extends TomcatServletTest { boolean testWebsockets() { false } + + @Override + boolean testProcessTags() { + true + } } diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/main/java/datadog/trace/instrumentation/tomcat/ContainerBaseInstrumentation.java b/dd-java-agent/instrumentation/tomcat-5.5/src/main/java/datadog/trace/instrumentation/tomcat/ContainerBaseInstrumentation.java new file mode 100644 index 00000000000..ba22df5fba6 --- /dev/null +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/main/java/datadog/trace/instrumentation/tomcat/ContainerBaseInstrumentation.java @@ -0,0 +1,49 @@ +package datadog.trace.instrumentation.tomcat; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.ProcessTags; +import net.bytebuddy.asm.Advice; +import org.apache.catalina.core.ContainerBase; +import org.apache.catalina.core.StandardEngine; + +@AutoService(InstrumenterModule.class) +public class ContainerBaseInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { + public ContainerBaseInstrumentation() { + super("tomcat"); + } + + @Override + public String instrumentedType() { + return "org.apache.catalina.core.ContainerBase"; + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + transformer.applyAdvice( + isMethod().and(named("setName")), getClass().getName() + "$SetNameAdvice"); + } + + @Override + public String muzzleDirective() { + return "tomcat-processtags"; + } + + public static class SetNameAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void afterSetName(@Advice.This final ContainerBase engine) { + if (engine instanceof StandardEngine) { + String engineName = engine.getName(); + if (engineName != null) { + ProcessTags.addTag(ProcessTags.SERVER_NAME, engineName); + ProcessTags.addTag(ProcessTags.SERVER_TYPE, "tomcat"); + } + } + } + } +} diff --git a/dd-java-agent/instrumentation/tomcat-5.5/src/test/groovy/TomcatServletTest.groovy b/dd-java-agent/instrumentation/tomcat-5.5/src/test/groovy/TomcatServletTest.groovy index 629b41cf1e8..78f020b6c6e 100644 --- a/dd-java-agent/instrumentation/tomcat-5.5/src/test/groovy/TomcatServletTest.groovy +++ b/dd-java-agent/instrumentation/tomcat-5.5/src/test/groovy/TomcatServletTest.groovy @@ -1,9 +1,12 @@ +import datadog.trace.api.ProcessTags + import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.CUSTOM_EXCEPTION import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.ERROR import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.EXCEPTION import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.REDIRECT import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.TIMEOUT_ERROR +import static datadog.trace.api.config.GeneralConfig.EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED import static org.junit.Assume.assumeTrue import com.google.common.io.Files @@ -85,6 +88,11 @@ abstract class TomcatServletTest extends AbstractServletTest server.start() port = ((server.connectors[0] as Connector).protocolHandler as Http11BaseProtocol).ep.serverSocket.localPort assert port > 0 + if (testProcessTags()) { + assert ProcessTags.getTagsAsStringList().containsAll(["server.type:tomcat", "server.name:test"]) + } else { + assert ProcessTags.getTagsAsStringList() == null + } } @Override @@ -172,6 +180,22 @@ abstract class TomcatServletTest extends AbstractServletTest true } + + boolean testProcessTags() { + false + } + + @Override + protected void configurePreAgent() { + super.configurePreAgent() + injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "${testProcessTags()}") + } + + def cleanupSpec() { + injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "false") + ProcessTags.reset() + } + boolean hasResponseSpan(ServerEndpoint endpoint) { def responseSpans = [REDIRECT, NOT_FOUND, ERROR, EXCEPTION, CUSTOM_EXCEPTION] return responseSpans.contains(endpoint) @@ -316,4 +340,8 @@ class TomcatServletV0Test extends TomcatServletTest implements TestingGenericHtt class TomcatServletV1ForkedTest extends TomcatServletTest implements TestingGenericHttpNamingConventions.ServerV1 { + @Override + boolean testProcessTags() { + true + } } diff --git a/internal-api/src/main/java/datadog/trace/api/ProcessTags.java b/internal-api/src/main/java/datadog/trace/api/ProcessTags.java index 9d863419a89..b13adc60fb1 100644 --- a/internal-api/src/main/java/datadog/trace/api/ProcessTags.java +++ b/internal-api/src/main/java/datadog/trace/api/ProcessTags.java @@ -21,6 +21,7 @@ public class ProcessTags { private static boolean enabled = Config.get().isExperimentalPropagateProcessTagsEnabled(); public static final String CLUSTER_NAME = "cluster.name"; public static final String SERVER_NAME = "server.name"; + public static final String SERVER_TYPE = "server.type"; public static final String ENTRYPOINT_NAME = "entrypoint.name"; public static final String ENTRYPOINT_BASEDIR = "entrypoint.basedir"; public static final String ENTRYPOINT_WORKDIR = "entrypoint.workdir"; @@ -130,6 +131,7 @@ private static boolean fillJbossTags(Map tags) { tags, maybeGetSystemProperty("jboss.home.dir"), "jboss.home")) { insertTagFromSysPropIfPresent(tags, "jboss.server.name", SERVER_NAME); tags.put("jboss.mode", hasSystemProperty("[Standalone]") ? "standalone" : "domain"); + tags.put(SERVER_TYPE, "jboss"); return true; } return false; @@ -138,6 +140,7 @@ tags, maybeGetSystemProperty("jboss.home.dir"), "jboss.home")) { private static boolean fillWebsphereTags(Map tags) { if (insertTagFromEnvIfPresent(tags, "WAS_CELL", CLUSTER_NAME)) { insertTagFromEnvIfPresent(tags, "SERVER_NAME", SERVER_NAME); + tags.put(SERVER_TYPE, "websphere"); return true; } return false; diff --git a/internal-api/src/test/groovy/datadog/trace/api/ProcessTagsForkedTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ProcessTagsForkedTest.groovy index 3a6fddf0b1b..34d530b3fd8 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ProcessTagsForkedTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ProcessTagsForkedTest.groovy @@ -56,8 +56,8 @@ class ProcessTagsForkedTest extends DDSpecification { System.clearProperty("jboss.server.name") where: jbossHome | mode | serverName | expected - "/opt/jboss/myserver" | "[Standalone]" | "standalone" | "entrypoint.basedir:somewhere,entrypoint.name:jboss-modules,entrypoint.type:jar,entrypoint.workdir:.+,jboss.home:myserver,jboss.mode:standalone,server.name:standalone" - "/opt/jboss/myserver" | "[server1:12345]" | "server1" | "entrypoint.basedir:somewhere,entrypoint.name:jboss-modules,entrypoint.type:jar,entrypoint.workdir:.+,jboss.home:myserver,jboss.mode:domain,server.name:server1" + "/opt/jboss/myserver" | "[Standalone]" | "standalone" | "entrypoint.basedir:somewhere,entrypoint.name:jboss-modules,entrypoint.type:jar,entrypoint.workdir:.+,jboss.home:myserver,jboss.mode:standalone,server.name:standalone,server.type:jboss" + "/opt/jboss/myserver" | "[server1:12345]" | "server1" | "entrypoint.basedir:somewhere,entrypoint.name:jboss-modules,entrypoint.type:jar,entrypoint.workdir:.+,jboss.home:myserver,jboss.mode:domain,server.name:server1,server.type:jboss" null | "[Standalone]" | "standalone" | "entrypoint.basedir:somewhere,entrypoint.name:jboss-modules,entrypoint.type:jar,entrypoint.workdir:[^,]+" // don't expect jboss tags since home is missing } @@ -84,8 +84,8 @@ class ProcessTagsForkedTest extends DDSpecification { ProcessTags.reset() where: cellName | serverName | expected - "cell1" | "server1" | "cluster.name:cell1,.+,server.name:server1.*" - null | "server1" | "^((?!cluster.name|server.name).)*\$" + "cell1" | "server1" | "cluster.name:cell1,.+,server.name:server1,server.type:websphere.*" + null | "server1" | "^((?!cluster.name|server.name|server.type).)*\$" } def 'should not calculate process tags by default'() {