log-inject
v0.0.0
Published
Drop-in console interceptor — forwards all console.* calls to your backend while preserving native browser behaviour.
Downloads
28
Maintainers
Readme
log-inject
A production-grade TypeScript console interceptor that:
- Wraps every
console.*method (all 22 in the MDN spec) - Forwards batched log entries to your backend via
POST - Preserves native DevTools behaviour (passthrough)
- Persists a session-id via localStorage or a non-tracking cookie (your choice)
- Guards against missing methods on old browsers (backward compatibility polyfill)
- Survives page unloads via
sendBeacon+ synchronous XHR fallback - Ships as a single 7 kB minified browser bundle
Project structure
log-inject/
├── src/
│ ├── types.ts — All TypeScript interfaces & type aliases
│ ├── session.ts — Session-id persistence (localStorage / cookie / none)
│ ├── serializer.ts — Safe arg serialisation + stack-trace capture
│ ├── transport.ts — Batched fetch + XHR fallback + sendBeacon unload flush
│ ├── patch.ts — Core interception logic for all 22 console methods
│ └── index.ts — Public barrel + data-attribute auto-installer
├── dist/
│ ├── log-inject.js — Unminified bundle (with source map)
│ └── log-inject.min.js — Minified bundle (7 kB)
├── example.html — Interactive demo page
├── tsconfig.json
└── package.jsonQuick start
Method 1 — data-attribute auto-install (zero JS)
Drop one <script> tag at the top of your <head>, before any other scripts:
<script
src="/log-inject.min.js"
data-endpoint="/api/console-logs"
data-methods="log,info,warn,error,debug,trace"
data-storage="localStorage"
data-flush-interval="3000"
></script>That's it. Every console.* call from that point is captured and batched.
Method 2 — programmatic install
<script src="/log-inject.min.js"></script>
<script>
ConsolePatch.install({
endpoint: '/api/console-logs',
// Add auth / correlation headers
headers: { 'Authorization': 'Bearer ' + getToken() },
// Limit to only these methods in production
methods: ['log', 'info', 'warn', 'error', 'assert', 'trace'],
// Keep native DevTools output
passthrough: true,
// Use a non-tracking session cookie instead of localStorage
storageType: 'cookie',
cookieOptions: {
maxAgeDays: 30,
sameSite: 'Strict',
secure: true,
},
flushInterval: 5000, // ms between flushes
maxQueueSize: 200, // immediate flush threshold
maxArgLength: 4000, // truncate long strings
onFlush(entries) {
console.debug('[polyfill] flushed', entries.length, 'entries');
},
onFlushError(err, entries) {
console.error('[polyfill] flush failed:', err.message);
},
});
</script>Backend payload
Each POST to your endpoint carries:
{
"logs": [
{
"id": "1715510000000-1",
"method": "error",
"level": "error",
"timestamp": "2026-05-12T10:00:00.000Z",
"timestampMs": 1715510000000,
"url": "https://yourapp.com/dashboard",
"userAgent": "Mozilla/5.0 …",
"sessionId": "a1b2c3d4-…",
"args": ["Uncaught TypeError", "Cannot read properties of null"],
"stack": "TypeError: Cannot read …\n at foo (app.js:42:7)\n …",
"groupDepth": 0
}
]
}Configuration reference
| Option | Type | Default | Description |
|---|---|---|---|
| endpoint | string \| null | '/api/console-logs' | POST URL. null disables remote shipping. |
| headers | Record<string,string> | {} | Extra HTTP headers (e.g. auth). |
| methods | ConsoleMethod[] | all 22 | Methods to intercept. |
| passthrough | boolean | true | Also forward to native DevTools. |
| flushInterval | number | 2000 | Milliseconds between batch flushes. |
| maxQueueSize | number | 50 | Immediate flush when queue exceeds this. |
| sessionKey | string | '__cpoly_sid' | Storage key for session-id. |
| storageType | 'localStorage' \| 'cookie' \| 'none' | 'localStorage' | Where to persist session-id. |
| cookieOptions.maxAgeDays | number | 365 | Cookie lifetime. |
| cookieOptions.sameSite | 'Strict' \| 'Lax' \| 'None' | 'Strict' | Cookie SameSite attribute. |
| cookieOptions.secure | boolean | false | Add Secure flag to cookie. |
| maxArgLength | number | 2000 | Truncate serialised args at this length. |
| onFlush | (entries) => void | — | Called after successful backend flush. |
| onFlushError | (err, entries) => void | — | Called on flush failure. |
Build commands
npm run build # typecheck + bundle (dev + min)
npm run typecheck # tsc --noEmit only
npm run dev # watch mode (unminified)Backward compatibility
- All 22 console methods are checked for existence before wrapping.
- Methods absent in old browsers get a no-op shim so call sites don't throw.
- The bundle targets ES2015 and works in all evergreen browsers.
fetchunavailable → falls back toXMLHttpRequest.sendBeaconunavailable → falls back to synchronous XHR on page unload.
Session tracking (non-tracking design)
The session-id is a random UUID stored only in your own origin
(localStorage or a SameSite=Strict cookie). It carries no PII and is
used solely to correlate log entries from the same browser tab session.
It is not shared with third parties.
