001package io.prometheus.client.hibernate; 002 003import io.prometheus.client.Collector; 004import io.prometheus.client.CollectorRegistry; 005import io.prometheus.client.CounterMetricFamily; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Collections; 009import java.util.List; 010import java.util.Map; 011import java.util.Map.Entry; 012import java.util.concurrent.ConcurrentHashMap; 013import org.hibernate.SessionFactory; 014import org.hibernate.stat.Statistics; 015 016/** 017 * Collect metrics from one or more Hibernate SessionFactory instances. 018 * <p> 019 * Usage example for a single session factory: 020 * <pre> 021 * new HibernateStatisticsCollector(sessionFactory, "myapp").register(); 022 * </pre> 023 * Usage example for multiple session factories: 024 * <pre> 025 * new HibernateStatisticsCollector() 026 * .add(sessionFactory1, "myapp1") 027 * .add(sessionFactory2, "myapp2") 028 * .register(); 029 * </pre> 030 * If you are in a JPA environment, you can obtain the SessionFactory like this: 031 * <pre> 032 * SessionFactory sessionFactory = 033 * entityManagerFactory.unwrap(SessionFactory.class); 034 * </pre> 035 * 036 * @author Christian Kaltepoth 037 */ 038public class HibernateStatisticsCollector extends Collector { 039 040 private static final List<String> LABEL_NAMES = Collections.singletonList("unit"); 041 042 private final Map<String, SessionFactory> sessionFactories = new ConcurrentHashMap<String, SessionFactory>(); 043 044 /** 045 * Creates an empty collector. If you use this constructor, you have to add one or more 046 * session factories to the collector by calling the {@link #add(SessionFactory, String)} 047 * method. 048 */ 049 public HibernateStatisticsCollector() { 050 // nothing 051 } 052 053 /** 054 * Creates a new collector for the given session factory. Calling this constructor 055 * has the same effect as creating an empty collector and adding the session factory 056 * using {@link #add(SessionFactory, String)}. 057 * 058 * @param sessionFactory The Hibernate SessionFactory to collect metrics for 059 * @param name A unique name for this SessionFactory 060 */ 061 public HibernateStatisticsCollector(SessionFactory sessionFactory, String name) { 062 add(sessionFactory, name); 063 } 064 065 /** 066 * Registers a Hibernate SessionFactory with this collector. 067 * 068 * @param sessionFactory The Hibernate SessionFactory to collect metrics for 069 * @param name A unique name for this SessionFactory 070 * @return Returns the collector 071 */ 072 public HibernateStatisticsCollector add(SessionFactory sessionFactory, String name) { 073 sessionFactories.put(name, sessionFactory); 074 return this; 075 } 076 077 @Override 078 public List<MetricFamilySamples> collect() { 079 List<MetricFamilySamples> metrics = new ArrayList<MetricFamilySamples>(); 080 metrics.addAll(getSessionMetrics()); 081 metrics.addAll(getConnectionMetrics()); 082 metrics.addAll(getCacheMetrics()); 083 metrics.addAll(getEntityMetrics()); 084 metrics.addAll(getQueryExecutionMetrics()); 085 return metrics; 086 } 087 088 @Override 089 public <T extends Collector> T register(CollectorRegistry registry) { 090 if (sessionFactories.isEmpty()) { 091 throw new IllegalStateException("You must register at least one SessionFactory."); 092 } 093 return super.register(registry); 094 } 095 096 private List<MetricFamilySamples> getSessionMetrics() { 097 return Arrays.<MetricFamilySamples>asList( 098 createCounter( 099 "hibernate_session_opened_total", 100 "Global number of sessions opened (getSessionOpenCount)", 101 new ValueProvider() { 102 @Override 103 public double getValue(Statistics statistics) { 104 return statistics.getSessionOpenCount(); 105 } 106 } 107 ), 108 createCounter( 109 "hibernate_session_closed_total", 110 "Global number of sessions closed (getSessionCloseCount)", 111 new ValueProvider() { 112 @Override 113 public double getValue(Statistics statistics) { 114 return statistics.getSessionCloseCount(); 115 } 116 } 117 ), 118 createCounter( 119 "hibernate_flushed_total", 120 "The global number of flushes executed by sessions (getFlushCount)", 121 new ValueProvider() { 122 @Override 123 public double getValue(Statistics statistics) { 124 return statistics.getFlushCount(); 125 } 126 } 127 ), 128 createCounter( 129 "hibernate_connect_total", 130 "The global number of connections requested by the sessions (getConnectCount)", 131 new ValueProvider() { 132 @Override 133 public double getValue(Statistics statistics) { 134 return statistics.getConnectCount(); 135 } 136 } 137 ), 138 createCounter( 139 "hibernate_optimistic_failure_total", 140 "The number of StaleObjectStateExceptions that occurred (getOptimisticFailureCount)", 141 new ValueProvider() { 142 @Override 143 public double getValue(Statistics statistics) { 144 return statistics.getOptimisticFailureCount(); 145 } 146 } 147 ) 148 ); 149 } 150 151 private List<MetricFamilySamples> getConnectionMetrics() { 152 return Arrays.<MetricFamilySamples>asList( 153 createCounter( 154 "hibernate_statement_prepared_total", 155 "The number of prepared statements that were acquired (getPrepareStatementCount)", 156 new ValueProvider() { 157 @Override 158 public double getValue(Statistics statistics) { 159 return statistics.getPrepareStatementCount(); 160 } 161 } 162 ), 163 createCounter( 164 "hibernate_statement_closed_total", 165 "The number of prepared statements that were released (getCloseStatementCount)", 166 new ValueProvider() { 167 @Override 168 public double getValue(Statistics statistics) { 169 return statistics.getCloseStatementCount(); 170 } 171 } 172 ), 173 createCounter( 174 "hibernate_transaction_total", 175 "The number of transactions we know to have completed (getTransactionCount)", 176 new ValueProvider() { 177 @Override 178 public double getValue(Statistics statistics) { 179 return statistics.getTransactionCount(); 180 } 181 } 182 ), 183 createCounter( 184 "hibernate_transaction_success_total", 185 "The number of transactions we know to have been successful (getSuccessfulTransactionCount)", 186 new ValueProvider() { 187 @Override 188 public double getValue(Statistics statistics) { 189 return statistics.getSuccessfulTransactionCount(); 190 } 191 } 192 ) 193 ); 194 } 195 196 private List<MetricFamilySamples> getCacheMetrics() { 197 return Arrays.<MetricFamilySamples>asList( 198 createCounter( 199 "hibernate_second_level_cache_hit_total", 200 "Global number of cacheable entities/collections successfully retrieved from the cache (getSecondLevelCacheHitCount)", 201 new ValueProvider() { 202 @Override 203 public double getValue(Statistics statistics) { 204 return statistics.getSecondLevelCacheHitCount(); 205 } 206 } 207 ), 208 createCounter( 209 "hibernate_second_level_cache_miss_total", 210 "Global number of cacheable entities/collections not found in the cache and loaded from the database (getSecondLevelCacheMissCount)", 211 new ValueProvider() { 212 @Override 213 public double getValue(Statistics statistics) { 214 return statistics.getSecondLevelCacheMissCount(); 215 } 216 } 217 ), 218 createCounter( 219 "hibernate_second_level_cache_put_total", 220 "Global number of cacheable entities/collections put in the cache (getSecondLevelCachePutCount)", 221 new ValueProvider() { 222 @Override 223 public double getValue(Statistics statistics) { 224 return statistics.getSecondLevelCachePutCount(); 225 } 226 } 227 ), 228 createCounter( 229 "hibernate_query_cache_hit_total", 230 "The global number of cached queries successfully retrieved from cache (getQueryCacheHitCount)", 231 new ValueProvider() { 232 @Override 233 public double getValue(Statistics statistics) { 234 return statistics.getQueryCacheHitCount(); 235 } 236 } 237 ), 238 createCounter( 239 "hibernate_query_cache_miss_total", 240 "The global number of cached queries not found in cache (getQueryCacheMissCount)", 241 new ValueProvider() { 242 @Override 243 public double getValue(Statistics statistics) { 244 return statistics.getQueryCacheMissCount(); 245 } 246 } 247 ), 248 createCounter( 249 "hibernate_query_cache_put_total", 250 "The global number of cacheable queries put in cache (getQueryCachePutCount)", 251 new ValueProvider() { 252 @Override 253 public double getValue(Statistics statistics) { 254 return statistics.getQueryCachePutCount(); 255 } 256 } 257 ), 258 createCounter( 259 "hibernate_natural_id_cache_hit_total", 260 "The global number of cached naturalId lookups successfully retrieved from cache (getNaturalIdCacheHitCount)", 261 new ValueProvider() { 262 @Override 263 public double getValue(Statistics statistics) { 264 return statistics.getNaturalIdCacheHitCount(); 265 } 266 } 267 ), 268 createCounter( 269 "hibernate_natural_id_cache_miss_total", 270 "The global number of cached naturalId lookups not found in cache (getNaturalIdCacheMissCount)", 271 new ValueProvider() { 272 @Override 273 public double getValue(Statistics statistics) { 274 return statistics.getNaturalIdCacheMissCount(); 275 } 276 } 277 ), 278 createCounter( 279 "hibernate_natural_id_cache_put_total", 280 "The global number of cacheable naturalId lookups put in cache (getNaturalIdCachePutCount)", 281 new ValueProvider() { 282 @Override 283 public double getValue(Statistics statistics) { 284 return statistics.getNaturalIdCachePutCount(); 285 } 286 } 287 ), 288 createCounter( 289 "hibernate_update_timestamps_cache_hit_total", 290 "The global number of timestamps successfully retrieved from cache (getUpdateTimestampsCacheHitCount)", 291 new ValueProvider() { 292 @Override 293 public double getValue(Statistics statistics) { 294 return statistics.getUpdateTimestampsCacheHitCount(); 295 } 296 } 297 ), 298 createCounter( 299 "hibernate_update_timestamps_cache_miss_total", 300 "The global number of tables for which no update timestamps was not found in cache (getUpdateTimestampsCacheMissCount)", 301 new ValueProvider() { 302 @Override 303 public double getValue(Statistics statistics) { 304 return statistics.getUpdateTimestampsCacheMissCount(); 305 } 306 } 307 ), 308 createCounter( 309 "hibernate_update_timestamps_cache_put_total", 310 "The global number of timestamps put in cache (getUpdateTimestampsCachePutCount)", 311 new ValueProvider() { 312 @Override 313 public double getValue(Statistics statistics) { 314 return statistics.getUpdateTimestampsCachePutCount(); 315 } 316 } 317 ) 318 ); 319 } 320 321 private List<MetricFamilySamples> getEntityMetrics() { 322 return Arrays.<MetricFamilySamples>asList( 323 createCounter( 324 "hibernate_entity_delete_total", 325 "Global number of entity deletes (getEntityDeleteCount)", 326 new ValueProvider() { 327 @Override 328 public double getValue(Statistics statistics) { 329 return statistics.getEntityDeleteCount(); 330 } 331 } 332 ), 333 createCounter( 334 "hibernate_entity_insert_total", 335 "Global number of entity inserts (getEntityInsertCount)", 336 new ValueProvider() { 337 @Override 338 public double getValue(Statistics statistics) { 339 return statistics.getEntityInsertCount(); 340 } 341 } 342 ), 343 createCounter( 344 "hibernate_entity_load_total", 345 "Global number of entity loads (getEntityLoadCount)", 346 new ValueProvider() { 347 @Override 348 public double getValue(Statistics statistics) { 349 return statistics.getEntityLoadCount(); 350 } 351 } 352 ), 353 createCounter( 354 "hibernate_entity_fetch_total", 355 "Global number of entity fetches (getEntityFetchCount)", 356 new ValueProvider() { 357 @Override 358 public double getValue(Statistics statistics) { 359 return statistics.getEntityFetchCount(); 360 } 361 } 362 ), 363 createCounter( 364 "hibernate_entity_update_total", 365 "Global number of entity updates (getEntityUpdateCount)", 366 new ValueProvider() { 367 @Override 368 public double getValue(Statistics statistics) { 369 return statistics.getEntityUpdateCount(); 370 } 371 } 372 ), 373 createCounter( 374 "hibernate_collection_load_total", 375 "Global number of collections loaded (getCollectionLoadCount)", 376 new ValueProvider() { 377 @Override 378 public double getValue(Statistics statistics) { 379 return statistics.getCollectionLoadCount(); 380 } 381 } 382 ), 383 createCounter( 384 "hibernate_collection_fetch_total", 385 "Global number of collections fetched (getCollectionFetchCount)", 386 new ValueProvider() { 387 @Override 388 public double getValue(Statistics statistics) { 389 return statistics.getCollectionFetchCount(); 390 } 391 } 392 ), 393 createCounter( 394 "hibernate_collection_update_total", 395 "Global number of collections updated (getCollectionUpdateCount)", 396 new ValueProvider() { 397 @Override 398 public double getValue(Statistics statistics) { 399 return statistics.getCollectionUpdateCount(); 400 } 401 } 402 ), 403 createCounter( 404 "hibernate_collection_remove_total", 405 "Global number of collections removed (getCollectionRemoveCount)", 406 new ValueProvider() { 407 @Override 408 public double getValue(Statistics statistics) { 409 return statistics.getCollectionRemoveCount(); 410 } 411 } 412 ), 413 createCounter( 414 "hibernate_collection_recreate_total", 415 "Global number of collections recreated (getCollectionRecreateCount)", 416 new ValueProvider() { 417 @Override 418 public double getValue(Statistics statistics) { 419 return statistics.getCollectionRecreateCount(); 420 } 421 } 422 ) 423 ); 424 } 425 426 private List<MetricFamilySamples> getQueryExecutionMetrics() { 427 return Arrays.<MetricFamilySamples>asList( 428 createCounter( 429 "hibernate_query_execution_total", 430 "Global number of executed queries (getQueryExecutionCount)", 431 new ValueProvider() { 432 @Override 433 public double getValue(Statistics statistics) { 434 return statistics.getQueryExecutionCount(); 435 } 436 } 437 ), 438 createCounter( 439 "hibernate_natural_id_query_execution_total", 440 "The global number of naturalId queries executed against the database (getNaturalIdQueryExecutionCount)", 441 new ValueProvider() { 442 @Override 443 public double getValue(Statistics statistics) { 444 return statistics.getNaturalIdQueryExecutionCount(); 445 } 446 } 447 ) 448 ); 449 } 450 451 private CounterMetricFamily createCounter(String metric, String help, ValueProvider provider) { 452 453 CounterMetricFamily metricFamily = new CounterMetricFamily(metric, help, LABEL_NAMES); 454 455 for (Entry<String, SessionFactory> entry : sessionFactories.entrySet()) { 456 metricFamily.addMetric( 457 Collections.singletonList(entry.getKey()), 458 provider.getValue(entry.getValue().getStatistics()) 459 ); 460 } 461 462 return metricFamily; 463 464 } 465 466 private interface ValueProvider { 467 468 double getValue(Statistics statistics); 469 470 } 471 472}