001package nl.nlighten.prometheus.tomcat; 002 003import io.prometheus.client.Collector; 004import io.prometheus.client.CounterMetricFamily; 005import io.prometheus.client.GaugeMetricFamily; 006import org.apache.catalina.util.ServerInfo; 007import org.apache.juli.logging.Log; 008import org.apache.juli.logging.LogFactory; 009import javax.management.MBeanServer; 010import javax.management.ObjectInstance; 011import javax.management.ObjectName; 012import java.lang.management.ManagementFactory; 013import java.util.*; 014 015/** 016 * Exports Tomcat metrics applicable to most most applications: 017 * 018 * - http session metrics 019 * - request processor metrics 020 * - thread pool metrics 021 * 022 * <p> 023 * Example usage: 024 * <pre> 025 * {@code 026 * new TomcatGenericExports(false).register(); 027 * } 028 * </pre> 029 * Example metrics being exported: 030 * <pre> 031 * tomcat_info{version="7.0.61.0",build="Apr 29 2015 14:58:03 UTC",} 1.0 032 * tomcat_session_active_total{context="/foo",host="default",} 877.0 033 * tomcat_session_rejected_total{context="/foo",host="default",} 0.0 034 * tomcat_session_created_total{context="/foo",host="default",} 24428.0 035 * tomcat_session_expired_total{context="/foo",host="default",} 23832.0 036 * tomcat_session_alivetime_seconds_avg{context="/foo",host="default",} 633.0 037 * tomcat_session_alivetime_seconds_max{context="/foo",host="default",} 9883.0 038 * tomcat_requestprocessor_received_bytes{name="http-bio-0.0.0.0-8080",} 0.0 039 * tomcat_requestprocessor_sent_bytes{name="http-bio-0.0.0.0-8080",} 5056098.0 040 * tomcat_requestprocessor_time_seconds{name="http-bio-0.0.0.0-8080",} 127386.0 041 * tomcat_requestprocessor_error_count{name="http-bio-0.0.0.0-8080",} 0.0 042 * tomcat_requestprocessor_request_count{name="http-bio-0.0.0.0-8080",} 33709.0 043 * tomcat_threads_total{pool="http-bio-0.0.0.0-8080",} 10.0 044 * tomcat_threads_active_total{pool="http-bio-0.0.0.0-8080",} 2.0 045 * tomcat_threads_active_max{pool="http-bio-0.0.0.0-8080",} 200.0 046 * </pre> 047 */ 048 049public class TomcatGenericExports extends Collector { 050 051 private static final Log log = LogFactory.getLog(TomcatGenericExports.class); 052 private String jmxDomain = "Catalina"; 053 054 public TomcatGenericExports(boolean embedded) { 055 if (embedded) { 056 jmxDomain = "Tomcat"; 057 } 058 } 059 private void addRequestProcessorMetrics(List<MetricFamilySamples> mfs) { 060 try { 061 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 062 ObjectName filterName = new ObjectName(jmxDomain + ":type=GlobalRequestProcessor,name=*"); 063 Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null); 064 065 if (mBeans.size() > 0) { 066 List<String> labelNameList = Collections.singletonList("name"); 067 068 GaugeMetricFamily requestProcessorBytesReceivedGauge = new GaugeMetricFamily( 069 "tomcat_requestprocessor_received_bytes", 070 "Number of bytes received by this request processor", 071 labelNameList); 072 073 GaugeMetricFamily requestProcessorBytesSentGauge = new GaugeMetricFamily( 074 "tomcat_requestprocessor_sent_bytes", 075 "Number of bytes sent by this request processor", 076 labelNameList); 077 078 GaugeMetricFamily requestProcessorProcessingTimeGauge = new GaugeMetricFamily( 079 "tomcat_requestprocessor_time_seconds", 080 "The total time spend by this request processor", 081 labelNameList); 082 083 CounterMetricFamily requestProcessorErrorCounter = new CounterMetricFamily( 084 "tomcat_requestprocessor_error_count", 085 "The number of error request served by this request processor", 086 labelNameList); 087 088 CounterMetricFamily requestProcessorRequestCounter = new CounterMetricFamily( 089 "tomcat_requestprocessor_request_count", 090 "The number of request served by this request processor", 091 labelNameList); 092 093 for (final ObjectInstance mBean : mBeans) { 094 List<String> labelValueList = Collections.singletonList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]", "")); 095 096 requestProcessorBytesReceivedGauge.addMetric( 097 labelValueList, 098 ((Long) server.getAttribute(mBean.getObjectName(), "bytesReceived")).doubleValue()); 099 100 requestProcessorBytesSentGauge.addMetric( 101 labelValueList, 102 ((Long) server.getAttribute(mBean.getObjectName(), "bytesSent")).doubleValue()); 103 104 requestProcessorProcessingTimeGauge.addMetric( 105 labelValueList, 106 ((Long) server.getAttribute(mBean.getObjectName(), "processingTime")).doubleValue() / 1000.0); 107 108 requestProcessorErrorCounter.addMetric( 109 labelValueList, 110 ((Integer) server.getAttribute(mBean.getObjectName(), "errorCount")).doubleValue()); 111 112 requestProcessorRequestCounter.addMetric( 113 labelValueList, 114 ((Integer) server.getAttribute(mBean.getObjectName(), "requestCount")).doubleValue()); 115 } 116 117 mfs.add(requestProcessorBytesReceivedGauge); 118 mfs.add(requestProcessorBytesSentGauge); 119 mfs.add(requestProcessorProcessingTimeGauge); 120 mfs.add(requestProcessorRequestCounter); 121 mfs.add(requestProcessorErrorCounter); 122 } 123 } catch (Exception e) { 124 log.error("Error retrieving metric.", e); 125 } 126 } 127 128 129 private void addSessionMetrics(List<MetricFamilySamples> mfs) { 130 try { 131 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 132 ObjectName filterName = new ObjectName(jmxDomain + ":type=Manager,context=*,host=*"); 133 Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null); 134 135 if (mBeans.size() > 0) { 136 List<String> labelNameList = Arrays.asList("host", "context"); 137 138 GaugeMetricFamily activeSessionCountGauge = new GaugeMetricFamily( 139 "tomcat_session_active_total", 140 "Number of active sessions", 141 labelNameList); 142 143 GaugeMetricFamily rejectedSessionCountGauge = new GaugeMetricFamily( 144 "tomcat_session_rejected_total", 145 "Number of sessions rejected due to maxActive being reached", 146 labelNameList); 147 148 GaugeMetricFamily createdSessionCountGauge = new GaugeMetricFamily( 149 "tomcat_session_created_total", 150 "Number of sessions created", 151 labelNameList); 152 153 GaugeMetricFamily expiredSessionCountGauge = new GaugeMetricFamily( 154 "tomcat_session_expired_total", 155 "Number of sessions that expired", 156 labelNameList); 157 158 GaugeMetricFamily sessionAvgAliveTimeGauge = new GaugeMetricFamily( 159 "tomcat_session_alivetime_seconds_avg", 160 "Average time an expired session had been alive", 161 labelNameList); 162 163 GaugeMetricFamily sessionMaxAliveTimeGauge = new GaugeMetricFamily( 164 "tomcat_session_alivetime_seconds_max", 165 "Maximum time an expired session had been alive", 166 labelNameList); 167 168 GaugeMetricFamily contextStateGauge = new GaugeMetricFamily( 169 "tomcat_context_state_started", 170 "Indication if the lifecycle state of this context is STARTED", 171 labelNameList); 172 173 for (final ObjectInstance mBean : mBeans) { 174 List<String> labelValueList = Arrays.asList(mBean.getObjectName().getKeyProperty("host"), mBean.getObjectName().getKeyProperty("context")); 175 176 activeSessionCountGauge.addMetric( 177 labelValueList, 178 ((Integer) server.getAttribute(mBean.getObjectName(), "activeSessions")).doubleValue()); 179 180 rejectedSessionCountGauge.addMetric( 181 labelValueList, 182 ((Integer) server.getAttribute(mBean.getObjectName(), "rejectedSessions")).doubleValue()); 183 184 createdSessionCountGauge.addMetric( 185 labelValueList, 186 ((Long) server.getAttribute(mBean.getObjectName(), "sessionCounter")).doubleValue()); 187 188 expiredSessionCountGauge.addMetric( 189 labelValueList, 190 ((Long) server.getAttribute(mBean.getObjectName(), "expiredSessions")).doubleValue()); 191 192 sessionAvgAliveTimeGauge.addMetric( 193 labelValueList, 194 ((Integer) server.getAttribute(mBean.getObjectName(), "sessionAverageAliveTime")).doubleValue()); 195 196 sessionMaxAliveTimeGauge.addMetric( 197 labelValueList, 198 ((Integer) server.getAttribute(mBean.getObjectName(), "sessionMaxAliveTime")).doubleValue()); 199 200 if (server.getAttribute(mBean.getObjectName(), "stateName").equals("STARTED")) { 201 contextStateGauge.addMetric(labelValueList, 1.0); 202 } else { 203 contextStateGauge.addMetric(labelValueList, 0.0); 204 } 205 } 206 207 mfs.add(activeSessionCountGauge); 208 mfs.add(rejectedSessionCountGauge); 209 mfs.add(createdSessionCountGauge); 210 mfs.add(expiredSessionCountGauge); 211 mfs.add(sessionAvgAliveTimeGauge); 212 mfs.add(sessionMaxAliveTimeGauge); 213 mfs.add(contextStateGauge); 214 } 215 } catch (Exception e) { 216 log.error("Error retrieving metric.", e); 217 } 218 } 219 220 221 private void addThreadPoolMetrics(List<MetricFamilySamples> mfs) { 222 try { 223 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 224 ObjectName filterName = new ObjectName(jmxDomain + ":type=ThreadPool,name=*"); 225 Set<ObjectInstance> mBeans = server.queryMBeans(filterName, null); 226 227 if (mBeans.size() > 0) { 228 List<String> labelList = Collections.singletonList("name"); 229 230 GaugeMetricFamily threadPoolCurrentCountGauge = new GaugeMetricFamily( 231 "tomcat_threads_total", 232 "Number threads in this pool.", 233 labelList); 234 235 GaugeMetricFamily threadPoolActiveCountGauge = new GaugeMetricFamily( 236 "tomcat_threads_active_total", 237 "Number of active threads in this pool.", 238 labelList); 239 240 GaugeMetricFamily threadPoolMaxThreadsGauge = new GaugeMetricFamily( 241 "tomcat_threads_max", 242 "Maximum number of threads allowed in this pool.", 243 labelList); 244 245 GaugeMetricFamily threadPoolConnectionCountGauge = new GaugeMetricFamily( 246 "tomcat_connections_active_total", 247 "Number of connections served by this pool.", 248 labelList); 249 250 GaugeMetricFamily threadPoolMaxConnectionGauge = new GaugeMetricFamily( 251 "tomcat_connections_active_max", 252 "Maximum number of concurrent connections served by this pool.", 253 labelList); 254 255 for (final ObjectInstance mBean : mBeans) { 256 List<String> labelValueList = Collections.singletonList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]", "")); 257 258 threadPoolCurrentCountGauge.addMetric( 259 labelValueList, 260 ((Integer) server.getAttribute(mBean.getObjectName(), "currentThreadCount")).doubleValue()); 261 262 threadPoolActiveCountGauge.addMetric( 263 labelValueList, 264 ((Integer) server.getAttribute(mBean.getObjectName(), "currentThreadsBusy")).doubleValue()); 265 266 threadPoolMaxThreadsGauge.addMetric( 267 labelValueList, 268 ((Integer) server.getAttribute(mBean.getObjectName(), "maxThreads")).doubleValue()); 269 270 threadPoolConnectionCountGauge.addMetric( 271 labelValueList, 272 ((Long) server.getAttribute(mBean.getObjectName(), "connectionCount")).doubleValue()); 273 274 threadPoolMaxConnectionGauge.addMetric( 275 labelValueList, 276 ((Integer) server.getAttribute(mBean.getObjectName(), "maxConnections")).doubleValue()); 277 278 } 279 280 mfs.add(threadPoolCurrentCountGauge); 281 mfs.add(threadPoolActiveCountGauge); 282 mfs.add(threadPoolMaxThreadsGauge); 283 mfs.add(threadPoolConnectionCountGauge); 284 mfs.add(threadPoolMaxConnectionGauge); 285 } 286 } catch (Exception e) { 287 log.error("Error retrieving metric.", e); 288 } 289 } 290 291 292 private void addVersionInfo(List<MetricFamilySamples> mfs) { 293 GaugeMetricFamily tomcatInfo = new GaugeMetricFamily( 294 "tomcat_info", 295 "tomcat version info", 296 Arrays.asList("version", "build")); 297 tomcatInfo.addMetric(Arrays.asList(ServerInfo.getServerNumber(), ServerInfo.getServerBuilt()), 1); 298 mfs.add(tomcatInfo); 299 } 300 301 302 public List<MetricFamilySamples> collect() { 303 List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); 304 addSessionMetrics(mfs); 305 addThreadPoolMetrics(mfs); 306 addRequestProcessorMetrics(mfs); 307 addVersionInfo(mfs); 308 return mfs; 309 310 } 311}