Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@harnessa-fe/log

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@harnessa-fe/log

Isomorphic structured logger for Harnessa-FE. Works in Server Components, Route Handlers, Server Actions, and Client Components — all events land in the same session timeline.

latest
Source
npmnpm
Version
1.0.2
Version published
Maintainers
1
Created
Source

@harnessa-fe/log

Isomorphic structured logger for Harnessa-FE. The same import { log } from '@harnessa-fe/log' works in Server Components, Route Handlers, Server Actions, and Client Components — every event lands in the same sessions/{sid}/timeline.jsonl on the daemon.

pnpm add @harnessa-fe/log

You also need at least one runtime SDK installed:

  • Browser only → @harnessa-fe/runtime
  • Server only → @harnessa-fe/node-runtime
  • Both (typical Next.js) → both

If neither is installed log is a no-op — calls never throw.

Usage

import { log } from '@harnessa-fe/log';

log.info('Page loaded');
log.warn('Cart total exceeds threshold', { total, limit });
log.error('Stripe webhook failed', err);
log.debug('Cache hit', { key, ttl });

// Scope chain — prefixes every event with `scope=cart.checkout`
const cartLog = log.scope('cart').scope('checkout');
cartLog.info('Submitting order', { items: items.length });

The last argument can be a plain object — it's treated as structured metadata; everything before it joins as the message.

Variadic + meta

log.info('user', userId, 'clicked', { button: 'buy' });
// → message: "user u_123 clicked", meta: { button: 'buy' }

Where do events land?

Every log.* call emits a t: 'app-log' row to the daemon's session timeline (~/.harnessa/data/sessions/{sessionId}/timeline.jsonl). The row carries:

FieldSource
sessionIdBrowser: from the runtime client. Server: from node-runtime.getRequestSessionId() → ALS → adapter provider → undefined
projectId / buildIdStamped by the daemon from peer registration
level'debug' | 'info' | 'warn' | 'error'
argsVariadic args passed to the log call
scopeDot-joined scope chain, if any
tsUnix ms, captured at call site

Agents tell app-log apart from auto-captured server-log / browser console events, so you can ask things like "show me all log.warn(...) from the cart scope in this session".

sessionId continuity

The defining property of this logger: two log.info() calls under the same page-load — one from a Server Component, one from a Client Component — emit with the same sessionId.

This is what makes timelines coherent. The mechanism:

  • Browser: reads window.__harnessa_fe_client__.sessionId, set by @harnessa-fe/runtime after it adopts the SSR seed
  • Server: delegates to @harnessa-fe/node-runtime.getRequestSessionId(), which walks:
    • AsyncLocalStorage (populated by withHarnessaTracing(handler))
    • Adapter-supplied provider (Next pushes a React cache()-backed getter via setSessionIdProvider)
    • undefined → orphan event filed under sessions/server-orphans/

Orphans are correct, not a bug — a log.info() from a background timer or cold-start init has no request to belong to. Better orphaned than misattributed to whatever request happened to be in flight.

Concurrency safety

log is safe under concurrent requests. The server sessionId is read fresh at emit time, not closed over at the import site. Two tabs hitting the same Next process at the same time get their own React cache() scope; their log.* rows go to separate session timelines with zero cross-contamination.

This is verified by @harnessa-fe/node-runtime's test suite — 28 cases including a Promise.all([renderA, renderB]) with interleaved console.log and explicit assertions.

What's NOT in the payload

By design, log does not include userId in the event payload. The link from session to user is held by the daemon's visitor index (sessionId → SessionMeta.participants → visitor.userId) — agents do one lookup if they need it. Trade-off: one extra index hit instead of any chance of cross-request user leakage.

API

interface Logger {
    debug(...args: unknown[]): void;
    info(...args: unknown[]): void;
    log(...args: unknown[]): void;     // alias of info
    warn(...args: unknown[]): void;
    error(...args: unknown[]): void;
    scope(name: string): Logger;        // returns a new prefixed logger
}

export const log: Logger;

Production behavior

log is gated by the runtime SDK it dispatches to — @harnessa-fe/runtime and @harnessa-fe/node-runtime are both NODE_ENV === 'development' only. In production builds log.* becomes a cheap no-op (no network, no writes).

License

MIT

Keywords

harnessa-fe

FAQs

Package last updated on 21 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts