All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.core.instrument.binder.jpa.HibernateMetrics Maven / Gradle / Ivy

There is a newer version: 1.13.2
Show newest version
/*
 * Copyright 2017 VMware, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micrometer.core.instrument.binder.jpa;

import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.lang.NonNullApi;
import io.micrometer.core.lang.NonNullFields;
import io.micrometer.core.lang.Nullable;
import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;

import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;

/**
 * A {@link MeterBinder} implementation that provides Hibernate metrics. It exposes the
 * same statistics as would be exposed when calling {@link Statistics#logSummary()}.
 *
 * @author Marten Deinum
 * @author Jon Schneider
 * @author Johnny Lim
 * @implNote This implementation requires Hibernate 5.3 or later.
 * @deprecated This implementation is deprecated in favor of the MeterBinder maintained as
 * part of the Hibernate project as of version 5.4.26. See
 * https://mvnrepository.com/artifact/org.hibernate/hibernate-micrometer/
 */
@NonNullApi
@NonNullFields
@Deprecated
public class HibernateMetrics implements MeterBinder {

    private static final String SESSION_FACTORY_TAG_NAME = "entityManagerFactory";

    private final Iterable tags;

    @Nullable
    private final Statistics statistics;

    /**
     * Create {@code HibernateMetrics} and bind to the specified meter registry.
     * @param registry meter registry to use
     * @param sessionFactory session factory to use
     * @param sessionFactoryName session factory name as a tag value
     * @param tags additional tags
     */
    public static void monitor(MeterRegistry registry, SessionFactory sessionFactory, String sessionFactoryName,
            String... tags) {
        monitor(registry, sessionFactory, sessionFactoryName, Tags.of(tags));
    }

    /**
     * Create {@code HibernateMetrics} and bind to the specified meter registry.
     * @param registry meter registry to use
     * @param sessionFactory session factory to use
     * @param sessionFactoryName session factory name as a tag value
     * @param tags additional tags
     */
    public static void monitor(MeterRegistry registry, SessionFactory sessionFactory, String sessionFactoryName,
            Iterable tags) {
        new HibernateMetrics(sessionFactory, sessionFactoryName, tags).bindTo(registry);
    }

    /**
     * Create {@code HibernateMetrics} and bind to the specified meter registry.
     * @param registry meter registry to use
     * @param entityManagerFactory entity manager factory to use
     * @param entityManagerFactoryName entity manager factory name as a tag value
     * @param tags additional tags
     * @deprecated since 1.1.2 in favor of
     * {@link #monitor(MeterRegistry, SessionFactory, String, String...)}
     */
    @Deprecated
    public static void monitor(MeterRegistry registry, EntityManagerFactory entityManagerFactory,
            String entityManagerFactoryName, String... tags) {
        monitor(registry, entityManagerFactory, entityManagerFactoryName, Tags.of(tags));
    }

    /**
     * Create {@code HibernateMetrics} and bind to the specified meter registry.
     * @param registry meter registry to use
     * @param entityManagerFactory entity manager factory to use
     * @param entityManagerFactoryName entity manager factory name as a tag value
     * @param tags additional tags
     * @deprecated since 1.1.2 in favor of
     * {@link #monitor(MeterRegistry, SessionFactory, String, Iterable)}
     */
    @Deprecated
    public static void monitor(MeterRegistry registry, EntityManagerFactory entityManagerFactory,
            String entityManagerFactoryName, Iterable tags) {
        new HibernateMetrics(entityManagerFactory, entityManagerFactoryName, tags).bindTo(registry);
    }

    /**
     * Create a {@code HibernateMetrics}.
     * @param sessionFactory session factory to use
     * @param sessionFactoryName session factory name as a tag value
     * @param tags additional tags
     */
    public HibernateMetrics(SessionFactory sessionFactory, String sessionFactoryName, Iterable tags) {
        this.tags = Tags.concat(tags, SESSION_FACTORY_TAG_NAME, sessionFactoryName);
        Statistics statistics = sessionFactory.getStatistics();
        this.statistics = statistics.isStatisticsEnabled() ? statistics : null;
    }

