33import io .prometheus .client .Collector ;
44import io .prometheus .client .CollectorRegistry ;
55import io .prometheus .client .CounterMetricFamily ;
6+ import io .prometheus .client .GaugeMetricFamily ;
67import java .util .ArrayList ;
78import java .util .Arrays ;
89import java .util .Collections ;
3233 * SessionFactory sessionFactory =
3334 * entityManagerFactory.unwrap(SessionFactory.class);
3435 * </pre>
36+ * <p>
37+ * When {@code enablePerQueryMetrics()} has been called, certain metrics like execution
38+ * time are collected per query. This may create a lot of monitoring data, so it should
39+ * be used with caution.
3540 *
3641 * @author Christian Kaltepoth
3742 */
3843public class HibernateStatisticsCollector extends Collector {
3944
4045 private static final List <String > LABEL_NAMES = Collections .singletonList ("unit" );
4146
47+ private static final List <String > LABEL_NAMES_PER_QUERY = Arrays .asList ("unit" , "query" );
48+
4249 private final Map <String , SessionFactory > sessionFactories = new ConcurrentHashMap <String , SessionFactory >();
4350
51+ private boolean perQueryMetricsEnabled ;
52+
4453 /**
4554 * Creates an empty collector. If you use this constructor, you have to add one or more
4655 * session factories to the collector by calling the {@link #add(SessionFactory, String)}
@@ -74,6 +83,19 @@ public HibernateStatisticsCollector add(SessionFactory sessionFactory, String na
7483 return this ;
7584 }
7685
86+ /**
87+ * Enables collection of per-query metrics. Produces a lot of monitoring data, so use with caution.
88+ * <p>
89+ * Per-query metrics have a label "query" with the actual HQL query as value. The query will contain
90+ * placeholders ("?") instead of the real parameter values (example: {@code select u from User u where id=?}).
91+ *
92+ * @return Returns the collector
93+ */
94+ public HibernateStatisticsCollector enablePerQueryMetrics () {
95+ this .perQueryMetricsEnabled = true ;
96+ return this ;
97+ }
98+
7799 @ Override
78100 public List <MetricFamilySamples > collect () {
79101 List <MetricFamilySamples > metrics = new ArrayList <MetricFamilySamples >();
@@ -82,6 +104,9 @@ public List<MetricFamilySamples> collect() {
82104 metrics .addAll (getCacheMetrics ());
83105 metrics .addAll (getEntityMetrics ());
84106 metrics .addAll (getQueryExecutionMetrics ());
107+ if (perQueryMetricsEnabled ) {
108+ metrics .addAll (getPerQueryMetrics ());
109+ }
85110 return metrics ;
86111 }
87112
@@ -448,25 +473,176 @@ public double getValue(Statistics statistics) {
448473 );
449474 }
450475
476+ private List <MetricFamilySamples > getPerQueryMetrics () {
477+ List <MetricFamilySamples > metrics = new ArrayList <MetricFamilySamples >();
478+
479+ metrics .addAll (Arrays .asList (
480+
481+ createCounterForQuery ("hibernate_per_query_cache_hit_total" ,
482+ "Global number of cache hits for query (getCacheHitCount)" ,
483+ new ValueProviderPerQuery () {
484+ @ Override
485+ public double getValue (Statistics statistics , String query ) {
486+ return statistics .getQueryStatistics (query )
487+ .getCacheHitCount ();
488+ }
489+ }
490+ ),
491+ createCounterForQuery ("hibernate_per_query_cache_miss_total" ,
492+ "Global number of cache misses for query (getCacheMissCount)" ,
493+ new ValueProviderPerQuery () {
494+ @ Override
495+ public double getValue (Statistics statistics , String query ) {
496+ return statistics .getQueryStatistics (query )
497+ .getCacheMissCount ();
498+ }
499+ }
500+ ),
501+ createCounterForQuery ("hibernate_per_query_cache_put_total" ,
502+ "Global number of cache puts for query (getCachePutCount)" ,
503+ new ValueProviderPerQuery () {
504+ @ Override
505+ public double getValue (Statistics statistics , String query ) {
506+ return statistics .getQueryStatistics (query )
507+ .getCachePutCount ();
508+ }
509+ }
510+ ),
511+ createCounterForQuery ("hibernate_per_query_execution_total" ,
512+ "Global number of executions for query (getExecutionCount)" ,
513+ new ValueProviderPerQuery () {
514+ @ Override
515+ public double getValue (Statistics statistics , String query ) {
516+ return statistics .getQueryStatistics (query )
517+ .getExecutionCount ();
518+ }
519+ }
520+ ),
521+ createCounterForQuery ("hibernate_per_query_execution_rows_total" ,
522+ "Global number of rows for all executions of query (getExecutionRowCount)" ,
523+ new ValueProviderPerQuery () {
524+ @ Override
525+ public double getValue (Statistics statistics , String query ) {
526+ return statistics .getQueryStatistics (query )
527+ .getExecutionRowCount ();
528+ }
529+ }
530+ ),
531+ createGaugeForQuery ("hibernate_per_query_execution_min_seconds" ,
532+ "Minimum execution time of query in seconds (based on getExecutionMinTime)" ,
533+ new ValueProviderPerQuery () {
534+ @ Override
535+ public double getValue (Statistics statistics , String query ) {
536+ return toSeconds (statistics .getQueryStatistics (query )
537+ .getExecutionMinTime ());
538+ }
539+ }
540+ ),
541+ createGaugeForQuery ("hibernate_per_query_execution_max_seconds" ,
542+ "Maximum execution time of query in seconds (based on getExecutionMaxTime)" ,
543+ new ValueProviderPerQuery () {
544+ @ Override
545+ public double getValue (Statistics statistics , String query ) {
546+ return toSeconds (statistics .getQueryStatistics (query )
547+ .getExecutionMaxTime ());
548+ }
549+ }
550+ ),
551+ createCounterForQuery ("hibernate_per_query_execution_seconds_total" ,
552+ "Accumulated execution time of query in seconds (based on getExecutionTotalTime)" ,
553+ new ValueProviderPerQuery () {
554+ @ Override
555+ public double getValue (Statistics statistics , String query ) {
556+ return toSeconds (statistics .getQueryStatistics (query )
557+ .getExecutionTotalTime ());
558+ }
559+ }
560+ )
561+ ));
562+
563+ return metrics ;
564+ }
565+
451566 private CounterMetricFamily createCounter (String metric , String help , ValueProvider provider ) {
452567
453568 CounterMetricFamily metricFamily = new CounterMetricFamily (metric , help , LABEL_NAMES );
454569
455570 for (Entry <String , SessionFactory > entry : sessionFactories .entrySet ()) {
456571 metricFamily .addMetric (
457- Collections .singletonList (entry .getKey ()),
458- provider .getValue (entry .getValue ().getStatistics ())
572+ Collections .singletonList (entry .getKey ()),
573+ provider .getValue (entry .getValue ().getStatistics ())
459574 );
460575 }
461576
462577 return metricFamily ;
463578
464579 }
465580
581+ private CounterMetricFamily createCounterForQuery (String metric , String help , ValueProviderPerQuery provider ) {
582+
583+ final CounterMetricFamily counters = new CounterMetricFamily (metric , help , LABEL_NAMES_PER_QUERY );
584+
585+ addMetricsForQuery (new PerQuerySamples () {
586+ @ Override
587+ public void addMetric (List <String > labelValues , double value ) {
588+ counters .addMetric (labelValues , value );
589+ }
590+ }, provider );
591+
592+ return counters ;
593+
594+ }
595+
596+ private GaugeMetricFamily createGaugeForQuery (String metric , String help , ValueProviderPerQuery provider ) {
597+
598+ final GaugeMetricFamily gauges = new GaugeMetricFamily (metric , help , LABEL_NAMES_PER_QUERY );
599+
600+ addMetricsForQuery (new PerQuerySamples () {
601+ @ Override
602+ public void addMetric (List <String > labelValues , double value ) {
603+ gauges .addMetric (labelValues , value );
604+ }
605+ }, provider );
606+
607+ return gauges ;
608+
609+ }
610+
611+ private void addMetricsForQuery (PerQuerySamples samples , ValueProviderPerQuery provider ) {
612+
613+ for (Entry <String , SessionFactory > entry : sessionFactories .entrySet ()) {
614+ SessionFactory sessionFactory = entry .getValue ();
615+ Statistics stats = sessionFactory .getStatistics ();
616+ String unitName = entry .getKey ();
617+
618+ for (String query : stats .getQueries ()) {
619+ samples .addMetric (Arrays .asList (unitName , query ), provider .getValue (stats , query ));
620+ }
621+ }
622+ }
623+
624+ private double toSeconds (long milliseconds ){
625+ return milliseconds / 1000d ;
626+ }
627+
628+ private interface PerQuerySamples {
629+
630+ void addMetric (List <String > labelValues , double value );
631+
632+ }
633+
634+
466635 private interface ValueProvider {
467636
468637 double getValue (Statistics statistics );
469638
470639 }
471640
641+ private interface ValueProviderPerQuery {
642+
643+ double getValue (Statistics statistics , String query );
644+
645+ }
646+
647+
472648}
0 commit comments