Pyranid Logo

Core Concepts

Metrics Collection

Pyranid exposes a MetricsCollector hook for low-cardinality database metrics. It is separate from StatementLogger: use the logger for diagnostics and SQL detail, and use metrics collectors for counters, gauges, and histograms.

Metrics collection is disabled by default. Collector failures are swallowed by Pyranid so metrics cannot change database behavior.

Core Configuration

Use MetricsCollector::inMemoryInstance() for in-process counters in tests or ad-hoc inspection:

MetricsCollector metricsCollector = MetricsCollector.inMemoryInstance();

Database database = Database.withDataSource(dataSource)
  .metricsCollector(metricsCollector)
  .build();

MetricsCollector.Snapshot snapshot = metricsCollector.snapshot().orElseThrow();

Passing null or omitting Database.Builder::metricsCollector(...) disables metrics collection:

Database database = Database.withDataSource(dataSource)
  .metricsCollector(null)
  .build();

The collector is fixed at Database.Builder::build() time. To reconfigure metrics, build another Database.

OpenTelemetry

Use the optional pyranid-otel artifact to export through OpenTelemetry:

<dependency>
  <groupId>com.pyranid</groupId>
  <artifactId>pyranid-otel</artifactId>
  <version>1.0.0</version>
</dependency>
OpenTelemetryMetricsCollector metricsCollector = OpenTelemetryMetricsCollector
  .withOpenTelemetry(openTelemetry)
  .poolName("primary")
  .namespace("orders")
  .recordCollectionName(false)
  .build();

Database database = Database.withDataSource(dataSource)
  .databaseType(DatabaseType.POSTGRESQL)
  .metricsCollector(metricsCollector)
  .build();

poolName(null) or leaving it unset skips pool-name-gated connection metrics. namespace(null) omits db.namespace; Pyranid does not look up a namespace from JDBC metadata. recordCollectionName(false) is the default because collection/table names can be too high-cardinality for some deployments.

If first-use transaction metrics need a precise db.system.name, configure Database.Builder::databaseType(...). Otherwise lazy detection may report other_sql for transaction-only events before a statement has detected the database type.

OpenTelemetry Metrics

MetricStabilityTypeUnitNotes
db.client.operation.durationStableHistogramsStatement operation duration
db.client.response.returned_rowsDevelopmentHistogram{row}Emitted when Pyranid observes returned rows
db.client.connection.wait_timeDevelopmentHistogramsEmitted only when poolName is configured
db.client.connection.use_timeDevelopmentHistogramsConnection hold time; not exact physical transaction lifetime

Connection pool internals such as idle count, max count, pending requests, timeouts, and create time are intentionally not emitted by Pyranid. Use your pool or proxy exporter for those.

Pyranid Metrics

MetricTypeUnit
pyranid.statement.preparation.durationHistograms
pyranid.statement.execution.durationHistograms
pyranid.statement.mapping.durationHistograms
pyranid.statement.errorsCounter{statement}
pyranid.statement.batch.sizeHistogram{statement}
pyranid.statement.rows_affectedHistogram{row}
pyranid.transaction.closure.durationHistograms
pyranid.transaction.commit.durationHistograms
pyranid.transaction.rollback.durationHistograms
pyranid.transaction.physical.begin_failuresCounter{transaction}
pyranid.transaction.countCounter{transaction}
pyranid.transaction.activeUp-down counter{transaction}
pyranid.savepoint.operationsCounter{operation}
pyranid.fetchstream.durationHistograms
pyranid.fetchstream.rows_consumedHistogram{row}
pyranid.post_transaction.operationsCounter{operation}

Attribute Policy

OpenTelemetryMetricsCollector keeps default attributes bounded. It emits db.system.name, db.operation.name, configured db.namespace, SQLSTATE class as db.response.status_code on failures, and error.type using the immediate throwable class.

Statement::getId() from Query::id(...) is not emitted by the OTel collector. It remains available to custom collectors and StatementLogger.

Snapshot Semantics

MetricsCollector.Snapshot is counter-only and intended for tests or local inspection. MetricsCollector::reset() is best-effort; concurrent updates may race with reset operations. OpenTelemetryMetricsCollector::snapshot() returns Optional::empty() because OTel consumers read metrics through the SDK/exporter pipeline.

Previous
Logging and Diagnostics