    /**
     * Create a {@code HibernateMetrics}.
     * @param entityManagerFactory entity manager factory to use
     * @param entityManagerFactoryName entity manager factory name as a tag value
     * @param tags additional tags
     * @deprecated since 1.1.2 in favor of
     * {@link #HibernateMetrics(SessionFactory, String, Iterable)}
     */
    @Deprecated
    public HibernateMetrics(EntityManagerFactory entityManagerFactory, String entityManagerFactoryName,
            Iterable tags) {
        this.tags = Tags.concat(tags, SESSION_FACTORY_TAG_NAME, entityManagerFactoryName);
        SessionFactory sessionFactory = unwrap(entityManagerFactory);
        if (sessionFactory != null) {
            Statistics statistics = sessionFactory.getStatistics();
            this.statistics = statistics.isStatisticsEnabled() ? statistics : null;
        }
        else {
            this.statistics = null;
        }
    }

    private void counter(MeterRegistry registry, String name, String description, ToDoubleFunction f,
            String... extraTags) {
        if (this.statistics == null) {
            return;
        }

        FunctionCounter.builder(name, statistics, f).tags(tags).tags(extraTags).description(description)
                .register(registry);
    }

    @Override
    public void bindTo(MeterRegistry registry) {
        if (this.statistics == null) {
            return;
        }

        // Session statistics
        counter(registry, "hibernate.sessions.open", "Sessions opened", Statistics::getSessionOpenCount);
        counter(registry, "hibernate.sessions.closed", "Sessions closed", Statistics::getSessionCloseCount);

        // Transaction statistics
        counter(registry, "hibernate.transactions", "The number of transactions we know to have been successful",
                Statistics::getSuccessfulTransactionCount, "result", "success");
        counter(registry, "hibernate.transactions", "The number of transactions we know to have failed",
                s -> s.getTransactionCount() - s.getSuccessfulTransactionCount(), "result", "failure");
        counter(registry, "hibernate.optimistic.failures",
                "The number of StaleObjectStateExceptions that have occurred", Statistics::getOptimisticFailureCount);

        counter(registry, "hibernate.flushes",
                "The global number of flushes executed by sessions (either implicit or explicit)",
                Statistics::getFlushCount);
        counter(registry, "hibernate.connections.obtained",
                "Get the global number of connections asked by the sessions "
                        + "(the actual number of connections used may be much smaller depending "
                        + "whether you use a connection pool or not)",
                Statistics::getConnectCount);

        // Statements
        counter(registry, "hibernate.statements", "The number of prepared statements that were acquired",
                Statistics::getPrepareStatementCount, "status", "prepared");
        counter(registry, "hibernate.statements", "The number of prepared statements that were released",
                Statistics::getCloseStatementCount, "status", "closed");

        // Second Level Caching

        Arrays.stream(statistics.getSecondLevelCacheRegionNames()).filter(this::hasDomainDataRegionStatistics)
                .forEach(regionName -> {
                    counter(registry, "hibernate.second.level.cache.requests",
                            "The number of cacheable entities/collections successfully retrieved from the cache",
                            stats -> stats.getDomainDataRegionStatistics(regionName).getHitCount(), "region",
                            regionName, "result", "hit");
                    counter(registry, "hibernate.second.level.cache.requests",
                            "The number of cacheable entities/collections not found in the cache and loaded from the database",
                            stats -> stats.getDomainDataRegionStatistics(regionName).getMissCount(), "region",
                            regionName, "result", "miss");
                    counter(registry, "hibernate.second.level.cache.puts",
                            "The number of cacheable entities/collections put in the cache",
                            stats -> stats.getDomainDataRegionStatistics(regionName).getPutCount(), "region",
                            regionName);
                });

        // Entity information
        counter(registry, "hibernate.entities.deletes", "The number of entity deletes",
                Statistics::getEntityDeleteCount);
        counter(registry, "hibernate.entities.fetches", "The number of entity fetches",
                Statistics::getEntityFetchCount);
        counter(registry, "hibernate.entities.inserts", "The number of entity inserts",
                Statistics::getEntityInsertCount);
        counter(registry, "hibernate.entities.loads", "The number of entity loads", Statistics::getEntityLoadCount);
        counter(registry, "hibernate.entities.updates", "The number of entity updates",
                Statistics::getEntityUpdateCount);

        // Collections
        counter(registry, "hibernate.collections.deletes", "The number of collection deletes",
                Statistics::getCollectionRemoveCount);
        counter(registry, "hibernate.collections.fetches", "The number of collection fetches",
                Statistics::getCollectionFetchCount);
        counter(registry, "hibernate.collections.loads", "The number of collection loads",
                Statistics::getCollectionLoadCount);
        counter(registry, "hibernate.collections.recreates", "The number of collections recreated",
                Statistics::getCollectionRecreateCount);
        counter(registry, "hibernate.collections.updates", "The number of collection updates",
                Statistics::getCollectionUpdateCount);

        // Natural Id cache
        counter(registry, "hibernate.cache.natural.id.requests",
                "The number of cached naturalId lookups successfully retrieved from cache",
                Statistics::getNaturalIdCacheHitCount, "result", "hit");
        counter(registry, "hibernate.cache.natural.id.requests",
                "The number of cached naturalId lookups not found in cache", Statistics::getNaturalIdCacheMissCount,
                "result", "miss");
        counter(registry, "hibernate.cache.natural.id.puts", "The number of cacheable naturalId lookups put in cache",
                Statistics::getNaturalIdCachePutCount);

        counter(registry, "hibernate.query.natural.id.executions",
                "The number of naturalId queries executed against the database",
                Statistics::getNaturalIdQueryExecutionCount);

        TimeGauge
                .builder("hibernate.query.natural.id.executions.max", statistics, TimeUnit.MILLISECONDS,
                        Statistics::getNaturalIdQueryExecutionMaxTime)
                .description("The maximum query time for naturalId queries executed against the database").tags(tags)
                .register(registry);

        // Query statistics
        counter(registry, "hibernate.query.executions", "The number of executed queries",
                Statistics::getQueryExecutionCount);

        TimeGauge
                .builder("hibernate.query.executions.max", statistics, TimeUnit.MILLISECONDS,
                        Statistics::getQueryExecutionMaxTime)
                .description("The time of the slowest query").tags(tags).register(registry);

        // Update timestamp cache
        counter(registry, "hibernate.cache.update.timestamps.requests",
                "The number of timestamps successfully retrieved from cache",
                Statistics::getUpdateTimestampsCacheHitCount, "result", "hit");
        counter(registry, "hibernate.cache.update.timestamps.requests",
                "The number of tables for which no update timestamps was not found in cache",
                Statistics::getUpdateTimestampsCacheMissCount, "result", "miss");
        counter(registry, "hibernate.cache.update.timestamps.puts", "The number of timestamps put in cache",
                Statistics::getUpdateTimestampsCachePutCount);

        // Query Caching
        counter(registry, "hibernate.cache.query.requests",
                "The number of cached queries successfully retrieved from cache", Statistics::getQueryCacheHitCount,
                "result", "hit");
        counter(registry, "hibernate.cache.query.requests", "The number of cached queries not found in cache",
                Statistics::getQueryCacheMissCount, "result", "miss");
        counter(registry, "hibernate.cache.query.puts", "The number of cacheable queries put in cache",
                Statistics::getQueryCachePutCount);
        counter(registry, "hibernate.cache.query.plan",
                "The global number of query plans successfully retrieved from cache",
                Statistics::getQueryPlanCacheHitCount, "result", "hit");
        counter(registry, "hibernate.cache.query.plan", "The global number of query plans lookups not found in cache",
                Statistics::getQueryPlanCacheMissCount, "result", "miss");
    }

    private boolean hasDomainDataRegionStatistics(String regionName) {
        // In 5.3, getDomainDataRegionStatistics (a new method) will throw an
        // IllegalArgumentException
        // if the region can't be resolved.
        try {
            return statistics.getDomainDataRegionStatistics(regionName) != null;
        }
        catch (IllegalArgumentException e) {
            return false;
        }
    }

    /**
     * Unwrap the {@link SessionFactory} from {@link EntityManagerFactory}.
     * @param entityManagerFactory {@link EntityManagerFactory} to unwrap
     * @return unwrapped {@link SessionFactory}
     */
    @Nullable
    private SessionFactory unwrap(EntityManagerFactory entityManagerFactory) {
        try {
            return entityManagerFactory.unwrap(SessionFactory.class);
        }
        catch (PersistenceException ex) {
            return null;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy