This is an automated email from the ASF dual-hosted git repository.

sebawagner pushed a commit to branch 
feature/OPENMEETINGS-2567-investigate-performance-monitoring
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to 
refs/heads/feature/OPENMEETINGS-2567-investigate-performance-monitoring by this 
push:
     new e2e2cd5  OPENMEETINGS-2567 Enable basic Prometheus JVM statistics.
e2e2cd5 is described below

commit e2e2cd56a103498602f072a650b15f12ebb81688
Author: Sebastian Wagner <[email protected]>
AuthorDate: Wed Feb 3 16:16:02 2021 +1300

    OPENMEETINGS-2567 Enable basic Prometheus JVM statistics.
---
 openmeetings-web/pom.xml                           |  12 +
 .../apache/openmeetings/web/app/Application.java   |   2 +
 .../web/util/logging/TomcatGenericExports.java     | 287 +++++++++++++++++++++
 openmeetings-web/src/main/webapp/WEB-INF/web.xml   |  33 +++
 pom.xml                                            |  16 ++
 5 files changed, 350 insertions(+)

diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index 8fe2685..81a582f 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -599,6 +599,18 @@
                        <artifactId>spring-orm</artifactId>
                </dependency>
                <dependency>
+                       <groupId>io.prometheus</groupId>
+                       <artifactId>simpleclient</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>io.prometheus</groupId>
+                       <artifactId>simpleclient_servlet</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.tomcat</groupId>
+                       <artifactId>tomcat-catalina</artifactId>
+               </dependency>
+               <dependency>
                        <groupId>org.wicketstuff</groupId>
                        <artifactId>wicketstuff-dashboard-core</artifactId>
                        <version>${wickets.version}</version>
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
index 5debb49..7466f47 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
@@ -94,6 +94,7 @@ import 
org.apache.openmeetings.web.user.record.PngRecordingResourceReference;
 import org.apache.openmeetings.web.util.GroupLogoResourceReference;
 import org.apache.openmeetings.web.util.ProfileImageResourceReference;
 import org.apache.openmeetings.web.util.UserDashboardPersister;
+import org.apache.openmeetings.web.util.logging.TomcatGenericExports;
 import org.apache.wicket.DefaultPageManagerProvider;
 import org.apache.wicket.Localizer;
 import org.apache.wicket.Page;
