npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ai-digital-factory/logging-react

v0.1.5

Published

React Provider and hook for same-origin browser records

Downloads

391

Readme

@ai-digital-factory/logging-react

React 18+ LogProvider and useLog() hook for sending explicit Browser records to your app’s same-origin Express ingest route (mounted by @ai-digital-factory/logging).

This package does not talk to your log provider from the browser. It batches JSON and POSTs to a path on the current page origin (default /__lms/browser-records). Your Express server forwards accepted records through the same LogManager as server logs (origin: "browser").

Audience: application developers and coding agents integrating logging into an existing React app.

Prerequisites

| Requirement | Notes | | --- | --- | | React 18+ | Peer dependency (react, react-dom). | | Node 22+ on the server | Install @ai-digital-factory/logging and register logs.expressMiddleware() before your routes. | | Same origin in the browser | The ingest POST must hit the same host/port the user sees (dev proxy or production reverse proxy). | | No browser credentials | LOG_INGESTION_URL and LOG_CREDENTIAL stay on the server only. |

Runnable reference: apps/example-react in this repository.

For the full step-by-step contract (including agent checklists), see the React integration guide.

Install

pnpm add @ai-digital-factory/logging-react @ai-digital-factory/logging

Use npm or yarn if that is what the app already uses. Install the Node SDK on the server package in a monorepo, and the React package on the client package.

Architecture (one glance)

Browser (React)                Your Express app              Log destination
─────────────────              ─────────────────             ───────────────
useLog() → batch
     │
     POST /__lms/browser-records  →  expressMiddleware()
     (same origin, no token)          → LogManager → LOG_INGESTION_URL

Integrate into an existing React app

1. Server (Express)

If the app already has Express (or you add a small API layer), register the SDK middleware once at process startup:

import express from "express";
import { createLogManager } from "@ai-digital-factory/logging";

const logs = createLogManager({
  service: "my-frontend-app",
  environment: process.env.NODE_ENV ?? "development",
  connection: {
    ingestionUrl: process.env.LOG_INGESTION_URL!,
    credential: process.env.LOG_CREDENTIAL!,
  },
  redact: ["eventMeta.accessToken"], // extend for your meta shape
});

const app = express();
app.use(logs.expressMiddleware()); // before your routes

// ... your API and static file routes ...

Load LOG_INGESTION_URL and LOG_CREDENTIAL from environment or secrets (never from the React bundle).

Details: @ai-digital-factory/logging README.

2. Client — wrap the tree once

Place one LogProvider as high as practical (typically next to your existing root providers).

Vite / CRA (main.tsx / index.tsx):

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { LogProvider } from "@ai-digital-factory/logging-react";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <LogProvider>
      <App />
    </LogProvider>
  </StrictMode>,
);

Next.js App Router: use a client component wrapper (e.g. app/providers.tsx with "use client") and render <LogProvider>{children}</LogProvider> inside it; import that wrapper from app/layout.tsx.

React Router / Redux / other providers: nest LogProvider outside route-specific trees so a single Session covers the mounted app (see Session).

You do not need to change every file at once. Add the Provider first, then call useLog() only in components where you want Browser records.

3. Client — log explicit events

import { useLog } from "@ai-digital-factory/logging-react";

export function CheckoutButton() {
  const log = useLog();

  return (
    <button
      type="button"
      onClick={() => log("click", "checkout", { step: "payment" })}
    >
      Pay
    </button>
  );
}

API: log(eventType, eventValue, eventMeta?)

  • eventType and eventValue are short strings you choose (e.g. "page view", "home").
  • eventMeta is optional structured data you supply. There is no fixed schema.
  • Hooks must run under LogProvider; otherwise useLog() throws.

4. Same origin in development (Vite)

If the UI dev server (e.g. port 5173) is separate from Express (e.g. 3001), proxy the ingest and API paths to Express. Example from this repo’s example app:

