package io.prometheus.client.hibernate;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.CounterMetricFamily;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;
/**
* Collect metrics from one or more Hibernate SessionFactory instances.
* <p>
* Usage example for a single session factory:
* <pre>
* new HibernateStatisticsCollector(sessionFactory, "myapp").register();
* </pre>
* Usage example for multiple session factories:
* <pre>
* new HibernateStatisticsCollector()
* .add(sessionFactory1, "myapp1")
* .add(sessionFactory2, "myapp2")
* .register();
* </pre>
* If you are in a JPA environment, you can obtain the SessionFactory like this:
* <pre>
* SessionFactory sessionFactory =
* entityManagerFactory.unwrap(SessionFactory.class);
* </pre>
*
* @author Christian Kaltepoth
*/
public class HibernateStatisticsCollector extends Collector {
private static final List<String> LABEL_NAMES = Collections.singletonList("unit");
private final Map<String, SessionFactory> sessionFactories = new ConcurrentHashMap<String, SessionFactory>();
/**
* Creates an empty collector. If you use this constructor, you have to add one or more
* session factories to the collector by calling the {@link #add(SessionFactory, String)}
* method.
*/
public HibernateStatisticsCollector() {
// nothing
}
/**
* Creates a new collector for the given session factory. Calling this constructor
* has the same effect as creating an empty collector and adding the session factory
* using {@link #add(SessionFactory, String)}.
*
* @param sessionFactory The Hibernate SessionFactory to collect metrics for
* @param name A unique name for this SessionFactory
*/
public HibernateStatisticsCollector(SessionFactory sessionFactory, String name) {
add(sessionFactory, name);
}
/**
* Registers a Hibernate SessionFactory with this collector.
*
* @param sessionFactory The Hibernate SessionFactory to collect metrics for
* @param name A unique name for this SessionFactory
* @return Returns the collector
*/
public HibernateStatisticsCollector add(SessionFactory sessionFactory, String name) {
sessionFactories.put(name, sessionFactory);
return this;
}
@Override
public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> metrics = new ArrayList<MetricFamilySamples>();
metrics.addAll(getSessionMetrics());
metrics.addAll(getConnectionMetrics());
metrics.addAll(getCacheMetrics());
metrics.addAll(getEntityMetrics());
metrics.addAll(getQueryExecutionMetrics());
return metrics;
}
@Override
public <T extends Collector> T register(CollectorRegistry registry) {
if (sessionFactories.isEmpty()) {
throw new IllegalStateException("You must register at least one SessionFactory.");
}
return super.register(registry);
}
private List<MetricFamilySamples> getSessionMetrics() {
return Arrays.<MetricFamilySamples>asList(
createCounter(
"hibernate_session_opened_total",
"Global number of sessions opened (getSessionOpenCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSessionOpenCount();
}
}
),
createCounter(
"hibernate_session_closed_total",
"Global number of sessions closed (getSessionCloseCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSessionCloseCount();
}
}
),
createCounter(
"hibernate_flushed_total",
"The global number of flushes executed by sessions (getFlushCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getFlushCount();
}
}
),
createCounter(
"hibernate_connect_total",
"The global number of connections requested by the sessions (getConnectCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getConnectCount();
}
}
),
createCounter(
"hibernate_optimistic_failure_total",
"The number of StaleObjectStateExceptions that occurred (getOptimisticFailureCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getOptimisticFailureCount();
}
}
)
);
}
private List<MetricFamilySamples> getConnectionMetrics() {
return Arrays.<MetricFamilySamples>asList(
createCounter(
"hibernate_statement_prepared_total",
"The number of prepared statements that were acquired (getPrepareStatementCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getPrepareStatementCount();
}
}
),
createCounter(
"hibernate_statement_closed_total",
"The number of prepared statements that were released (getCloseStatementCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCloseStatementCount();
}
}
),
createCounter(
"hibernate_transaction_total",
"The number of transactions we know to have completed (getTransactionCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getTransactionCount();
}
}
),
createCounter(
"hibernate_transaction_success_total",
"The number of transactions we know to have been successful (getSuccessfulTransactionCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSuccessfulTransactionCount();
}
}
)
);
}
private List<MetricFamilySamples> getCacheMetrics() {
return Arrays.<MetricFamilySamples>asList(
createCounter(
"hibernate_second_level_cache_hit_total",
"Global number of cacheable entities/collections successfully retrieved from the cache (getSecondLevelCacheHitCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSecondLevelCacheHitCount();
}
}
),
createCounter(
"hibernate_second_level_cache_miss_total",
"Global number of cacheable entities/collections not found in the cache and loaded from the database (getSecondLevelCacheMissCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSecondLevelCacheMissCount();
}
}
),
createCounter(
"hibernate_second_level_cache_put_total",
"Global number of cacheable entities/collections put in the cache (getSecondLevelCachePutCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getSecondLevelCachePutCount();
}
}
),
createCounter(
"hibernate_query_cache_hit_total",
"The global number of cached queries successfully retrieved from cache (getQueryCacheHitCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getQueryCacheHitCount();
}
}
),
createCounter(
"hibernate_query_cache_miss_total",
"The global number of cached queries not found in cache (getQueryCacheMissCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getQueryCacheMissCount();
}
}
),
createCounter(
"hibernate_query_cache_put_total",
"The global number of cacheable queries put in cache (getQueryCachePutCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getQueryCachePutCount();
}
}
),
createCounter(
"hibernate_natural_id_cache_hit_total",
"The global number of cached naturalId lookups successfully retrieved from cache (getNaturalIdCacheHitCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getNaturalIdCacheHitCount();
}
}
),
createCounter(
"hibernate_natural_id_cache_miss_total",
"The global number of cached naturalId lookups not found in cache (getNaturalIdCacheMissCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getNaturalIdCacheMissCount();
}
}
),
createCounter(
"hibernate_natural_id_cache_put_total",
"The global number of cacheable naturalId lookups put in cache (getNaturalIdCachePutCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getNaturalIdCachePutCount();
}
}
),
createCounter(
"hibernate_update_timestamps_cache_hit_total",
"The global number of timestamps successfully retrieved from cache (getUpdateTimestampsCacheHitCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getUpdateTimestampsCacheHitCount();
}
}
),
createCounter(
"hibernate_update_timestamps_cache_miss_total",
"The global number of tables for which no update timestamps was not found in cache (getUpdateTimestampsCacheMissCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getUpdateTimestampsCacheMissCount();
}
}
),
createCounter(
"hibernate_update_timestamps_cache_put_total",
"The global number of timestamps put in cache (getUpdateTimestampsCachePutCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getUpdateTimestampsCachePutCount();
}
}
)
);
}
private List<MetricFamilySamples> getEntityMetrics() {
return Arrays.<MetricFamilySamples>asList(
createCounter(
"hibernate_entity_delete_total",
"Global number of entity deletes (getEntityDeleteCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getEntityDeleteCount();
}
}
),
createCounter(
"hibernate_entity_insert_total",
"Global number of entity inserts (getEntityInsertCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getEntityInsertCount();
}
}
),
createCounter(
"hibernate_entity_load_total",
"Global number of entity loads (getEntityLoadCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getEntityLoadCount();
}
}
),
createCounter(
"hibernate_entity_fetch_total",
"Global number of entity fetches (getEntityFetchCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getEntityFetchCount();
}
}
),
createCounter(
"hibernate_entity_update_total",
"Global number of entity updates (getEntityUpdateCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getEntityUpdateCount();
}
}
),
createCounter(
"hibernate_collection_load_total",
"Global number of collections loaded (getCollectionLoadCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCollectionLoadCount();
}
}
),
createCounter(
"hibernate_collection_fetch_total",
"Global number of collections fetched (getCollectionFetchCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCollectionFetchCount();
}
}
),
createCounter(
"hibernate_collection_update_total",
"Global number of collections updated (getCollectionUpdateCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCollectionUpdateCount();
}
}
),
createCounter(
"hibernate_collection_remove_total",
"Global number of collections removed (getCollectionRemoveCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCollectionRemoveCount();
}
}
),
createCounter(
"hibernate_collection_recreate_total",
"Global number of collections recreated (getCollectionRecreateCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getCollectionRecreateCount();
}
}
)
);
}
private List<MetricFamilySamples> getQueryExecutionMetrics() {
return Arrays.<MetricFamilySamples>asList(
createCounter(
"hibernate_query_execution_total",
"Global number of executed queries (getQueryExecutionCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getQueryExecutionCount();
}
}
),
createCounter(
"hibernate_natural_id_query_execution_total",
"The global number of naturalId queries executed against the database (getNaturalIdQueryExecutionCount)",
new ValueProvider() {
@Override
public double getValue(Statistics statistics) {
return statistics.getNaturalIdQueryExecutionCount();
}
}
)
);
}
private CounterMetricFamily createCounter(String metric, String help, ValueProvider provider) {
CounterMetricFamily metricFamily = new CounterMetricFamily(metric, help, LABEL_NAMES);
for (Entry<String, SessionFactory> entry : sessionFactories.entrySet()) {
metricFamily.addMetric(
Collections.singletonList(entry.getKey()),
provider.getValue(entry.getValue().getStatistics())
);
}
return metricFamily;
}
private interface ValueProvider {
double getValue(Statistics statistics);
}
}