@@ -192,6 +193,7 @@ public class Application extends 
AuthenticatedWebApplication implements IApplica
 
        @Override
        protected void init() {
+               new TomcatGenericExports(false).register();
                setWicketApplicationName(super.getName());
                getSecuritySettings().setAuthenticationStrategy(new 
OmAuthenticationStrategy());
                
getApplicationSettings().setAccessDeniedPage(AccessDeniedPage.class);
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/logging/TomcatGenericExports.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/logging/TomcatGenericExports.java
new file mode 100644
index 0000000..e910dc4
--- /dev/null
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/logging/TomcatGenericExports.java
@@ -0,0 +1,287 @@
+package org.apache.openmeetings.web.util.logging;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CounterMetricFamily;
+import io.prometheus.client.GaugeMetricFamily;
+import org.apache.catalina.util.ServerInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.*;
+import java.lang.management.ManagementFactory;
+import java.util.*;
+
+/**
+ * Exports Tomcat metrics applicable to most most applications:
+ *
+ * - http session metrics - request processor metrics - thread pool metrics
+ *
+ * <p>
+ * Example usage:
+ *
+ * <pre>
+ * {@code
+ *   new TomcatGenericExports(false).register();
+ * }
+ * </pre>
+ *
+ * Example metrics being exported:
+ *
+ * <pre>
+ * tomcat_info{version="7.0.61.0",build="Apr 29 2015 14:58:03 UTC",} 1.0
+ * tomcat_session_active_total{context="/foo",host="default",} 877.0
+ * tomcat_session_rejected_total{context="/foo",host="default",} 0.0
+ * tomcat_session_created_total{context="/foo",host="default",} 24428.0
+ * tomcat_session_expired_total{context="/foo",host="default",} 23832.0
+ * tomcat_session_alivetime_seconds_avg{context="/foo",host="default",} 633.0
+ * tomcat_session_alivetime_seconds_max{context="/foo",host="default",} 9883.0
+ * tomcat_requestprocessor_received_bytes{name="http-bio-0.0.0.0-8080",} 0.0
+ * tomcat_requestprocessor_sent_bytes{name="http-bio-0.0.0.0-8080",} 5056098.0
+ * tomcat_requestprocessor_time_seconds{name="http-bio-0.0.0.0-8080",} 127386.0
+ * tomcat_requestprocessor_error_count{name="http-bio-0.0.0.0-8080",} 0.0
+ * tomcat_requestprocessor_request_count{name="http-bio-0.0.0.0-8080",} 33709.0
+ * tomcat_threads_total{pool="http-bio-0.0.0.0-8080",} 10.0
+ * tomcat_threads_active_total{pool="http-bio-0.0.0.0-8080",} 2.0
+ * tomcat_threads_active_max{pool="http-bio-0.0.0.0-8080",} 200.0
+ * </pre>
+ */
+
+public class TomcatGenericExports extends Collector {
+
+       private static final Logger log = 
LoggerFactory.getLogger(TomcatGenericExports.class);
+       private String jmxDomain = "Catalina";
+
+       public TomcatGenericExports(boolean embedded) {
+               if (embedded) {
+                       jmxDomain = "Tomcat";
+               }
+       }
+
+       private void addRequestProcessorMetrics(List<MetricFamilySamples> mfs) {
+               try {
+                       final MBeanServer server = 
ManagementFactory.getPlatformMBeanServer();
+                       ObjectName filterName = new ObjectName(jmxDomain + 
":type=GlobalRequestProcessor,name=*");
+                       Set<ObjectInstance> mBeans = 
server.queryMBeans(filterName, null);
+
+                       if (mBeans.size() > 0) {
+                               List<String> labelNameList = 
Collections.singletonList("name");
+
+                               GaugeMetricFamily 
requestProcessorBytesReceivedGauge = new GaugeMetricFamily(
+                                               
"tomcat_requestprocessor_received_bytes", "Number of bytes received by this 
request processor",
+                                               labelNameList);
+
+                               GaugeMetricFamily 
requestProcessorBytesSentGauge = new GaugeMetricFamily(
+                                               
"tomcat_requestprocessor_sent_bytes", "Number of bytes sent by this request 
processor",
+                                               labelNameList);
+
+                               GaugeMetricFamily 
requestProcessorProcessingTimeGauge = new GaugeMetricFamily(
+                                               
"tomcat_requestprocessor_time_seconds", "The total time spend by this request 
processor",
+                                               labelNameList);
+
+                               CounterMetricFamily 
requestProcessorErrorCounter = new CounterMetricFamily(
+                                               
"tomcat_requestprocessor_error_count",
+                                               "The number of error request 
served by this request processor", labelNameList);
+
+                               CounterMetricFamily 
requestProcessorRequestCounter = new CounterMetricFamily(
+                                               
"tomcat_requestprocessor_request_count",
+                                               "The number of request served 
by this request processor", labelNameList);
+
+                               for (final ObjectInstance mBean : mBeans) {
+                                       List<String> labelValueList = 
Collections
+                                                       
.singletonList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]",
 ""));
+
+                                       
requestProcessorBytesReceivedGauge.addMetric(labelValueList,
+                                                       ((Long) 
server.getAttribute(mBean.getObjectName(), "bytesReceived")).doubleValue());
+
+                                       
requestProcessorBytesSentGauge.addMetric(labelValueList,
+                                                       ((Long) 
server.getAttribute(mBean.getObjectName(), "bytesSent")).doubleValue());
+
+                                       
requestProcessorProcessingTimeGauge.addMetric(labelValueList,
+                                                       ((Long) 
server.getAttribute(mBean.getObjectName(), "processingTime")).doubleValue()
+                                                                       / 
1000.0);
+
+                                       
requestProcessorErrorCounter.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "errorCount")).doubleValue());
+
+                                       
requestProcessorRequestCounter.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "requestCount")).doubleValue());
+                               }
+
+                               mfs.add(requestProcessorBytesReceivedGauge);
+                               mfs.add(requestProcessorBytesSentGauge);
+                               mfs.add(requestProcessorProcessingTimeGauge);
+                               mfs.add(requestProcessorRequestCounter);
+                               mfs.add(requestProcessorErrorCounter);
+                       }
+               } catch (Exception e) {
+                       log.error("Error retrieving metric.", e);
+               }
+       }
+
+       private void addSessionMetrics(List<MetricFamilySamples> mfs) {
+               try {
+                       final MBeanServer server = 
ManagementFactory.getPlatformMBeanServer();
+                       ObjectName filterName = new ObjectName(jmxDomain + 
":type=Manager,context=*,host=*");
+                       Set<ObjectInstance> mBeans = 
server.queryMBeans(filterName, null);
+
+                       if (mBeans.size() > 0) {
+                               List<String> labelNameList = 
Arrays.asList("host", "context");
+
+                               GaugeMetricFamily activeSessionCountGauge = new 
GaugeMetricFamily("tomcat_session_active_total",
+                                               "Number of active sessions", 
labelNameList);
+
+                               GaugeMetricFamily rejectedSessionCountGauge = 
new GaugeMetricFamily("tomcat_session_rejected_total",
+                                               "Number of sessions rejected 
due to maxActive being reached", labelNameList);
+
+                               GaugeMetricFamily createdSessionCountGauge = 
new GaugeMetricFamily("tomcat_session_created_total",
+                                               "Number of sessions created", 
labelNameList);
+
+                               GaugeMetricFamily expiredSessionCountGauge = 
new GaugeMetricFamily("tomcat_session_expired_total",
+                                               "Number of sessions that 
expired", labelNameList);
+
+                               GaugeMetricFamily sessionAvgAliveTimeGauge = 
new GaugeMetricFamily(
+                                               
"tomcat_session_alivetime_seconds_avg", "Average time an expired session had 
been alive",
+                                               labelNameList);
+
+                               GaugeMetricFamily sessionMaxAliveTimeGauge = 
new GaugeMetricFamily(
+                                               
"tomcat_session_alivetime_seconds_max", "Maximum time an expired session had 
been alive",
+                                               labelNameList);
+
+                               GaugeMetricFamily contextStateGauge = new 
GaugeMetricFamily("tomcat_context_state_started",
+                                               "Indication if the lifecycle 
state of this context is STARTED", labelNameList);
+
+                               for (final ObjectInstance mBean : mBeans) {
+                                       List<String> labelValueList = 
Arrays.asList(mBean.getObjectName().getKeyProperty("host"),
+                                                       
mBean.getObjectName().getKeyProperty("context"));
+
+                                       
activeSessionCountGauge.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "activeSessions")).doubleValue());
+
+                                       
rejectedSessionCountGauge.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "rejectedSessions")).doubleValue());
+
+                                       
createdSessionCountGauge.addMetric(labelValueList,
+                                                       ((Long) 
server.getAttribute(mBean.getObjectName(), "sessionCounter")).doubleValue());
+
+                                       
expiredSessionCountGauge.addMetric(labelValueList,
+                                                       ((Long) 
server.getAttribute(mBean.getObjectName(), "expiredSessions")).doubleValue());
+
+                                       
sessionAvgAliveTimeGauge.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "sessionAverageAliveTime"))
+                                                                       
.doubleValue());
+
+                                       
sessionMaxAliveTimeGauge.addMetric(labelValueList,
+                                                       ((Integer) 
server.getAttribute(mBean.getObjectName(), "sessionMaxAliveTime"))
+                                                                       
.doubleValue());
+
+                                       if 
(server.getAttribute(mBean.getObjectName(), "stateName").equals("STARTED")) {
+                                               
contextStateGauge.addMetric(labelValueList, 1.0);
+                                       } else {
+                                               
contextStateGauge.addMetric(labelValueList, 0.0);
+                                       }
+                               }
+
+                               mfs.add(activeSessionCountGauge);
+                               mfs.add(rejectedSessionCountGauge);
+                               mfs.add(createdSessionCountGauge);
+                               mfs.add(expiredSessionCountGauge);
+                               mfs.add(sessionAvgAliveTimeGauge);
+                               mfs.add(sessionMaxAliveTimeGauge);
+                               mfs.add(contextStateGauge);
+                       }
+               } catch (Exception e) {
+                       log.error("Error retrieving metric.", e);
+               }
+       }
+
+       private void addThreadPoolMetrics(List<MetricFamilySamples> mfs) {
+               try {
+                       final MBeanServer server = 
ManagementFactory.getPlatformMBeanServer();
+                       ObjectName filterName = new ObjectName(jmxDomain + 
":type=ThreadPool,name=*");
+                       Set<ObjectInstance> mBeans = 
server.queryMBeans(filterName, null);
+
+                       if (mBeans.size() > 0) {
+                               List<String> labelList = 
Collections.singletonList("name");
+
+                               GaugeMetricFamily threadPoolCurrentCountGauge = 
new GaugeMetricFamily("tomcat_threads_total",
+                                               "Number threads in this pool.", 
labelList);
+
+                               GaugeMetricFamily threadPoolActiveCountGauge = 
new GaugeMetricFamily("tomcat_threads_active_total",
+                                               "Number of active threads in 
this pool.", labelList);
+
+                               GaugeMetricFamily threadPoolMaxThreadsGauge = 
new GaugeMetricFamily("tomcat_threads_max",
+                                               "Maximum number of threads 
allowed in this pool.", labelList);
+
+                               GaugeMetricFamily 
threadPoolConnectionCountGauge = new GaugeMetricFamily(
+                                               
"tomcat_connections_active_total", "Number of connections served by this 
pool.", labelList);
+
+                               GaugeMetricFamily threadPoolMaxConnectionGauge 
= new GaugeMetricFamily("tomcat_connections_active_max",
+                                               "Maximum number of concurrent 
connections served by this pool.", labelList);
+
+                               String[] genericAttributes = new String[] { 
"currentThreadCount", "currentThreadsBusy", "maxThreads",
+                                               "connectionCount", 
"maxConnections" };
+
+                               for (final ObjectInstance mBean : mBeans) {
+                                       List<String> labelValueList = 
Collections
+                                                       
.singletonList(mBean.getObjectName().getKeyProperty("name").replaceAll("[\"\\\\]",
 ""));
+                                       AttributeList attributeList = 
server.getAttributes(mBean.getObjectName(), genericAttributes);
+                                       for (Attribute attribute : 
attributeList.asList()) {
+                                               switch (attribute.getName()) {
+                                               case "currentThreadCount":
+                                                       
threadPoolCurrentCountGauge.addMetric(labelValueList,
+                                                                       
((Integer) attribute.getValue()).doubleValue());
+                                                       break;
+                                               case "currentThreadsBusy":
+                                                       
threadPoolActiveCountGauge.addMetric(labelValueList,
+                                                                       
((Integer) attribute.getValue()).doubleValue());
+                                                       break;
+                                               case "maxThreads":
+                                                       
threadPoolMaxThreadsGauge.addMetric(labelValueList,
+                                                                       
((Integer) attribute.getValue()).doubleValue());
+                                                       break;
+                                               case "connectionCount":
+                                                       
threadPoolConnectionCountGauge.addMetric(labelValueList,
+                                                                       ((Long) 
attribute.getValue()).doubleValue());
+                                                       break;
+                                               case "maxConnections":
+                                                       
threadPoolMaxConnectionGauge.addMetric(labelValueList,
+                                                                       
((Integer) attribute.getValue()).doubleValue());
+
+                                               }
+                                       }
+                               }
+
+                               addNonEmptyMetricFamily(mfs, 
threadPoolCurrentCountGauge);
+                               addNonEmptyMetricFamily(mfs, 
threadPoolActiveCountGauge);
+                               addNonEmptyMetricFamily(mfs, 
threadPoolMaxThreadsGauge);
+                               addNonEmptyMetricFamily(mfs, 
threadPoolConnectionCountGauge);
+                               addNonEmptyMetricFamily(mfs, 
threadPoolMaxConnectionGauge);
+                       }
+               } catch (Exception e) {
+                       log.error("Error retrieving metric:" + e.getMessage());
+               }
+       }
+
+       private void addVersionInfo(List<MetricFamilySamples> mfs) {
+               GaugeMetricFamily tomcatInfo = new 
GaugeMetricFamily("tomcat_info", "tomcat version info",
+                               Arrays.asList("version", "build"));
+               
tomcatInfo.addMetric(Arrays.asList(ServerInfo.getServerNumber(), 
ServerInfo.getServerBuilt()), 1);
+               mfs.add(tomcatInfo);
+       }
+
+       private void addNonEmptyMetricFamily(List<MetricFamilySamples> mfs, 
GaugeMetricFamily metricFamily) {
+               if (metricFamily.samples.size() > 0) {
+                       mfs.add(metricFamily);
+               }
+       }
+
+       public List<MetricFamilySamples> collect() {
+               List<MetricFamilySamples> mfs = new 
ArrayList<MetricFamilySamples>();
+               addSessionMetrics(mfs);
+               addThreadPoolMetrics(mfs);
+               addRequestProcessorMetrics(mfs);
+               addVersionInfo(mfs);
+               return mfs;
+
+       }
+}
diff --git a/openmeetings-web/src/main/webapp/WEB-INF/web.xml 
b/openmeetings-web/src/main/webapp/WEB-INF/web.xml
index 62e5e2a..265231f 100644
--- a/openmeetings-web/src/main/webapp/WEB-INF/web.xml
+++ b/openmeetings-web/src/main/webapp/WEB-INF/web.xml
@@ -54,6 +54,30 @@
                        
<param-value>css,docs,images,js,persistence,public,screenshare,data,services</param-value>
                </init-param>
        </filter>
+       
+       <filter>
+         <filter-name>prometheusFilter</filter-name>
+         <filter-class>io.prometheus.client.filter.MetricsFilter</filter-class>
+         <init-param>
+           <param-name>metric-name</param-name>
+           <param-value>webapp_metrics_filter</param-value>
+         </init-param>
+         <init-param>
+           <param-name>help</param-name>
+           <param-value>This is the help for your metrics filter</param-value>
+         </init-param>
+         <init-param>
+           <param-name>buckets</param-name>
+           
<param-value>0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10</param-value>
+         </init-param>
+         <!-- Optionally override path components; anything less than 1 (1 is 
the default)
+              means full granularity -->
+         <init-param>
+           <param-name>path-components</param-name>
+           <param-value>0</param-value>
+         </init-param>
+       </filter>
+       
        <filter-mapping>
                <filter-name>OpenmeetingsApplication</filter-name>
                <url-pattern>/*</url-pattern>
@@ -77,6 +101,15 @@
                <servlet-name>CXFServlet</servlet-name>
                <url-pattern>/services/*</url-pattern>
        </servlet-mapping>
+       
+       <servlet>
+               <servlet-name>metrics</servlet-name>
+               
<servlet-class>io.prometheus.client.exporter.MetricsServlet</servlet-class>
+       </servlet>
+       <servlet-mapping>
+           <servlet-name>metrics</servlet-name>
+           <url-pattern>/services/metrics/</url-pattern>
+       </servlet-mapping>
 
        <mime-mapping>
                <extension>inc</extension>
diff --git a/pom.xml b/pom.xml
index 5ebedee..3770f02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,6 +84,7 @@
                <tomcat.version>9.0.40</tomcat.version>
                <ical4j.version>3.0.18</ical4j.version>
                <cxf.version>3.4.1</cxf.version>
+               <io.prometheus.version>0.10.0</io.prometheus.version>
                <simple-xml.version>2.7.1</simple-xml.version>
                <jettison.version>1.4.1</jettison.version>
                <site.basedir>${project.basedir}</site.basedir>
@@ -530,6 +531,21 @@
                                <version>${cxf.version}</version>
                        </dependency>
                        <dependency>
+                               <groupId>io.prometheus</groupId>
+                               <artifactId>simpleclient</artifactId>
+                               <version>${io.prometheus.version}</version>
+                       </dependency>
+                       <dependency>
+                         <groupId>io.prometheus</groupId>
+                         <artifactId>simpleclient_servlet</artifactId>
+                         <version>${io.prometheus.version}</version>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.apache.tomcat</groupId>
+                               <artifactId>tomcat-catalina</artifactId>
+                               <version>${tomcat.version}</version>
+                       </dependency>
+                       <dependency>
                                <groupId>org.codehaus.jettison</groupId>
                                <artifactId>jettison</artifactId>
                                <version>${jettison.version}</version>

Reply via email to