@lmstech/monitor
v0.2.0
Published
Lightweight monitoring package for Next.js apps. Captures server logs, client errors, uncaught exceptions, and unhandled rejections — sends everything to the Observatory dashboard.
Readme
@lmstech/monitor
Lightweight monitoring package for Next.js apps. Captures server logs, client errors, uncaught exceptions, and unhandled rejections — sends everything to the Observatory dashboard.
If MONITOR_URL and MONITOR_KEY aren't set, the entire package is inert. No patching, no listeners, no network calls.
Setup in a consuming app
Four files, nine lines.
1. instrumentation.ts (project root)
export async function register() {
const { init } = await import("@lmstech/monitor/server");
init({ app: "my-app", env: "production", version: process.env.COMMIT_SHA });
}Filtering noisy events
Pass an ignore predicate to drop events before they're reported — useful for noisy-but-harmless errors like HTTP socket aborts from client disconnections:
init({
app: "my-app",
env: "production",
ignore: (event) =>
event.message === "aborted" && event.stack?.includes("abortIncoming") === true,
});The predicate runs for every event (console.error, uncaughtException, unhandledRejection, log). Return true to drop. A throwing predicate is treated as "do not ignore" — a broken filter won't hide errors.
2. app/api/monitor/route.ts
export { POST } from "@lmstech/monitor/server";3. app/layout.tsx
import { Monitor } from "@lmstech/monitor/client";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Monitor app="my-app" env="production">
{children}
</Monitor>
</body>
</html>
);
}4. Environment variables
MONITOR_URL=https://your-dashboard.vercel.app
MONITOR_KEY=your-api-key
COMMIT_SHA=abc123 # typically set by CI (GITHUB_SHA in GitHub Actions)Don't set these in local dev. The package stays invisible when they're absent.
Custom logging
Server-side
import { log } from "@lmstech/monitor/server";
log({ level: "info", message: "Payment processed", meta: { userId: "123" } });Client-side
import { useMonitor } from "@lmstech/monitor/client";
const { log } = useMonitor();
log({ level: "error", message: "Checkout failed", meta: { step: "payment" } });useMonitor() must be called inside the <Monitor> provider tree.
Developing the package
Everything runs from the repo root.
pnpm pkg:build # clean build (tsc)
pnpm pkg:publish # build + publish to npmThe build compiles src/ to dist/ using tsconfig.build.json. No bundler — just tsc.
Testing
cd packages/monitor
pnpm test # run tests once
pnpm test:watch # watch mode
pnpm typecheck # type-check without emittingPackage structure
src/
types.ts # all public types (EventLevel, LogPayload, InitOptions, etc.)
server/
index.ts # exports: init, log, POST
init.ts # console.error patching, process handlers, startup event
log.ts # custom server-side log()
route.ts # POST handler for client-side proxy
batch.ts # in-memory queue, 5s / 50-event flush
send.ts # fire-and-forget fetch to /api/ingest
client/
index.ts # exports: Monitor, useMonitor
context.tsx # React context for app/env
monitor.tsx # error + unhandledrejection listeners, sendBeacon
use-monitor.ts # hook for custom client-side logging
shared/
serialize.ts # safe meta serialization (circular refs, depth/size limits)Two entry points: @lmstech/monitor/server and @lmstech/monitor/client.
Publishing
- Bump the version in
packages/monitor/package.json. - From the repo root:
pnpm pkg:publish.
That's it. The script builds first, then publishes dist/ to npm.
First time only: make sure you're logged in (npm login) and the @lmstech npm org exists with public access. The publish script uses --access public for the scoped package.
