Log entries are stored in Durable Objects alongside your data, so there is no external log service to wire up, and the admin panel can search them.

1import { addLogEntry, getUserLogEntries } from "apiker";
2
3await addLogEntry("checkout", { plan: "pro", amount: 99 });
4
5const recent = await getUserLogEntries("checkout", 50);

What an entry holds

The caller's details are filled in for you; anything you pass is merged on top.

{ propertyName: "checkout:…:1710000000000", time: 1710000000000, id: "…", // the signed IP, or an entity you name clientId: "…", // IP + User-Agent countryCode: "US", pathname: "/checkout", userId: "…", // when the caller is signed in plan: "pro", // your own fields amount: 99 }

Reading entries

  • getUserLogEntries(prefix, limit) — one caller's entries, newest first.
  • getAllLogEntries(objectName, limit) — everything in one object instance.
  • getIndexedLogEntries(limit) — across every caller, using the mirrored index.

Entries are written per caller, which keeps writes spread out, and mirrored into a small sharded index so a dashboard can list recent activity without reading every instance.

Retention

Logs are kept for 30 days and rate limit counters for 2. Pruning happens opportunistically on write — roughly one in twenty writes also clears expired entries — so storage stays bounded without a scheduled job.

import { pruneLogSeries } from "apiker"; const deleted = await pruneLogSeries("checkout");
Include Logs in objects. Entries are keyed by the caller's signed IP rather than a raw address, so logs do not become a store of personal data.