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="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{context="/foo",pool="jdbc/mypool"} 20.0 024 * tomcat_jdbc_connections_active_total{context="/foo",pool="jdbc/mypool"} 2.0 025 * tomcat_jdbc_connections_idle_total{context="/foo",pool="jdbc/mypool"} 6.0 026 * tomcat_jdbc_connections_total{context="/foo",pool="jdbc/mypool"} 8.0 027 * tomcat_jdbc_connections_threadswaiting_total{context="/foo",pool="jdbc/mypool"} 0.0 028 * </pre> * </pre> 029 */ 030 031public class TomcatJdbcPoolExports extends Collector { 032 033 private static final Log log = LogFactory.getLog(TomcatJdbcPoolExports.class); 034 035 public List<MetricFamilySamples> collect() { 036 List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); 037 try { 038 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 039 ObjectName filterName = new ObjectName("tomcat.jdbc:class=org.apache.tomcat.jdbc.pool.DataSource,type=ConnectionPool,*"); 040 Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null); 041 042 if (mBeans.size() > 0) { 043 List<String> labelList = Arrays.asList("pool", "context"); 044 045 GaugeMetricFamily maxActiveConnectionsGauge = new GaugeMetricFamily( 046 "tomcat_jdbc_connections_max", 047 "Maximum number of active connections that can be allocated from this pool at the same time", 048 labelList); 049 050 GaugeMetricFamily activeConnectionsGauge = new GaugeMetricFamily( 051 "tomcat_jdbc_connections_active_total", 052 "Number of active connections allocated from this pool", 053 labelList); 054 055 GaugeMetricFamily idleConnectionsGauge = new GaugeMetricFamily( 056 "tomcat_jdbc_connections_idle_total", 057 "Number of idle connections in this pool", 058 labelList); 059 060 GaugeMetricFamily totalConnectionsGauge = new GaugeMetricFamily( 061 "tomcat_jdbc_connections_total", 062 "Total number of connections in this pool", 063 labelList); 064 065 GaugeMetricFamily waitingThreadsCountGauge = new GaugeMetricFamily( 066 "tomcat_jdbc_waitingthreads_total", 067 "Number of threads waiting for connections from this pool", 068 labelList); 069 070 GaugeMetricFamily borrowedConnectionsGauge = new GaugeMetricFamily( 071 "tomcat_jdbc_connections_borrowed_total", 072 "Number of connections borrowed from this pool", 073 labelList); 074 075 GaugeMetricFamily returnedConnectionsGauge = new GaugeMetricFamily( 076 "tomcat_jdbc_connections_returned_total", 077 "Number of connections returned to this pool", 078 labelList); 079 080 GaugeMetricFamily createdConnectionsGauge = new GaugeMetricFamily( 081 "tomcat_jdbc_connections_created_total", 082 "Number of connections created by this pool", 083 labelList); 084 GaugeMetricFamily releasedConnectionsGauge = new GaugeMetricFamily( 085 "tomcat_jdbc_connections_released_total", 086 "Number of connections released by this pool", 087 labelList); 088 089 GaugeMetricFamily reconnectedConnectionsGauge = new GaugeMetricFamily( 090 "tomcat_jdbc_connections_reconnected_total", 091 "Number of reconnected connections by this pool", 092 labelList); 093 094 GaugeMetricFamily removeAbandonedConnectionsGauge = new GaugeMetricFamily( 095 "tomcat_jdbc_connections_removeabandoned_total", 096 "Number of abandoned connections that have been removed", 097 labelList); 098 099 GaugeMetricFamily releasedIdleConnectionsGauge = new GaugeMetricFamily( 100 "tomcat_jdbc_connections_releasedidle_total", 101 "Number of idle connections that have been released", 102 labelList); 103 104 String[] poolAttributes = new String[]{"MaxActive", "Active", "Idle", "Size", "WaitCount", "BorrowedCount", "ReturnedCount", "CreatedCount", "ReleasedCount", "ReconnectedCount", "RemoveAbandonedCount", "ReleasedIdleCount"}; 105 106 for (final ObjectInstance mBean : mBeans) { 107 List<String> labelValueList = Arrays.asList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]", ""), Optional.ofNullable(mBean.getObjectName().getKeyProperty("context")).orElse("global")); 108 if (mBean.getObjectName().getKeyProperty("connections") == null) { // Tomcat 8.5.33 ignore PooledConnections 109 AttributeList attributeList = server.getAttributes(mBean.getObjectName(), poolAttributes); 110 111 for (Attribute attribute : attributeList.asList()) { 112 switch (attribute.getName()) { 113 case "MaxActive": 114 maxActiveConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue()); 115 break; 116 case "Active": 117 activeConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue()); 118 break; 119 case "Idle": 120 idleConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue()); 121 break; 122 case "Size": 123 totalConnectionsGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue()); 124 break; 125 case "WaitCount": 126 waitingThreadsCountGauge.addMetric(labelValueList, ((Integer) attribute.getValue()).doubleValue()); 127 break; 128 case "BorrowedCount": 129 borrowedConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 130 break; 131 case "ReturnedCount": 132 returnedConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 133 break; 134 case "CreatedCount": 135 createdConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 136 break; 137 case "ReleasedCount": 138 releasedConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 139 break; 140 case "ReconnectedCount": 141 reconnectedConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 142 break; 143 case "RemoveAbandonedCount": 144 removeAbandonedConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 145 break; 146 case "ReleasedIdleCount": 147 releasedIdleConnectionsGauge.addMetric(labelValueList, ((Long) attribute.getValue()).doubleValue()); 148 } 149 } 150 } 151 } 152 mfs.add(maxActiveConnectionsGauge); 153 mfs.add(activeConnectionsGauge); 154 mfs.add(idleConnectionsGauge); 155 mfs.add(totalConnectionsGauge); 156 mfs.add(waitingThreadsCountGauge); 157 mfs.add(borrowedConnectionsGauge); 158 mfs.add(returnedConnectionsGauge); 159 mfs.add(createdConnectionsGauge); 160 mfs.add(releasedConnectionsGauge); 161 mfs.add(reconnectedConnectionsGauge); 162 mfs.add(removeAbandonedConnectionsGauge); 163 mfs.add(releasedIdleConnectionsGauge); 164 } 165 } catch (Exception e) { 166 log.error("Error retrieving metric:" + e.getMessage()); 167 } 168 return mfs; 169 } 170 171 public static boolean isTomcatJdbcUsed() { 172 try { 173 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 174 ObjectName filterName = new ObjectName("tomcat.jdbc:class=org.apache.tomcat.jdbc.pool.DataSource,type=ConnectionPool,*"); 175 Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null); 176 return !mBeans.isEmpty(); 177 } catch (Exception e) { 178 e.printStackTrace(); 179 } 180 return false; 181 } 182 183}