// vite.config.ts
import { defineConfig, loadEnv } from "vite";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");
  const backend = `http://127.0.0.1:${env.PORT ?? "3001"}`;

  return {
    server: {
      proxy: {
        "/__lms": backend,
        "/api": backend,
      },
    },
  };
});

Open the Vite URL in the browser during development. Browser records go to Vite → proxy → Express → provider.

Webpack dev server: configure an equivalent devServer.proxy for /__lms (and your API prefix).

5. Same origin in production

The browser must still POST to the same origin users visit. Typical patterns:

  • Express serves the built SPA and API on one host/port, or
  • A reverse proxy routes /__lms and /api to the Node process that runs expressMiddleware().

Do not point the React app at the raw LOG_INGESTION_URL from the client.

Custom ingest path

Default path: /__lms/browser-records (DEFAULT_BROWSER_INGEST_PATH).

If you set browserIngestPath on createLogManager, pass the same path to the Provider:

<LogProvider ingestPath="/telemetry/browser">

Update dev proxies and production routing to match.

Session and SSR

  • Each LogProvider mount creates a random in-memory Session ID sent with every record in that mount.
  • Logging starts after the Provider hydrates in the browser. SSR does not emit Browser records.
  • Session is not joined to Express req.log request IDs (by design).

Privacy and behavior

Do log (examples): control names, feature flags, coarse outcomes (success / fail), non-sensitive status codes.

Do not log: passwords, tokens, credentials, full URLs, query strings, headers, request/response bodies, or text typed by users. SDK redact paths apply to forwarded records but do not scan arbitrary strings—avoid putting secrets in eventMeta.

Not automatic: DOM events, router transitions, forms, fetch, or XHR. You must call useLog() explicitly.

Delivery: Batched, same-origin, best-effort (bounded buffer; failures are dropped silently on the client batcher). Not an audit trail.

Stored shape: Browser records are written at info with origin: "browser"; server logs use origin: "server".

Agent checklist

Use this when adding logging to an existing codebase:

  1. Confirm the app is React 18+ and has (or will have) Express with @ai-digital-factory/logging middleware registered before routes.
  2. Confirm no LOG_INGESTION_URL / LOG_CREDENTIAL in client env or bundled VITE_* / NEXT_PUBLIC_* variables.
  3. Add @ai-digital-factory/logging-react to the client package only.
  4. Wrap the existing root with <LogProvider> (one mount per browser tab experience).
  5. Add useLog() at intentional UX points; keep eventType / eventValue stable for querying.
  6. Configure dev proxy and production routing so /__lms/* (or custom ingestPath) reaches Express on the user-visible origin.
  7. Extend server redact for any sensitive eventMeta keys you allow.
  8. Verify in the log UI: new records on the stream from LOG_INGESTION_URL, origin: "browser", service from createLogManager.

Do not import @log-management/*, Pino, or provider SDKs in application code.

Troubleshooting

| Symptom | Likely cause | | --- | --- | | useLog must be used within a LogProvider | Component is outside LogProvider, or duplicate React roots without the Provider. | | Records appear on the wrong stream | Browser traffic hit a different Express app (common: dev proxy still pointing at port 3000 / example-server). Confirm startup log ingest URL on your server process. | | No records in the UI | Middleware not registered, proxy missing /__lms, or server env vars unset. Check network tab for POST to ingest path (should be same origin, 2xx from Express). | | example_react stream empty but example_server has browser-shaped messages | Vite proxied to the wrong backend; fix PORT / proxy target and restart dev. |

API reference

export const DEFAULT_BROWSER_INGEST_PATH = "/__lms/browser-records";

export type EventMeta = Readonly<Record<string, unknown>>;
export type LogBrowserEvent = (
  eventType: string,
  eventValue: string,
  eventMeta?: EventMeta,
) => void;

export function LogProvider(props: {
  children: React.ReactNode;
  ingestPath?: string;
}): JSX.Element;

export function useLog(): LogBrowserEvent;

Related docs