npm package: @objlabs/trace
Description: Secure trace/logging layer for ObjLabs projects
Author: ObjLabs
Latest version: 0.1.0


# @objlabs/trace

`@objlabs/trace` is a secure trace/logging layer for ObjLabs projects (Next.js / Node / browser).
It replaces raw `console.*` usage with a policy-driven, security‑aware trace API that can be
shared across all apps in a monorepo.

The goal is:

- Remove `console.*` from application code (for secure coding compliance)
- Centralize logging/trace behavior in one place
- Enforce masking of sensitive data
- Apply different behaviors per environment (`dev`, `staging`, `prod`)

---

## Features

- **Simple API**

  ```ts
  import { trace } from "@objlabs/trace";

  trace.debug("something happened", { foo: "bar" });
  trace.info("user:login", { userId });
  trace.warn("api:slow", { ms: 1234 });
  trace.error("unexpected", { error });
  ```

- **Scopes & Context**

  ```ts
  const t = trace.scope("user:update").with({ ip: clientIp });

  t.info("start", { userId });
  t.error("failed", { error });
  ```

- **Environment‑aware**

  - `dev`  : `debug` and above, verbose traces allowed
  - `staging`: `info` and above
  - `prod`: `warn` and above by default

- **Sensitive data redaction**

  - Keys like `password`, `token`, `secret`, `email`, `phone`, `ssn`, `rrn` are automatically masked.
  - Circular references in data are safely handled.

- **Pluggable transports**

  - Node: JSON to `stdout` (or custom transports)
  - Browser: dev can log to console, non‑dev sends to `/api/trace` via `sendBeacon`/`fetch`

---

## Install

```bash
# from your private registry or local link
npm install @objlabs/trace
# or
pnpm add @objlabs/trace
```

---

## Basic Usage

### 1. Configure once at app bootstrap

In a Next.js app (App Router):

```ts
// apps/web/app/trace-setup.ts
import { configureTrace } from "@objlabs/trace";
import { defaultTransports } from "@objlabs/trace/transports";

configureTrace({
  // env and level default from NODE_ENV / TRACE_ENV
  transports: defaultTransports(),
  // scopesEnabled: ["user:*", "order:create"], // optional, dev-only scope filter
});
```

Then import this file once at the root layout:

```ts
// apps/web/app/layout.tsx
import "./trace-setup";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ko">
      <body>{children}</body>
    </html>
  );
}
```

### 2. Use `trace` in your code

```ts
// apps/web/app/api/users/route.ts
import { trace } from "@objlabs/trace";

export async function POST(req: Request) {
  const body = await req.json();
  const t = trace.scope("user:create").with({
    ip: req.headers.get("x-forwarded-for"),
  });

  t.info("start", { email: body.email });

  try {
    // ... user creation logic
    const userId = "123";
    t.info("success", { userId });
    return new Response("OK", { status: 201 });
  } catch (err) {
    t.error("failed", { error: String(err) });
    return new Response("ERROR", { status: 500 });
  }
}
```

---

## API

### `trace` (singleton)

Default trace instance configured via `configureTrace`.

```ts
import { trace } from "@objlabs/trace";

trace.debug(message, data?);
trace.info(message, data?);
trace.warn(message, data?);
trace.error(message, data?);

trace.scope(name);              // returns a child Trace with a scope
trace.with(context);            // returns a child Trace with extra context
```

### `configureTrace(config: TraceConfig)`

Configure global trace behavior once at bootstrap.

```ts
import { configureTrace } from "@objlabs/trace";
import type { TraceConfig } from "@objlabs/trace";

const config: TraceConfig = {
  env: "dev" | "staging" | "prod",    // optional, defaults from env vars
  level: "debug" | "info" | "warn" | "error",
  transports: [/* TraceTransport[] */],
  redactor: (data) => data,           // optional, defaults to built‑in redactor
  scopesEnabled: ["user:*", "order:create"] | "*",
};

configureTrace(config);
```

### `createTrace(config?: TraceConfig)`

Create an isolated Trace instance (e.g. for tests or special pipelines).

```ts
import { createTrace } from "@objlabs/trace";

const testTrace = createTrace({
  env: "dev",
  level: "debug",
  transports: [
    (record) => {
      // push to an in‑memory array for assertions
      console.log("test record", record);
    },
  ],
});
```

---

## Environment & Levels

`env` is resolved as:

1. `TRACE_ENV` if set
2. else `NODE_ENV`
3. else `development`

Mapping:

- `"production"` → `"prod"`
- `"development"` → `"dev"`
- anything else → `"staging"`

Default level per env:

- `dev` → `"debug"`
- `staging` → `"info"`
- `prod` → `"warn"`

You can override via `TraceConfig.level`.

---

## Sensitive Data Redaction

By default, the built‑in redactor:

- Handles non‑object data transparently
- Recursively walks objects/arrays
- Masks values for keys containing:

  - `password`, `passwd`
  - `token`, `secret`
  - `email`
  - `phone`, `tel`
  - `ssn`, `rrn`

- Replaces circular references with `"[Circular]"`

You can supply your own `redactor` function in `TraceConfig` to extend or replace this behavior.

---

## Scopes

Scopes let you group traces by feature/domain:

```ts
trace.scope("user:login").info("start", { email });
trace.scope("user:login").error("failed", { reason });
trace.scope("order:create").debug("payload", payload);
```

In `dev`, you can restrict which scopes actually emit traces
with `scopesEnabled`:

```ts
configureTrace({
  scopesEnabled: ["user:*", "order:create"], // only these scopes are active
});
```

In `staging` / `prod`, scope is recorded as metadata but does not
filter emission (level decides).

---

## Secure Coding & `console` Policy

The recommended usage pattern is:

- **Application code must not use `console.*` directly.**
- Enforce via ESLint:

  ```js
  // .eslintrc.js
  module.exports = {
    rules: {
      "no-console": "error",
    },
  };
  ```

- All logging/trace behavior goes through `@objlabs/trace`.
- `@objlabs/trace` encapsulates:

  - Environment‑specific levels
  - Sensitive data redaction
  - Output destinations (stdout, HTTP, etc.)
  - Optional browser behavior

This makes it much easier to comply with secure‑coding guidelines
and pass static analysis that flags residual debugging code.

---

## Extending Transports

Transports are simple functions that receive a `TraceRecord`:

```ts
import type { TraceRecord, TraceTransport } from "@objlabs/trace";

const sentryTransport: TraceTransport = (rec: TraceRecord) => {
  if (rec.level === "error") {
    // Example: integrate with Sentry / APM
    // Sentry.captureMessage(rec.message, { extra: rec });
  }
};
```

Register via `configureTrace`:

```ts
import { configureTrace } from "@objlabs/trace";
import { defaultTransports } from "@objlabs/trace/transports";

configureTrace({
  transports: [...defaultTransports(), sentryTransport],
});
```

---

## License

MIT (or your preferred internal license)