KmemoMetrics
Publishes a SemanticCache's behaviour to a Micrometer registry — so the cache shows up in the same Prometheus / Datadog / CloudWatch dashboards as everything else your service runs.
It is both a MeterBinder (register it with your registry) and a CacheListener (give it to the cache), and the two halves are wired together by you:
val metrics = KmemoMetrics(tags = Tags.of("cache", "faq"))
val cache = SemanticCache(embedder, listeners = listOf(metrics))
metrics.bindTo(registry) // Spring Boot Actuator calls this for youEvery meter is driven by the CacheEvent stream, which is exact: the cache emits one event per counted lookup, so the counters here equal dev.kmemo.CacheStats to the event. Events that arrive before bindTo are ignored — metrics begin the moment you bind, which for a service is startup, before traffic. Meters registered:
| Meter | Type | Tags | Meaning |
|---|---|---|---|
kmemo.cache.lookups | counter | — | total lookups (hits + misses) |
kmemo.cache.hits | counter | — | lookups served from cache |
kmemo.cache.misses | counter | reason | misses, split by MissReason |
kmemo.cache.guard.rejections | counter | guard | guard rejections, split by guard name |
kmemo.cache.writes | counter | — | entries written |
kmemo.cache.evictions | counter | cause | evictions, split by EvictionCause (needs a store listener) |
kmemo.cache.hit.ratio | gauge | — | hits / lookups |
kmemo.cache.embed | timer | — | dev.kmemo.Embedder latency per lookup |
kmemo.cache.search | timer | — | dev.kmemo.CacheStore search latency per lookup |
kmemo.cache.verify | timer | — | dev.kmemo.Verifier latency, when one runs |
Eviction counters only move if the store is also given this instance as its listener (evictions are the store's business); with InMemoryStore(listener = metrics) they light up too.
Cardinality
Meters here are not tagged by cache scope. Scope is caller-defined and often unbounded (one per tenant, per user, per model-version), and an unbounded tag is how a metrics bill or a Prometheus head-block blows up. The scope is on every CacheEvent for anyone who wants to build a deliberately bounded per-scope meter with their own CacheListener; this adapter stays safe by default.
Thread-safe: Micrometer meters are, and the listener does nothing else.
Parameters
base tags added to every meter, e.g. the cache's name when a service runs several.