001package nl.nlighten.prometheus.tomcat;
002
003import io.prometheus.client.Collector;
004import io.prometheus.client.GaugeMetricFamily;
005import org.apache.juli.logging.Log;
006import org.apache.juli.logging.LogFactory;
007
008import javax.management.*;
009import java.lang.management.ManagementFactory;
010import java.util.*;
011
012/**
013 * Exports Tomcat <a href="https://tomcat.apache.org/tomcat-8.5-doc/jndi-datasource-examples-howto.html#Database_Connection_Pool_(DBCP_2)_Configurations">DBCP2-pool</a> metrics.
014 * <p>
015 * Example usage:
016 * <pre>
017 * {@code
018 *   new TomcatDbcp2PoolExports().register();
019 * }
020 * </pre>
021 * Example metrics being exported:
022 * <pre>
023 *    tomcat_dpcp2_connections_max{pool="jdbc/mypool"} 20.0
024 *    tomcat_dbcp2_connections_active_total{pool="jdbc/mypool"} 2.0
025 *    tomcat_dbcp2_connections_idle_total{pool="jdbc/mypool"} 6.0
026 * </pre>
027 */
028
029public class TomcatDbcp2PoolExports extends Collector {
030
031    private static final Log log = LogFactory.getLog(TomcatDbcp2PoolExports.class);
032
033    public List<MetricFamilySamples> collect() {
034        List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
035        try {
036            final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
037            ObjectName filterName = new ObjectName("Tomcat:class=javax.sql.DataSource,type=DataSource,*");
038            Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null);
039
040            if (mBeans.size() > 0) {
041                List<String> labelList = Arrays.asList("pool", "context");
042
043                GaugeMetricFamily maxActiveConnectionsGauge = new GaugeMetricFamily(
044                        "tomcat_dbcp2_connections_max",
045                        "Maximum number of active connections that can be allocated from this pool at the same time",
046                        labelList);
047
048                GaugeMetricFamily activeConnectionsGauge = new GaugeMetricFamily(
049                        "tomcat_dbcp2_connections_active_total",
050                        "Number of active connections allocated from this pool",
051                        labelList);
052
053                GaugeMetricFamily idleConnectionsGauge = new GaugeMetricFamily(
054                        "tomcat_dbcp2_connections_idle_total",
055                        "Number of idle connections in this pool",
056                        labelList);
057
058                String[] poolAttributes = new String[]{"maxTotal", "numActive", "numIdle"};
059
060
061                for (final ObjectInstance mBean : mBeans) {
062                    if (mBean.getObjectName().getKeyProperty("connectionpool") == null) {
063                        List<String> labelValueList = Arrays.asList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]", ""), Optional.ofNullable(mBean.getObjectName().getKeyProperty("context")).orElse("global"));
064                        if (mBean.getObjectName().getKeyProperty("connections") == null) {  // Tomcat 8.5.33 ignore PooledConnections
065                            AttributeList attributeList = server.getAttributes(mBean.getObjectName(), poolAttributes);
066
067                            for (Attribute attribute : attributeList.asList()) {
068                                switch (attribute.getName()) {
069                                    case "maxTotal":
070                                        maxActiveConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue());
071                                        break;
072                                    case "numActive":
073                                        activeConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue());
074                                        break;
075                                    case "numIdle":
076                                        idleConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue());
077                                }
078                            }
079                        }
080                    }
081                }
082                mfs.add(maxActiveConnectionsGauge);
083                mfs.add(activeConnectionsGauge);
084                mfs.add(idleConnectionsGauge);
085            }
086        }
087        catch (Exception e) {
088            log.error("Error retrieving metric:" + e.getMessage());
089        }
090        return mfs;
091    }
092
093    public static boolean isDbcp2Used() {
094        try {
095            final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
096            ObjectName filterName = new ObjectName("tomcat.jdbc:class=org.apache.tomcat.jdbc.pool.DataSource,type=ConnectionPool,*");
097            Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null);
098            return !mBeans.isEmpty();
099        } catch (Exception e) {
100            e.printStackTrace();
101        }
102        return false;
103    }
104
105}