001package nl.nlighten.prometheus.tomcat;
002
003import io.prometheus.client.Collector;
004import io.prometheus.client.GaugeMetricFamily;
005
006import javax.management.MBeanServer;
007import javax.management.ObjectInstance;
008import javax.management.ObjectName;
009import java.lang.management.ManagementFactory;
010import java.util.*;
011
012/**
013 * Exports Tomcat <a href="http://tomcat.apache.org/tomcat-8.5-doc/jdbc-pool.html">jdbc-pool</a> metrics.
014 * <p>
015 * Example usage:
016 * <pre>
017 * {@code
018 *   new TomcatJdbcPoolExports().register();
019 * }
020 * </pre>
021 * Example metrics being exported:
022 * <pre>
023 *    tomcat_jdbc_connections_max{pool="jdbc/mypool"} 20.0
024 *    tomcat_jdbc_connections_active_total{pool="jdbc/mypool"} 2.0
025 *    tomcat_jdbc_connections_idle_total{pool="jdbc/mypool"} 6.0
026 *    tomcat_jdbc_connections_total{pool="jdbc/mypool"} 8.0
027 *    tomcat_jdbc_connections_threadswaiting_total{pool="jdbc/mypool"} 0.0
028 * </pre>
029 */
030
031public class TomcatJdbcPoolExports extends Collector {
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.jdbc:class=org.apache.tomcat.jdbc.pool.DataSource,type=ConnectionPool,*");
038            Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null);
039
040            if (mBeans.size() > 0) {
041                List<String> labelList = Collections.singletonList("pool");
042
043                GaugeMetricFamily maxActiveConnectionsGauge = new GaugeMetricFamily(
044                        "tomcat_jdbc_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_jdbc_connections_active_total",
050                        "Number of active connections allocated from this pool",
051                        labelList);
052
053                GaugeMetricFamily idleConnectionsGauge = new GaugeMetricFamily(
054                        "tomcat_jdbc_connections_idle_total",
055                        "Number of idle connections in this pool",
056                        labelList);
057
058                GaugeMetricFamily totalConnectionsGauge = new GaugeMetricFamily(
059                        "tomcat_jdbc_connections_total",
060                        "Total number of connections in this pool",
061                        labelList);
062
063                GaugeMetricFamily waitingThreadsCountGauge = new GaugeMetricFamily(
064                        "tomcat_jdbc_waitingthreads_total",
065                        "Number of threads waiting for connections from this pool",
066                        labelList);
067
068                GaugeMetricFamily borrowedConnectionsGauge = new GaugeMetricFamily(
069                        "tomcat_jdbc_connections_borrowed_total",
070                        "Number of connections borrowed from this pool",
071                        labelList);
072
073                GaugeMetricFamily returnedConnectionsGauge = new GaugeMetricFamily(
074                        "tomcat_jdbc_connections_returned_total",
075                        "Number of connections returned to this pool",
076                        labelList);
077
078                GaugeMetricFamily createdConnectionsGauge = new GaugeMetricFamily(
079                        "tomcat_jdbc_connections_created_total",
080                        "Number of connections created by this pool",
081                        labelList);
082
083                GaugeMetricFamily releasedConnectionsGauge = new GaugeMetricFamily(
084                        "tomcat_jdbc_connections_released_total",
085                        "Number of connections released by this pool",
086                        labelList);
087
088                GaugeMetricFamily reconnectedConnectionsGauge = new GaugeMetricFamily(
089                        "tomcat_jdbc_connections_reconnected_total",
090                        "Number of reconnected connections by this pool",
091                        labelList);
092
093                GaugeMetricFamily removeAbandonedConnectionsGauge = new GaugeMetricFamily(
094                        "tomcat_jdbc_connections_removeabandoned_total",
095                        "Number of abandoned connections that have been removed",
096                        labelList);
097
098                GaugeMetricFamily releasedIdleConnectionsGauge = new GaugeMetricFamily(
099                        "tomcat_jdbc_connections_releasedidle_total",
100                        "Number of idle connections that have been released",
101                        labelList);
102
103                for (final ObjectInstance mBean : mBeans) {
104                    List<String> labelValueList = Collections.singletonList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]", ""));
105                    if (mBean.getObjectName().getKeyProperty("connections") == null) {  // Tomcat 8.5.33 ignore PooledConnections
106
107                        maxActiveConnectionsGauge.addMetric(
108                                labelValueList,
109                                ((Integer) server.getAttribute(mBean.getObjectName(), "MaxActive")).doubleValue());
110
111                        activeConnectionsGauge.addMetric(
112                                labelValueList,
113                                ((Integer) server.getAttribute(mBean.getObjectName(), "Active")).doubleValue());
114
115                        idleConnectionsGauge.addMetric(
116                                labelValueList,
117                                ((Integer) server.getAttribute(mBean.getObjectName(), "Idle")).doubleValue());
118
119                        totalConnectionsGauge.addMetric(
120                                labelValueList,
121                                ((Integer) server.getAttribute(mBean.getObjectName(), "Size")).doubleValue());
122
123                        waitingThreadsCountGauge.addMetric(
124                                labelValueList,
125                                ((Integer) server.getAttribute(mBean.getObjectName(), "WaitCount")).doubleValue());
126
127                        borrowedConnectionsGauge.addMetric(
128                                labelValueList,
129                                ((Long) server.getAttribute(mBean.getObjectName(), "BorrowedCount")).doubleValue());
130
131                        returnedConnectionsGauge.addMetric(
132                                labelValueList,
133                                ((Long) server.getAttribute(mBean.getObjectName(), "ReturnedCount")).doubleValue());
134
135                        createdConnectionsGauge.addMetric(
136                                labelValueList,
137                                ((Long) server.getAttribute(mBean.getObjectName(), "CreatedCount")).doubleValue());
138
139                        releasedConnectionsGauge.addMetric(
140                                labelValueList,
141                                ((Long) server.getAttribute(mBean.getObjectName(), "ReleasedCount")).doubleValue());
142
143                        reconnectedConnectionsGauge.addMetric(
144                                labelValueList,
145                                ((Long) server.getAttribute(mBean.getObjectName(), "ReconnectedCount")).doubleValue());
146
147                        removeAbandonedConnectionsGauge.addMetric(
148                                labelValueList,
149                                ((Long) server.getAttribute(mBean.getObjectName(), "RemoveAbandonedCount")).doubleValue());
150
151                        releasedIdleConnectionsGauge.addMetric(
152                                labelValueList,
153                                ((Long) server.getAttribute(mBean.getObjectName(), "ReleasedIdleCount")).doubleValue());
154
155                    }
156                }
157                mfs.add(maxActiveConnectionsGauge);
158                mfs.add(activeConnectionsGauge);
159                mfs.add(idleConnectionsGauge);
160                mfs.add(totalConnectionsGauge);
161                mfs.add(waitingThreadsCountGauge);
162                mfs.add(borrowedConnectionsGauge);
163                mfs.add(returnedConnectionsGauge);
164                mfs.add(createdConnectionsGauge);
165                mfs.add(releasedConnectionsGauge);
166                mfs.add(reconnectedConnectionsGauge);
167                mfs.add(removeAbandonedConnectionsGauge);
168                mfs.add(releasedIdleConnectionsGauge);
169            }
170        } catch (Exception e) {
171            e.printStackTrace();
172        }
173        return mfs;
174    }
175
176    public static boolean isTomcatJdbcUsed() {
177        try {
178            final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
179            ObjectName filterName = new ObjectName("tomcat.jdbc:class=org.apache.tomcat.jdbc.pool.DataSource,type=ConnectionPool,*");
180            Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null);
181            return !mBeans.isEmpty();
182        } catch (Exception e) {
183            e.printStackTrace();
184        }
185        return false;
186    }
187
188}