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}