Bentocache
Trace Bentocache and @adonisjs/cache operations with @unhandled/instrumentation-bentocache, including keys, hits and tiers.
@unhandled/instrumentation-bentocache records one span per Bentocache operation. It also covers @adonisjs/cache, which runs a BentoCache underneath. Your app needs bentocache 1.6.0 or later (below 2.0).
Install
npm install @unhandled/instrumentation-bentocacheRegister with the AdonisJS agent
Add the integration to config/unhandled.ts. Installing the package alone does nothing.
import env from '#start/env'
import { defineConfig } from '@unhandled/agent-adonisjs'
import { bentocache } from '@unhandled/instrumentation-bentocache/integration'
export default defineConfig({
dsn: env.get('UNHANDLED_DSN'),
integrations: [bentocache({ requireParentSpan: true })],
})bentocache({ enabled: false }) turns it off without removing it. If bentocache cannot be resolved, the agent fails at startup with an IntegrationSetupError whose cause is a BentocacheNotInstalledError naming the missing package.
Register standalone
With any other OpenTelemetry setup, register the instrumentation before bentocache is imported:
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import { BentocacheInstrumentation } from '@unhandled/instrumentation-bentocache'
const instrumentation = new BentocacheInstrumentation({ includeKeys: true })
registerInstrumentations({ instrumentations: [instrumentation] })In ESM apps the module hook needs the import-in-the-middle loader. If your setup misses the import, patch the module yourself with instrumentation.patch(await import('bentocache')).
Options
| Option | Default | Effect |
|---|---|---|
enabled | true | Integration only. false skips it. |
requireParentSpan | false | Only create spans when a span is already active. |
includeKeys | true | false leaves cache.key and cache.keys off every span. |
keySanitizer | masks IDs | (key) => string | undefined. Replaces the default sanitizer. |
The default sanitizer splits the key on : and replaces every UUID, all-digit segment and hex segment of 8 or more characters with *. products:4821:price becomes products:*:price. Keys include the prefix you configured in Bentocache. The sanitizer is also exported as sanitizeCacheKey if you want to build on it.
Turn on requireParentSpan if cache calls made outside requests and jobs would otherwise show up as background spans.
What it records
One INTERNAL span per operation, named cache.<operation>: cache.get, cache.set, cache.getOrSet, cache.factory, cache.delete, cache.deleteMany, cache.clear and cache.expire. Operations that run during another one, such as the factory and the write inside a getOrSet, are its children. A failure records the exception and sets ERROR status.
| Attribute | When |
|---|---|
cache.operation | Always |
cache.store | Always |
cache.key | With includeKeys, when the operation has a key |
cache.keys | With includeKeys, when the operation has several keys (deleteMany) |
cache.hit | get and getOrSet |
cache.tier | get hits: l1 or l2 |
cache.graced | get hits: whether the value came from the grace period |
cache.operation puts the span in the cache category, and cache.hit drives the hit rate on the cache page.
Bentocache's own L2 calls (Redis, database) and bus messages run with tracing suppressed, so they do not add Redis or SQL spans under every cache call. This works by wrapping the BentoCache constructor. If the module's exports are read-only, the wrapper is skipped with a diagnostic warning and those internal spans appear.