JAM-Application-Metrics (Everything is a potential metric)

Version 68

    JAM-APPLICATION-METRICS

     

     

    JAM-Application-Metrics is a project, which intends to facilitate the creation, record and monitoring of metrics for any deployment. Using the power on interceptors and reflection the only thing that the developer has to do is to annotate the class or the method where lies the field-metric he wants to record by using the annotation @Metric(fieldName = {"field name of first metric","field name of second metric", etc}, groupName="some name characteristic to the metric group"). This way the field-metric will be automatically recorded in the metrics-cache and also monitored by RHQ (http://rhq-project.github.io/rhq/) and Hawkular. It is the initial project JBoss-Automated-Metrics which is renamed.

     

     

     

    JAM-Application-Metric FEATURES :

     

    1. Cache-Storage of Metrics.

    2. Database-Storage of Metrics.

    3. Monitoring of Metrics using RHQ.

    4. Monitoring of Metrics using Hawkular and Grafana.

    5. Creation of 2D and 3D Metric Plots.

     

     

     

    JAM-Application-Metrics ARCHITECTURE

     

    JAM-architectural-image.png

     

     

    How To Use

    Just annotate the method of your ejb, or your injectable class in the general case, with the annotation @Metric(fieldName = {"field name of first metric","field name of second metric", etc}, groupName="some name characteristic to the metric group"). This is sufficient for the field-metrics to be recorded in the metrics-cache. An example of usage can be found under the folder ApplicationMetricsApiTest (https://github.com/panossot/jam-metrics   ). Deploy the generated .war file on the Wildfly server to see the metrics being printed.

     

    For example, if we annotate the countMethod method below, with the annotation @Metric(fieldName = {"count","count2"}, groupName="myTestGroup") :

     

    @Stateful
    @LocalBean
    public class MetricsApiSessionBean {
    
        private int count = 0;
    
        private int count2 = 0;
    
        public MetricsApiSessionBean() {
        }
    
        @Metric(fieldName = {"count","count2"}, grouptName = "myTestGroup")  // The annotation parameter groupName is used for grouping of metrics under the same metric-properties configuration.
        public int countMethod() {
            count++;
            count2 += 2;
    
            return count;
        }
    
    }
                                      

     

    and we call the method countMethod two sequential times :

     

    public class PrintMetrics extends HttpServlet {
        @EJB
        private MetricsApiSessionBean metricsApiSessionBean;
    
        private String grouptName = "myTestGroup";  // The annotation parameter groupName is used for grouping of metrics under the same metric-properties configuration.
    
        protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html;charset=UTF-8");
            initializeMetricProperties();  // Using JBoss MetricsPropertiesApi to configure JBoss-Automated-Metrics.  All the configuration needed is done inside the deployment (no xml configurations needed, no System Properties to be set)
    
            try (PrintWriter out = response.getWriter()) {
                out.println("<!DOCTYPE html>");
                out.println("<html>");
                out.println("<head>");
                out.println("<title>Servlet PrintMetrics</title>");
                out.println("</head>");
                out.println("<body>");
                out.println("<h1>Servlet PrintMetrics : </h1>");
                metricsApiSessionBean.countMethod();
                metricsApiSessionBean.countMethod();
                out.println(MetricsCacheApi.printMetricsCache(groupName));
                out.println("<br>Successful Run ...</br>");
                out.println("</body>");
                out.println("</html>");
            }
        }
    
        private void initializeMetricProperties() {
            HashMap<String,String> rhqScheduleIds = new HashMap<String,String>();
            rhqScheduleIds.put("count", "11401");   // 1st Metric-Field name - Equivalent Schedule Id (Retrieved from the RHQ resources - http://RHQserverUrl:7080/rest/resource?ps=99999)
            rhqScheduleIds.put("count2", "11402");  // 2nd  Metric-Field name - Equivalent Schedule Id (Retrieved from the RHQ resources - http://RHQserverUrl:7080/rest/resource?ps=99999)
            MetricProperties metricProperties = new MetricProperties();
            metricProperties.setRhqMonitoring("true");  // Enabling the RHQ Monitoring operation.
            metricProperties.setCacheStore("true");  // Enabling the Cache Storage operation.
            metricProperties.setRhqServerUrl("lz-panos-jon33.bc.jonqe.lab.eng.bos.redhat.com");  // Adding the RHQ server URL.
            metricProperties.setRhqScheduleIds(rhqScheduleIds);  // Adding the RHQ Schedule Ids of the metrics we want to monitor.
            MetricsPropertiesApi.storeProperties(groupName, metricProperties);
        }
                                                .
                                                .
    
    }
    
    
    
    
    
    
                                      

     

    then the field-metrics will be automatically stored in our metrics cache :

     

    Metric Field Name : count (of instance org.jboss.metrics.MetricsApiSessionBean@43f98edf)

     

    Value : 1

     

    Value : 2

     

    Metric Field Name : count2 (of instance org.jboss.metrics.MetricsApiSessionBean@43f98edf)

     

    Value : 2

     

    Value : 4

     

     

     

    As mentioned above there is also the possibility of monitoring the metrics using RHQ. To do this please perform the following steps :

     

    • Firstly, download and configure the RHQ Server
    • Change the rhq-plugin.xml located in MetricsRhqPlugin folder with the metrics that you want to monitor
    • Build the plugin and load it on RHQ
    • At this point, you should be able to see your MetricsServer under the Resources of RHQ (e.g. http://localhost:7080/rest/resource?ps=99999)
    • Check the schedule ids of the metrics you want to monitor and configure Jam-application-metrics using JBossApplicationMeticsProperties api (Please, check initializeMetricProperties()  of the example above).
    • Enable the RHQ Monitoring Operation using JBossApplicationMeticsProperties api  (Please, check the method initializeMetricProperties()  of the example above).
    • At this point, you should be able to use the operations of the JAM-Application-Metrics that you have activated.

     

    and you will see your metrics being monitored :

     

     

    rhqCountMetric.png

    count metric

     

     

    rhqCount2Metric.png

    count2 metric

     

     

     

     

    Monitoring JAM Metrics with HAWKULAR and GRAFANA

     

    If any help is needed using grafana with hawkular, please refer to the Hawkular-Grafana QuickStart Guide : Hawkular - Grafana Quickstart Guide  .

     

            In order to enable hawkular-grafana monitoring, first, we should enable/parametrize the Hawkular Monitoring and the Cache Storage :    

          MetricProperties metricProperties = new MetricProperties(); 
          metricProperties.setHawkularMonitoring("true"); 
          metricProperties.setHawkularMonitoringRefreshRate(100); 
          metricProperties.setHawkularTenant("hawkular"); 
          metricProperties.setCacheStore("true"); 
          metricProperties.setCacheMaxSize(10000); 
          MetricsPropertiesApi.storeProperties("hawkularGroup", metricProperties);

     

            Annotate the the method that processes the metric you would like to monitor with the @Metric annotation :

     

          
          @Metric(fieldName = {"count"}, groupName = "hawkularGroup") 
          public synchronized void getAndSetCountIncreased(Callable<Object> func) throws Exception { 
               func.call(); 
          }

     

            Then, deploy your application on a server that uses Hawkular Services, execute your code and you will see your metrics being monitored :

     

    JamMonitoring.png

     

     

    Application Metric Storage in Databases using JAM-Application-Metrics

     

    JAM-Application-Metrics can be used to store the metrics in some Database, just by annotating the method of your ejb, or your injectable class in the general case, with the annotation    @DBStore(groupName = "some name characteristic to the metric group", queryUpdateDB = {"the name of the query stored in the properties of the group","field name of the metric to be stored in the database"}, statementName = "the name of the statement that is stored in the group properties"). It is used in combination with the @Metric annotation. This is sufficient for the field-metrics to be stored in the database. An example of usage can be found under the folder ApplicationMetricsApiTest6. Start your DB Server and deploy the generated .war file on the Wildfly server to see the metrics being stored in the database.

     

    For example, if we annotate the countMethod method below, with the annotations @Metric(fieldName = {"count"}, groupName="myTestGroup") and @DBStore(groupName = "myTestGroup", queryUpdateDB = {"StoreDBMetric","count"}, statementName = "statement_1"):

     

    @Stateful
    @LocalBean
    public class MetricsApiSessionBean {
    
        private int count = 0;
    
        public MetricsApiSessionBean() {
        }
    
        @Metric(fieldName = {"count"}, groupName = "myTestGroup")
        @DBStore(groupName = "myTestGroup", queryUpdateDB = {"StoreDBMetric","count"}, statementName = "statement_1")  // Annotation that enables the storage of JBoss-Automated-Metrics in the database
        public int countMethod() {
            count++;
    
            return count;
        }
    
    }
                               

     

     

        and we call the method countMethod one time :

     

    public class PrintMetrics extends HttpServlet {
    
            @EJB
            private MetricsApiSessionBean metricsApiSessionBean;
    
            private String groupName = "myTestGroup";
    
            protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException {
                response.setContentType("text/html;charset=UTF-8");
                initializeMetricProperties();
    
                try (PrintWriter out = response.getWriter()) {
                    out.println("<!DOCTYPE html>");
                    out.println("<html>");
                    out.println("<head>");
                    out.println("<title>Servlet PrintMetrics</title>");
                    out.println("</head>");
                    out.println("<body>");
                    out.println("<h1>Servlet PrintMetrics : </h1>");
                    metricsApiSessionBean.countMethod();
                    out.println(MetricsCacheApi.printMetricsCache(groupName));
                    out.println("<br>Successful Run ...</br>");
                    out.println("</body>");
                    out.println("</html>");
                }
           }
    
           private void initializeMetricProperties() {
                HashMap<String,String> rhqScheduleIds = new HashMap<String,String>();
                rhqScheduleIds.put("count", "11401");
                rhqScheduleIds.put("count2", "11402");
                MetricProperties metricProperties = new MetricProperties();
                metricProperties.setRhqMonitoring("false");
                metricProperties.setCacheStore("true");
                metricProperties.setRhqServerUrl("lz-panos-jon33.bc.jonqe.lab.eng.bos.redhat.com");
                metricProperties.setRhqScheduleIds(rhqScheduleIds);
                metricProperties.setDatabaseStore("true");  //Enable the database storage of metrics
                try {
                    Connection  connection = DriverManager.getConnection("jdbc:mariadb://localhost:3306", "root", "panos");  //Create the DriverManager connection
                    Statement stmt = connection.createStatement();
                    createDbTable(stmt);   // Create the DB tables
                    HashMap<String,Statement> dbStmt = new HashMap<String,Statement>();
                    dbStmt.put("statement_1", stmt);
                    metricProperties.setDatabaseStatement(dbStmt);   // Store the statement in the group properties
                    HashMap<String,String> query1 = new HashMap<String,String>();
                    query1.put("StoreDBMetric", "INSERT INTO MyMETRICS.metricValues(METRIC_NAME,METRIC_VALUE,METRIC_INSTANCE,RECORD_TIME) VALUES('{1}', [1], '{instance}', '{time}');");   //Store the sql query in the group properties
                                                               // sql query semantics :  {1} = Name of metric 1, [1] = Value of metric 1, {instance} = the instance of the class that contains the metric, {time} = the time of storage
                    metricProperties.setUpdateDbQueries(query1);
                } catch(Exception e) {
                    e.printStackTrace();
                }
                MetricsPropertiesApi.storeProperties(groupName, metricProperties);   //Store the group properties
            }
    
            private void createDbTable(Statement stmt) {
                try {
                    String query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'MyMETRICS' AND table_name = 'metricValues'";
                    ResultSet rs = stmt.executeQuery(query);
                    rs.next();
                    boolean exists = rs.getInt("COUNT(*)") > 0;
    
                    if (!exists) {
                        String sql = "CREATE DATABASE MyMETRICS";   //Create the database
                        stmt.executeUpdate(sql);
                        System.out.println("Database created successfully...");
    
                        sql = "CREATE TABLE MyMETRICS.metricValues(ID int NOT NULL AUTO_INCREMENT, METRIC_NAME varchar(255) NOT NULL," +          //Create the tables
                              " METRIC_VALUE varchar(255) NOT NULL, METRIC_INSTANCE varchar(255), RECORD_TIME DATETIME, PRIMARY KEY(ID));";
    
                        stmt.executeUpdate(sql);
                    }
                } catch(Exception e){
                    e.printStackTrace();
                }
            }
                              
                                .
                                .
                                .
    
        }
    
    
    
    
    
    
    
                          

     

    then the field-metric will be automatically stored in our metrics cache :

     

    Metric Field Name : count (of instance org.jboss.metrics.MetricsApiSessionBean@43f98edf)

     

    Value : 1

     

     

    and in the database table MyMETRICS.metricValues :

     

     

    ID     METRIC_NAME     METRIC_VALUE                             METRIC_INSTANCE                                           RECORD_TIME

    1              count                      1                     org.jboss.metrics.MetricsApiSessionBean@43f98edf               2015-07-30 16:01:35

     

     

     

     

     

    JAM-Application-Metrics are now available as a Wildfly Subsystem Extension

      

     

       How to install the JAM-Application-Metrics Wildfly extension :

      1. Go to the directory WildflyAddExtensionSubsystem/ApplicationMetricsSubsystemExtension (panossot/jam-metrics · GitHub) .
      2. Execute the maven command : mvn install -DJBossHome=The home directory of your Wildfly installation.
      3. Restart Wildfly server.

     

     

     

     

    Record and Monitoring examples of Metrics using JAM-Application-Metrics

     

    JAM-Application-Metrics can be used to record and monitor standard metrics, as JVM Memory Metrics and Free Disk Space Metrics. Such examples can be found in the folder StandardMetricsReadyForUsage (jam-metrics/StandardMetricsReadyForUsage at master · panossot/jam-metrics · GitHub).

     

     

     

     

    JAM-Application-Metrics are now available using Java SE

     

    JAM-Application-Metrics can be used to store the metrics in some Cache or Database and monitor them with Rhq, using Java SE. For this purpose, org.jboss.metrics.javase.applicationmetricsjavaseapi.JbossAupplicationJavaSeMetrics and org.jboss.metrics.javase.applicationmetricsjavaseapi.JbossApplicationJavaSeMetricsDbStore are used. An example of usage can be found under the folder ApplicationMetricsJavaSeApiTest2 (https://github.com/panossot/jboss-automated-metrics/tree/master/JavaSeApplicationMetrics/ApplicationMetricsJavaSeApiTest2).

     

     

    For example, in class MetricsApiSeTestClass below,  we use org.jboss.metrics.javase.applicationmetricsjavaseapi.JbossApplicationJavaSeMetrics for Cache Storage and Rhq Monitoring and org.jboss.metrics.javase.applicationmetricsjavaseapi.JbossApplicationJavaSeMetricsDbStore for Database Storage of metrics in Java SE:

     

    public class MetricsApiSeTestClass {
    
        private int count = 0;
    
        private int count2 = 0;
    
        JbossAutomatedJavaSeMetrics jbMetrics;  // JBoss-Automated-Metrics Api class used for Cache Storage and Rhq Monitoring
        JbossAutomatedJavaSeMetricsDbStore jbMetricsDbStore;  // JBoss-Automated-Metrics Api class used for Database Storage
    
        public MetricsApiSeTestClass() {
            jbMetrics = new JbossAutomatedJavaSeMetrics();
            jbMetricsDbStore = new JbossAutomatedJavaSeMetricsDbStore();
        }
    
        public int countMethod() throws Exception {
            count++;
            count2 += 2;
            jbMetrics.metric(this,count,"count","myTestGroup");  // Used for Cache Storage and Rhq Monitoring of the metric "count"
                                   // params : instance, metric value, metric name, group
            jbMetricsDbStore.metricsDbStore(this, new Object[]{count}, "myTestGroup", "statement_1", new String[]{"StoreDBMetric","count"});   // Used for Database Storage of the metric "count"
                                                             // params : instance, array of values used to replace sql query semantics [i], group, statement, sql query to be executed and strings to replace semantics {i}.
            jbMetrics.metric(this,count2,"count2","myTestGroup");   // Used for Cache Storage and Rhq Monitoring of the metric "count2"
            jbMetricsDbStore.metricsDbStore(this, new Object[]{count2}, "myTestGroup", "statement_1", new String[]{"StoreDBMetric","count2"});  // Used for Database Storage of the metric "count2"
    
            return count;
        }
    
    }
                    

     

     

     

    and we call the method countMethod two sequential times :

     

    public class AutomatedMetricsJavaSeApiTest {
    
        private static String groupName = "myTestGroup";
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            try {
                initializeMetricProperties();
                MetricsApiSeTestClass mTC = new MetricsApiSeTestClass();
                mTC.countMethod();
                mTC.countMethod();
                System.out.println(MetricsCacheApi.printMetricsCache(groupName));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static void initializeMetricProperties() {
            HashMap<String,String> rhqScheduleIds = new HashMap<String,String>();
            rhqScheduleIds.put("count", "11401");
            rhqScheduleIds.put("count2", "11402");
            MetricProperties metricProperties = new MetricProperties();
            metricProperties.setRhqMonitoring("true");  // Enable Rhq monitoring
            metricProperties.setCacheStore("true");  // Enable Cache Storage
            metricProperties.setRhqServerUrl("lz-panos-jon33.bc.jonqe.lab.eng.bos.redhat.com");
            metricProperties.setRhqScheduleIds(rhqScheduleIds);
            metricProperties.setDatabaseStore("true");  // Enable Database Storage
            try {
                Connection  connection = DriverManager.getConnection("jdbc:mariadb://localhost:3306", "root", "panos");
                Statement stmt = connection.createStatement();
                createDbTable(stmt);
                HashMap<String,Statement> dbStmt = new HashMap<String,Statement>();
                dbStmt.put("statement_1", stmt);
                metricProperties.setDatabaseStatement(dbStmt);
                HashMap<String,String> query1 = new HashMap<String,String>();
                query1.put("StoreDBMetric", "INSERT INTO MyMETRICS.metricValues(METRIC_NAME,METRIC_VALUE,METRIC_INSTANCE,RECORD_TIME) VALUES('{1}', [1], '{instance}', '{time}');");
                                 // sql query semantics :  {1} = Name of metric 1, [1] = Value of metric 1, {instance} = the instance of the class that contains the metric, {time} = the time of storage
                metricProperties.setUpdateDbQueries(query1);
            } catch(Exception e) {
                e.printStackTrace();
            }
            MetricsPropertiesApi.storeProperties(groupName, metricProperties);
        }
    
        private static void createDbTable(Statement stmt) {
            try {
                String query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'MyMETRICS' AND table_name = 'metricValues'";
                ResultSet rs = stmt.executeQuery(query);
                rs.next();
                boolean exists = rs.getInt("COUNT(*)") > 0;
    
                if (!exists) {
                    String sql = "CREATE DATABASE MyMETRICS";
                    stmt.executeUpdate(sql);
                    System.out.println("Database created successfully...");
    
                    sql = "CREATE TABLE MyMETRICS.metricValues(ID int NOT NULL AUTO_INCREMENT, METRIC_NAME varchar(255) NOT NULL," +
                          " METRIC_VALUE varchar(255) NOT NULL, METRIC_INSTANCE varchar(255), RECORD_TIME DATETIME, PRIMARY KEY(ID));";
    
                    stmt.executeUpdate(sql);
                }
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    }
                       

     

     

    then the field-metrics will be automatically stored in our metrics cache :

     

    Metric Field Name : count (of instance org.jboss.metrics.applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf)

     

    Value : 1

     

    Value : 2

     

    Metric Field Name : count2 (of instance org.jboss.metrics.applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf)

     

    Value : 2

     

    Value : 4

     

     

    and in the database table MyMETRICS.metricValues :

     

     

    ID     METRIC_NAME     METRIC_VALUE                                              METRIC_INSTANCE                                                                                         RECORD_TIME

    1              count                      1                     org.jboss.metrics..applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf               2015-07-30 16:08:15

    2              count2                   2                     org.jboss.metrics..applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf               2015-07-30 16:08:15

    3              count                     2                     org.jboss.metrics..applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf               2015-07-30 16:08:15

    4              count2                   4                     org.jboss.metrics..applicationmetricsjavaseapitest.MetricsApiSeTestClass@47f92qcf               2015-07-30 16:08:15

     

     

    The metrics are also monitored using RHQ.

     

     

     

     

    JAM-Application-Metrics plots

     

    JAM-Application-Metrics provide the choice of plotting the metrics using line, bar or scatter 2-D plots and 3-D plots. Please, check the relative example on github (ApplicationMetricsApiTest7 , ApplicationMetricsApiTest17 , ApplicationMetricsApiTest18 , ApplicationMetricsApiTest19 , ApplicationMetricsApiTest20) to see how JAM-Application-Metrics can be plotted.

     

     

    2D and 3D Metric Plots
    bar2D.jpgbar2Dcompare.jpgbox2D.jpg
    box3D.jpgbox3Dcompare.jpggrid3D.jpg
    grid3Dcompare.jpghistogram2D.jpghistogram3D.jpg
    histogram3Dcompare.jpgline2D.jpgline2Dcompare.jpg
    line3D.jpgline3Dcompare.jpgscatter2D.jpg
    scatter2Dcompare.jpgscatter3D.jpgscatter3Dcompare.jpg
    stair2D.jpgstair2Dcompare.jpg

     

     

     

    Line plot

     

     

     

     

    The code can be found under panossot/jam-metrics · GitHub .

     

     

    FUTURE EXTENSIONS

      • Extend JAM-Application-Metrics to be monitored with Hawkular
      • Work as a subsystem of Wildfly (Wildfly could contribute in Sciences - simulations, etc)     --Implemented--
      • Extend JAM-Application-Metrics to send and store the metrics in any database      --Implemented--
      • Extend JAM-Application-Metrics to be displayed in the web console
      • Create standard metrics, ready for use
      • Open Source Analytics

     

    LATEST NEWS

      • JBoss-Automated-Metrics Wildfly Standalone version 1.0.0.Final is released!!! Search for org.jboss.metrics in The Central Maven Repo.
      • JBoss-Automated-Metrics Java SE version 1.0.0.Final is released!!!
      • JBoss-Automated-Metrics Wildfly Standalone version 1.0.1.Final is released!!! (Minor synchronization fixes)
      • JBoss-Automated-Metrics Java SE version 1.0.1.Final is released!!! (Minor synchronization fixes)
      • JBoss-Automated-Metrics Wildfly Standalone version 1.0.2.Final is released!!! (More Stable, More asynchronous, Parametrized cache storage size, Send array of data to RHQ, etc)
      • JBoss-Automated-Metrics Java SE version 1.0.2.Final is released!!! (More Stable, More asynchronous, Parametrized cache storage size, Send array of data to RHQ, etc)
      • JBoss-Automated-Metrics Wildfly Standalone version 1.0.3.Final is released!!! (Synchronous and asynchronous @DBStore functions, independent @Metric and @DBStore functions, addition of capability to store more parameters (other than the metrics) using @DBStore, addition of capability to execute a batch of queries using @DBStore, addition of code-enablement capability, development of integration tests which proove the functionalities, renaming of the artifacts , etc)
      • JBoss-Automated-Metrics Java SE version 1.0.3.Final is released!!!(Synchronous and asynchronous DBStore functions, independent Metric and DBStore functions, addition of capability to store more parameters (other than the metrics) using DBStore, addition of capability to execute a batch of queries using DBStore, addition of code-enablement capability, development of integration tests which proove the functionalities, renaming of the artifacts , etc)