# Observability Design ยง37. What is measured, and what must never appear in a measurement. ## Bounded tags, always Every JPA metric carries exactly five tags: persistence unit, operation, query, outcome, failure category. All five are registered identifiers, validated by `LowCardinality` at construction rather than at the registry โ€” so an unbounded value fails where it was introduced instead of surviving until a dashboard stops loading. Never a tag: entity id, tenant id, SQL parameter, exception message, JDBC URL. Each is unbounded, so each creates a time series per row or per failure; several are also the data the platform keeps out of logs, which a metrics backend would store just as durably and export just as widely. ## Transaction metrics | Meter | Why it exists | |---|---| | `jpa.transaction.duration` | the baseline | | `jpa.transaction.rollback` | rollback rate by failure category | | `jpa.transaction.timeout` | timeouts, distinct from other rollbacks | | `jpa.transaction.completion.unknown` | its own counter, deliberately | Completion-unknown gets a separate counter rather than being folded into failures. It is the one outcome that means a human has to look: every other failure is a transaction that definitely did not happen, while this one is a transaction that may have. ## Query metrics `jpa.query.duration` and `jpa.query.rows`. Rows are measured as well as duration because a query that issues one statement and hydrates twenty thousand rows is fast per statement and catastrophic per request โ€” a duration metric alone reports it as merely slow. ## Retry metrics Attempts are metrics, not warnings. Optimistic conflicts and serialization failures are the expected cost of concurrency; logging each at WARN pages someone for a system working as designed, after which the retry log gets filtered out and takes the genuinely interesting entries with it. `jpa.retry.attempt`, `jpa.retry.attempts` (distribution per operation), `jpa.retry.exhausted`. ## Query names in SQL `NamedStatementInspector` prefixes each statement with its registered query name as a SQL comment, which travels into `pg_stat_activity`, `auto_explain`, and the slow-query log. Without it, "which endpoint issues this query" is answered by grepping the codebase for fragments of SQL. ## Diagnostics `SqlDiagnosticRedactor` removes string literals, numbers, and anything email-shaped before SQL reaches a log. Redaction is blunt on purpose: preserving "harmless" values would require knowing which columns hold personal data. ## The actuator endpoint `jpaplatform` reports database major version, provider version, schema version, OSIV state, runtime role verification, and capability levels. It reports no JDBC URL, no username, no SQL, and no entity catalog โ€” an actuator endpoint is reachable by anyone who reaches the management port, and each of those would be a free reconnaissance answer. It is read-only: an endpoint that could trigger a migration or a repair would be an admin capability exposed over HTTP.