From 19245c67d65029073dc41f039086b1382f5d453b Mon Sep 17 00:00:00 2001 From: tbradellis Date: Sat, 15 Oct 2022 14:51:31 -0700 Subject: [PATCH] add embedded tomcat jmx instrumentation update copyright different name for datasource mbean reorganize tomcat jmx fix tomcat utils check for embedded signal clarify comment for non embedded tc log different paths by name also check datasource b4 adding jmx clarify comment --- .../instrumentation/tomcat/TomcatUtils.java | 38 +++++++-- .../com/newrelic/agent/jmx/JmxApiImpl.java | 6 ++ .../EmbeddedTomcatDataSourceJmxValues.java | 65 ++++++++++++++ .../jmx/values/EmbeddedTomcatJmxValues.java | 84 +++++++++++++++++++ .../agent/jmx/values/TomcatJmxValues.java | 2 +- 5 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatDataSourceJmxValues.java create mode 100644 newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatJmxValues.java diff --git a/instrumentation/tomcat-jmx/src/main/java/com/nr/agent/instrumentation/tomcat/TomcatUtils.java b/instrumentation/tomcat-jmx/src/main/java/com/nr/agent/instrumentation/tomcat/TomcatUtils.java index 83a304819c..c239f584d4 100644 --- a/instrumentation/tomcat-jmx/src/main/java/com/nr/agent/instrumentation/tomcat/TomcatUtils.java +++ b/instrumentation/tomcat-jmx/src/main/java/com/nr/agent/instrumentation/tomcat/TomcatUtils.java @@ -7,22 +7,50 @@ package com.nr.agent.instrumentation.tomcat; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; - import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; + public class TomcatUtils { private static final String JMX_PREFIX = "Catalina"; + private static final String JMX_EMBEDDED_PREFIX = "Tomcat"; + private static final String JMX_EMBEDDED_DATASOURCE_PREFIX = "org.apache.tomcat.jdbc.pool.jmx"; + private static final AtomicBoolean addedJmx = new AtomicBoolean(false); public static void addJmx() { if (System.getProperty("com.sun.aas.installRoot") == null) { if (!addedJmx.getAndSet(true)) { - AgentBridge.jmxApi.addJmxMBeanGroup(JMX_PREFIX); - NewRelic.getAgent().getLogger().log(Level.FINER, "Added JMX for Tomcat"); + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + //The ObjectName is different if embedded tomcat is used. Checking that this object has been registered + //will let us know that we are in an embedded tomcat. The Set returned by queryNames will return size 0 if the ObjectName is not + //found + try { + if (server.queryNames(new ObjectName("Tomcat:type=Server"), null).size() == 1) { + AgentBridge.jmxApi.addJmxMBeanGroup(JMX_EMBEDDED_PREFIX); + NewRelic.getAgent().getLogger().log(Level.FINER, "Added JMX for Tomcat"); + + } + if(server.queryNames(new ObjectName("org.apache.tomcat.jdbc.pool.jmx:name=dataSourceMBean,type=ConnectionPool"), null) + .size() == 1){ + AgentBridge.jmxApi.addJmxMBeanGroup(JMX_EMBEDDED_DATASOURCE_PREFIX); + NewRelic.getAgent().getLogger().log(Level.FINER, "Added JMX for Tomcat dataSourceMbean ConnectionPool"); + + } else { + // It is safe to assume we are in a non embedded Tomcat (Catalina) which uses Catalina for the ObjectName, no need to query. + AgentBridge.jmxApi.addJmxMBeanGroup(JMX_PREFIX); + NewRelic.getAgent().getLogger().log(Level.FINER, "Added JMX for Catalina"); + } + } catch (MalformedObjectNameException e) { + NewRelic.getAgent().getLogger().log(Level.FINEST, e, e.getMessage()); + } } } } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/JmxApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/JmxApiImpl.java index f0d43f720f..12f26e274e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/JmxApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/JmxApiImpl.java @@ -10,6 +10,8 @@ import com.newrelic.agent.Agent; import com.newrelic.agent.bridge.JmxApi; import com.newrelic.agent.jmx.metrics.JmxFrameworkValues; +import com.newrelic.agent.jmx.values.EmbeddedTomcatDataSourceJmxValues; +import com.newrelic.agent.jmx.values.EmbeddedTomcatJmxValues; import com.newrelic.agent.jmx.values.GlassfishJmxValues; import com.newrelic.agent.jmx.values.Jboss7UpJmxValues; import com.newrelic.agent.jmx.values.JettyJmxMetrics; @@ -71,6 +73,10 @@ private JmxFrameworkValues getJmxFrameworkValues(String prefixName) { return new WebsphereLibertyJmxValues(); case TomcatJmxValues.PREFIX: return new TomcatJmxValues(); + case EmbeddedTomcatJmxValues.PREFIX: + return new EmbeddedTomcatJmxValues(); + case EmbeddedTomcatDataSourceJmxValues.PREFIX: + return new EmbeddedTomcatDataSourceJmxValues(); case JettyJmxMetrics.PREFIX: return new JettyJmxMetrics(); case Jboss7UpJmxValues.PREFIX: diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatDataSourceJmxValues.java b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatDataSourceJmxValues.java new file mode 100644 index 0000000000..c8bbe27317 --- /dev/null +++ b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatDataSourceJmxValues.java @@ -0,0 +1,65 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.agent.jmx.values; + +import com.newrelic.agent.MetricNames; +import com.newrelic.agent.jmx.metrics.BaseJmxValue; +import com.newrelic.agent.jmx.metrics.DataSourceJmxMetricGenerator; +import com.newrelic.agent.jmx.metrics.JmxFrameworkValues; +import com.newrelic.agent.jmx.metrics.JmxMetric; + +import java.util.ArrayList; +import java.util.List; + +public class EmbeddedTomcatDataSourceJmxValues extends JmxFrameworkValues { + + /** + * The Mbean group namespaces are different between standalone Tomcat and Embedded Tomcat + * Standalone uses the prefix (aka type) Catalina for queries whereas embedded Tomcat uses the prefix (aka type) Tomcat. + * Our TomcatJmxValues instrumentation only queries MBeans with the Catalina prefix thus queries initiated by embedded Tomcat + * do not provide metrics. Additionally, datasource metrics + * were broken out into type: org.apache.tomcat.pool.jmx for embedded tomcat, hence this Class. See (@EmbeddedTomcatJmxValues) + */ + + public static final String PREFIX = "org.apache.tomcat.jdbc.pool.jmx"; + + private static final int METRIC_COUNT = 1; + + private static final JmxMetric CONNECTIONS_ACTIVE = DataSourceJmxMetricGenerator.CONNECTIONS_ACTIVE.createMetric("NumActive"); + private static final JmxMetric CONNECTIONS_IDLE = DataSourceJmxMetricGenerator.CONNECTIONS_IDLE.createMetric("NumIdle"); + private static final JmxMetric CONNECTIONS_MAX = DataSourceJmxMetricGenerator.CONNECTIONS_MAX.createMetric("MaxActive"); + private static final JmxMetric CONNECTIONS_CREATED = DataSourceJmxMetricGenerator.CONNECTIONS_CREATED.createMetric("CreatedCount"); + + private final List metrics = new ArrayList<>(METRIC_COUNT); + + public EmbeddedTomcatDataSourceJmxValues() { + createMetrics("*"); + } + + public EmbeddedTomcatDataSourceJmxValues(String name) { + createMetrics(name); + + } + + private void createMetrics(String name) { + + metrics.add(new BaseJmxValue("org.apache.tomcat.jdbc.pool.jmx:name=*,type=ConnectionPool", MetricNames.JMX_DATASOURCES + "{name}/", + new JmxMetric[] { CONNECTIONS_ACTIVE, CONNECTIONS_IDLE, CONNECTIONS_MAX, CONNECTIONS_CREATED })); + } + + @Override + public List getFrameworkMetrics() { + return metrics; + } + + @Override + public String getPrefix() { + return PREFIX; + } + +} \ No newline at end of file diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatJmxValues.java b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatJmxValues.java new file mode 100644 index 0000000000..62183eb5b8 --- /dev/null +++ b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/EmbeddedTomcatJmxValues.java @@ -0,0 +1,84 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.newrelic.agent.jmx.values; + +import com.newrelic.agent.MetricNames; +import com.newrelic.agent.jmx.JmxType; +import com.newrelic.agent.jmx.metrics.BaseJmxValue; +import com.newrelic.agent.jmx.metrics.JmxAction; +import com.newrelic.agent.jmx.metrics.JmxFrameworkValues; +import com.newrelic.agent.jmx.metrics.JmxMetric; +import com.newrelic.agent.jmx.metrics.ServerJmxMetricGenerator; + +import java.util.ArrayList; +import java.util.List; + +public class EmbeddedTomcatJmxValues extends JmxFrameworkValues { + + /** + * The Mbean group namespaces are different between standalone Tomcat and Embedded Tomcat + * Standalone uses the prefix (aka type) Catalina for queries whereas embedded Tomcat uses the prefix (aka type) Tomcat. + * Our TomcatJmxValues instrumentation only queries MBeans with the Catalina prefix thus queries initiated by embedded Tomcat + * do not provide metrics. Additionally, datasource metrics + * were broken out into type: org.apache.tomcat.pool.jmx for embedded tomcat. See (@EmbeddedTomcatDataSourceJmxValues) + */ + + public static final String PREFIX = "Tomcat"; + + private static final int METRIC_COUNT = 2; + + // SESSION METRICS (Manager) + private static final JmxMetric ACTIVE_SESSIONS = ServerJmxMetricGenerator.SESSION_ACTIVE_COUNT.createMetric("activeSessions"); + private static final JmxMetric EXPIRED_SESSIONS = ServerJmxMetricGenerator.SESSION_EXPIRED_COUNT.createMetric("expiredSessions"); + private static final JmxMetric REJECTED_SESSIONS = ServerJmxMetricGenerator.SESSION_REJECTED_COUNT.createMetric("rejectedSessions"); + private static final JmxMetric SESSION_ALIVE_TIME = ServerJmxMetricGenerator.SESSION_AVG_ALIVE_TIME.createMetric("sessionAverageAliveTime"); + + // THREAD POOL METRICS + private static final JmxMetric CURRENT_MAX_COUNT = ServerJmxMetricGenerator.MAX_THREAD_POOL_COUNT.createMetric("maxThreads"); + private static final JmxMetric CURRENT_ACTIVE_COUNT = ServerJmxMetricGenerator.ACTIVE_THREAD_POOL_COUNT.createMetric("currentThreadsBusy"); + private static final JmxMetric CURRENT_IDLE_COUNT = JmxMetric.create(new String[] { "currentThreadCount", + "currentThreadsBusy" }, MetricNames.JMX_THREAD_POOL_IDLE, JmxAction.SUBTRACT_ALL_FROM_FIRST, JmxType.SIMPLE); + + private final List metrics = new ArrayList<>(METRIC_COUNT); + + public EmbeddedTomcatJmxValues() { + createMetrics("*"); + } + + public EmbeddedTomcatJmxValues(String name) { + createMetrics(name); + + } + + private void createMetrics(String name) { + /* + * Only used by 7.0+. The manager bean provides information about sessions. sessionCounter is the total number of + * sessions created by this manager. ActiveSessions is the number of active sessions at this moment. + * expiredSesions is the number of sessions that have expired. RejectedSessions is the number of sessions + * rejected due to maxActive being reached. SessionAverageAliveTime is the average time an expired session had + * been alive. + */ + metrics.add(new BaseJmxValue(name + ":type=Manager,context=*,host=*,*", MetricNames.JMX_SESSION + "{context}/", + new JmxMetric[] { ACTIVE_SESSIONS, EXPIRED_SESSIONS, REJECTED_SESSIONS, SESSION_ALIVE_TIME })); + + metrics.add(new BaseJmxValue(name + ":type=ThreadPool,name=*", MetricNames.JMX_THREAD_POOL + "{name}/", + new JmxMetric[] { CURRENT_ACTIVE_COUNT, CURRENT_IDLE_COUNT, CURRENT_MAX_COUNT })); + + } + + @Override + public List getFrameworkMetrics() { + return metrics; + } + + @Override + public String getPrefix() { + return PREFIX; + } + +} \ No newline at end of file diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/TomcatJmxValues.java b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/TomcatJmxValues.java index 788144a91c..c6395e9ec8 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/TomcatJmxValues.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/jmx/values/TomcatJmxValues.java @@ -27,7 +27,7 @@ public class TomcatJmxValues extends JmxFrameworkValues { */ public static final String PREFIX = "Catalina"; - private static final int METRIC_COUNT = 3; + private static final int METRIC_COUNT = 5; // SESSION METRICS private static final JmxMetric ACTIVE_SESSIONS = ServerJmxMetricGenerator.SESSION_ACTIVE_COUNT.createMetric("activeSessions");